Blynk Board Bridge Widget Demo

Pages
Contributors: .Brent.
Favorited Favorite 2

Firmware

The firmware for this demo is Arduino code. For detailed information on getting the Blynk libraries installed and your development environment configured, please visit the Blynk Board Arduino Development Guide.

Configuration

One major way that the firmware for this demo differs from "standard" Blynk firmware is that you need a second authentication code.

language:c
char auth[] = "7DF0F1DE119F48149EFF64BC7B2FBD11"; // Put your Auth Token here
char remoteAuth[] = "RemoteAuthToken"; // Auth token of bridged device

To get this second authentication code, you need a second project on your mobile device. For the chat to also work, you will want to start the second project on a second mobile device logged into the same account. Once you have the second project for a secondary Blynk Board, that token can be entered as the remoteAuth for the primary Blynk Board. These two lines are the only differences between Bridge_Example_1.ino and Bridge_Example_2.ino. Two were created as a convenient way keep the confusion low and to make tweaks to one but not both easier.

The next section to configure is the section of aliases used to tell the body of the code which virtual pin was chosen in the app. These don't need to be changed if you followed along in the previous section on the mobile app configuration. In both the code and in the mobile app, we have the local LED button mapped to virtual pin 1 (V1). We have configured the second button labeled REMT LED in the app to use virtual pin 2 (V2). Here in the code we defined an alias REMOTE_LED_BUTTON for V2. This section allows us to use the exact same code for many bridged devices.

language:c
// Virtual Pins:
#define TERMINAL                 0  // V0
#define LOCAL_LED_BUTTON         1  // V1
#define REMOTE_LED_BUTTON        2  // V2
#define TERMINAL_RECEIVE         3  // V3
#define BRIDGE                   4  // V4
#define LOCAL_LED_RECIEVE        5  // V5
#define REMOTE_LED_STATUS_UPDATE 6  // V6

The final configuration needed is to tell the Blynk Board how to connect to the Wi-Fi®. Replace SSID with the SSID (the name of your Wi-Fi® network) leaving the double quotes. Also replace PASSWORD with the actual password for your network, again leaving the double quotes.

language:c
Blynk.begin(auth, "SSID", "PASSWORD");  // Here your Arduino connects to the Blynk Cloud.

How It Works

Physical Button

Let's start with the physical button on the Blynk Board. This button will be used to toggle the LED on the other Blynk Board.

This button connects GPIO0 to ground when pressed. This is called active low; the signal goes low, when the button is pressed/active. An internal pull-up resistor is used to keep this signal high when inactive. An interrupt service routine is used to immediately capture the button press event, but the resulting action isn't taken until the main loop of the code gets around to doing it. This keeps more important things running fast. Here is the code snippet to configure what was just described.

language:c
// Setup the onboard button
pinMode(BUTTON_PIN, INPUT_PULLUP);
// Attach BUTTON_PIN interrupt to our handler
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), onButtonPress, FALLING);

The second parameter to the attachInterrupt function call is the name of the interrupt service handler, onButtonPress.

language:c
// Interrupt service routine to capture button press event
ICACHE_RAM_ATTR void onButtonPress()
{
  // Invert state, since button is "Active LOW"
  wasButtonPressed = !digitalRead(BUTTON_PIN);
}

This function is called when the physical button is pressed. Since the processor may be in the middle of doing something with more critical timing, this function does as little as possible, and the event is dealt with later. Here we have a variable wasButtonPressed that keeps track of whether or not the button was pressed. We store the opposite of the current state of the pin in this variable. The opposite is obtained with the ! operator. We read this pin a second time as a form of debouncing. A real button press should send the value of that pin low, and it should remain low at least the fraction of a second it takes to jump to this function and double check the state. This avoids changing state based on fast transient glitches.

Next, let's discuss the main loop:

language:c
void loop()
{
  Blynk.run(); // All the Blynk Magic happens here...

  if (wasButtonPressed) {
    // This can be seen in the Serial Monitor
    BLYNK_LOG("Physical button was pressed.");
    // Toggle state
    remoteLedState ^= HIGH;
    // Send new state to remote board LED
    bridge.virtualWrite(LOCAL_LED_RECIEVE, remoteLedState);
    // Update state of button on local mobile app
    Blynk.virtualWrite(REMOTE_LED_BUTTON, remoteLedState);
    // Clear state variable set in ISR
    wasButtonPressed = false;
  }
}

Two things happen here. First, we let the Blynk library do its thing. Next, we check to see if the physical button was pressed. If the button had been pressed since the last time this check was run, we have a few things to do.

First, if the Blynk Board is connected via a micro USB cable to a computer running a terminal emulator or the Arduino Serial Monitor, we write a debug message to that at 9600 baud. This is done with BLYNK_LOG("Physical button was pressed.");. This code can be compiled out if the first line of actual code in the sketch is removed or commented out (#define BLYNK_PRINT Serial).

A state variable keeping track of the state of the remote LED is toggled. This is done with remoteLedState ^= HIGH; but could also be done with something like remoteLedState = 1 - remoteLedState;, or remoteLedState = !remoteLedState;.

Next, we send the new state across the bridge doing a virtual write to the remote LOCAL_LED_RECIEVE virtual pin. On the other end is a function that handles the value sent.

After we have told the remote devices to update, we need to update the state on the mobile app attached to this Blynk Board. We do this with a virtual write to the REMOTE_LED_BUTTON with the new state value.

The final thing to do is clear the variable that indicates we have an unhandled button press, wasButtonPressed. This causes the code block we are in to be skipped until the button is pressed again.

To follow along to logical flow of a physical button press, let's cover the function that handles when another device signals that its button has been pressed:

language:c
// Remote device triggered an LED change. Update the LED and app status.
BLYNK_WRITE(LOCAL_LED_RECIEVE)
{
  // This can be seen in the Serial Monitor
  BLYNK_LOG("Remote device triggered LED change");

  ledState = param.asInt();
  // Turn on LED
  digitalWrite(LED_PIN, ledState);
  // Set state of virtual button to match remote LED
  Blynk.virtualWrite(LOCAL_LED_BUTTON, ledState);
}

The new or important items found in this function are the digitalWrite and the virtualWrite. After updating the state of the LED, this value is sent to the GPIO connected to the LED. We then update the UI on the mobile app associated with this board.

The previous code block handled updating the GUI associated with the board that had the button pressed. This block update the physical LED and the GUI associated with the board with the LED that changed. One virtual button press updated a physical pin and two GUI elements.

LED Button in the Mobile App GUI

When the button labeled LED is pressed, the following function is called. The first bit is nothing new. We update the state variable and log this info via a serial connection, if it exists.

language:c
// Virtual button on connected app was used to change remote LED.
// Tell remote devices about the change.
BLYNK_WRITE(REMOTE_LED_BUTTON)
{
  remoteLedState = param.asInt(); // Update state with info from remote
  BLYNK_LOG("New remote LED state: %s", param.asInt() ? "HIGH" : "LOW");
  // Send updated status to remote board.
  bridge.virtualWrite(LOCAL_LED_RECIEVE, remoteLedState);
}

The last statement in this function sends the remoteLedStat to the other Bkynk Board's LOCAL_LED_RECEIVE virtual pin over the bridge. The bridge was configured in this line:

language:c
WidgetBridge bridge(BRIDGE);

BRIDGE was defined earlier in the code to be virtual pin 4.

The other end of the bridge reacts to this signal with the following function. Nothing new to see here, we log via the serial port if it exists, update the state variable, update the physical pin, and finally update the state of the GUI.

language:c
// Remote device triggered an LED change. Update the LED and app status.
BLYNK_WRITE(LOCAL_LED_RECIEVE)
{
  // This can be seen in the Serial Monitor
  BLYNK_LOG("Remote device triggered LED change");

  ledState = param.asInt();
  // Turn on LED
  digitalWrite(LED_PIN, ledState);
  // Set state of virtual button to match remote LED
  Blynk.virtualWrite(LOCAL_LED_BUTTON, ledState);
}

Remote LED Button in the Mobile App GUI

The remote LED button in the mobile app GUI behaves roughly the same as the LED button. In this case the button is labeled REMT LED. It uses the following functions. This first is called when the button in the app is pressed.

language:c
// Virtual button on connected app was used to change remote LED.
// Tell remote devices about the change.
BLYNK_WRITE(REMOTE_LED_BUTTON)
{
  remoteLedState = param.asInt(); // Update state with info from remote
  BLYNK_LOG("New remote LED state: %s", param.asInt() ? "HIGH" : "LOW");
  // Send updated status to remote board.
  bridge.virtualWrite(LOCAL_LED_RECIEVE, remoteLedState);
}

That function sends a signal handled on the other end of the bridge handled with the following function.

language:c
// Remote device triggered an LED change. Update the LED and app status.
BLYNK_WRITE(LOCAL_LED_RECIEVE)
{
  // This can be seen in the Serial Monitor
  BLYNK_LOG("Remote device triggered LED change");

  ledState = param.asInt();
  // Turn on LED
  digitalWrite(LED_PIN, ledState);
  // Set state of virtual button to match remote LED
  Blynk.virtualWrite(LOCAL_LED_BUTTON, ledState);
}

Inter-Device Chat Window

Another part of this demo is sending text over the bridge. Alone this isn't the most amazing use of the technology, but in the case of an Internet outage a local Blynk Server and a Wi-Fi router could be setup for a phone to phone chat service.

The first requirement is to setup a Terminal widget to go with the one added in the mobile app.

language:c
// Attach virtual serial terminal to TERMINAL Virtual Pin
WidgetTerminal terminal(TERMINAL);

Next, a function takes the output of this widget and sends it to another Blynk Board.

language:c
// You can send commands from Terminal to your hardware. Just use
// the same Virtual Pin as your Terminal Widget
BLYNK_WRITE(TERMINAL)
{
  // Send from this terminal to remote terminal
  bridge.virtualWrite(TERMINAL_RECEIVE, param.asStr());
}

On the other side of the bridge is the following function that takes the received string and writes it to the local terminal.

language:c
// Receive a string on LOCAL_RECEIVE and write that value to the connected
// phone's terminal
BLYNK_WRITE(TERMINAL_RECEIVE)
{
  terminal.println(param.asStr());  // Write received string to local terminal
  terminal.flush();
}

Example of chat functionality

Example of chat functionality

What You Should See

That was quite a lot so let's go over what you will see after uploading code to the respective Blynk boards and running the projects. Try typing into terminal window for either project. This will send a message to the other Blynk board that is connected and vice versa. If you press a "LED" button on the Blynk app associated with the project, you will notice the GUI react to the button press and the blue LED turn on with the Blynk board. If you press the "REMT LED" button on the Blynk app associated with the project or the physical button on the other Blynk board, you will notice the LED turn on with the Blynk board!