ESP8266 Bidirectional Bridge

I am trying to create a simple circuit using two ESP8266.
I need a physical button connected to either one to activate an LED on both.
(See Diagram)

I am new to coding and was able to find some example projects to start from. I tried using the Blynk “Sync Physical Button” example code, and was able to successfully make a button toggle the LED on one of the two ESPs but it does not communicate with the second one even with both the same virtual pin.

I want to make use of the Blynk bridge to activate the LED on both ESPs from the button on either one. I am sure there is a fairly simple code to do this, but I am struggling to make that jump.

Here is the code I am currently using to toggle the LED on one ESP.

/* Comment this out to disable prints and save space */
#define BLYNK_PRINT Serial


#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "aaaaaaaaaaaaaaaaaaaa";


// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "1111111111";
char pass[] = "2222222222";


// Set your LED and physical button pins here
const int ledPin = 5;
const int btnPin = 12;

BlynkTimer timer;
void checkPhysicalButton();

int ledState = LOW;
int btnState = HIGH;

// Every time we connect to the cloud...
BLYNK_CONNECTED() {
  // Request the latest state from the server
  Blynk.syncVirtual(V2);

  // Alternatively, you could override server state using:
  //Blynk.virtualWrite(V2, ledState);
}

// When App button is pushed - switch the state
BLYNK_WRITE(V2) {
  ledState = param.asInt();
  digitalWrite(ledPin, ledState);
}

void checkPhysicalButton()
{
  if (digitalRead(btnPin) == LOW) {
    // btnState is used to avoid sequential toggles
    if (btnState != LOW) {

      // Toggle LED state
      ledState = !ledState;
      digitalWrite(ledPin, ledState);

      // Update Button Widget
      Blynk.virtualWrite(V2, ledState);
    }
    btnState = LOW;
  } else {
    btnState = HIGH;
  }
}

void setup()
{
  // Debug console
  Serial.begin(115200);
  Blynk.begin(auth, ssid, pass, "blynk-cloud.com", 8080);
  // You can also specify server:
  //Blynk.begin(auth, ssid, pass, "blynk-cloud.com", 80);
  //Blynk.begin(auth, ssid, pass, IPAddress(192,168,1,100), 8080);

  pinMode(ledPin, OUTPUT);
  pinMode(btnPin, INPUT_PULLUP);
  digitalWrite(ledPin, ledState);

  // Setup a function to be called every 100 ms
  timer.setInterval(100L, checkPhysicalButton);
}

void loop()
{
  Blynk.run();
  timer.run();
}

The code you have looks good.

The key is that you need to have different auth tokens for the two devices. Lets call these Token A and Token B.

You then use Bridge as a way to send data from device A, via the Blynk server to device B. This requires device A to initialise the bridge with the device B token.
The code running on device B will do the opposite.

See this example for details…

Pete.

This is the code I came up with by trying to combine the example button code with the example bridge code.

It is not working though.
Any advice is appreciated.

#define BLYNK_PRINT Serial
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

char auth[] = "aaa";  // Token for Device A
//char auth[] = "bbb";  // Token for Device B
char ssid[] = "ssid";
char pass[] = "pass";
char server[] = "blynk-cloud.com";
int port = 8080;

// Bridge widget on virtual pin 1
WidgetBridge bridge1(V100);

// Set your LED and physical button pins here
const int btnPin1 = 12;
const int ledPin1 = 5;

// Timer for blynking
BlynkTimer timer;

void checkPhysicalButton();

int ledState = LOW;
int btnpause = HIGH;

// Every time we connect to the cloud...
BLYNK_CONNECTED() {
  // Request the latest state from the server
  Blynk.syncVirtual(V1);
  //bridge1.setAuthToken("aaa"); // Token of the Device A
  bridge1.setAuthToken("bbb"); // Token of the Device B
}

void blynkAnotherDevice() // Here we will send HIGH or LOW once per second
{
  if (digitalRead(btnPin1) == LOW) {
    // btnpause is used to avoid sequential toggles
    if (btnpause != LOW) {

      // Toggle LED state
      ledState = !ledState;

      bridge1.virtualWrite(V1, ledState); // Sends ledstate value to other ESP.
      Blynk.virtualWrite(V1, ledState);  // Sends ledstate value to Blynk App 

    btnpause = LOW;
  } else {
    btnpause = HIGH;
  }
}


// When App button is pushed - switch the state
BLYNK_WRITE(V1) {
  ledState = param.asInt();
  digitalWrite(ledPin1, ledState);
}


void setup()
{
  // Debug console
  Serial.begin(115200);
  Blynk.begin(auth, ssid, pass, server, port);

  // Call blynkAnotherDevice every second
  timer.setInterval(1000L, blynkAnotherDevice);
}

void loop()
{
  Blynk.run();
  timer.run();
}

What exactly does that mean?

What code are you running on device B?

Pete.

Hello, I am also working on this same project with the OP.

Please excuse our ignorance.

Current Test Setup
We flashed the same code on two EESP8266 only swapping the auth for local and bridge.

We are pulling most of our code concept from the Blynk Examples Page

Bridge Virtual Pin
In the example code, they reference “V1” on the Widget Bridge

// Bridge widget on virtual pin 1
WidgetBridge bridge1(V1);

But they reference “V5” on the send values.

bridge1.virtualWrite(V5, 1);

This leads us to believe you need to dedicate a virtual pin for the bridge to run over that is separate from the virtual pins we are writing to.

DigitalWrite vs VirtualWrite with Bridge
In the example, there is a reference to “digitalWrite” pin9 and then “virtualWrite” V5

if (value) {
    bridge1.digitalWrite(9, HIGH); 
    bridge1.virtualWrite(V5, 1); 

Are these both required? I only included the virtualWrite, because I assumed that the incoming processor on the other board could write to that digial pin.

BLYNK_WRITE(V5) {
  ledState= param.asInt();
  digitalWrite(ledPin1, ledState);
}

Our Attempt at bridging our button
Because the button code on the first post worked locally over a virtual pin. We tried inserting a write to bridge in the IF statement in place of digitalWrite.

void checkPhysicalButton()
{
  if (digitalRead(btnPin) == LOW) {
    if (btnState != LOW) {
      ledState = !ledState;
      digitalWrite(ledPin, ledState);

      Blynk.virtualWrite(V2, ledState);
    }
    btnState = LOW;
  } else {
    btnState = HIGH;
  }
}

Was changed to this

void blynkAnotherDevice() // Here we will send HIGH or LOW once per second
{
  if (digitalRead(btnPin1) == LOW) {
    if (btnpause != LOW) {
      ledState = !ledState;
      bridge1.virtualWrite(V1, ledState);

      Blynk.virtualWrite(V1, ledState);
    btnpause = LOW;
  } else {
    btnpause = HIGH;
  }
}

and then we added an input receiver afterwards like this.

BLYNK_WRITE(V1) {
pinData = param.asInt();
digitalWrite(ledPin1, pinData);
}

The hope was that it would take the state of the button and push it out to the bridge as well as to the app and that both ends would see incoming data on V1 and then turn on the LED.

The result was that nighter LED lit nor did the APP receive the button change.

I think you should start with a simpler sketch, sending a value from device A to device B
When that is working, send a modified version of that data back from device B to device A.

Then you’ll understand how the bridge works, and will be able to incorporate that into your sketch.

When you’re posting your sketches and asking for assistance, you need to post BOTH sketches, and when you’re redacting your auth tokens you need to use something like “AAAA” and “BBBB” to represent the device A and B tokens.

Pete.

1 Like

I do have aaa and bbb in the code above. Guess I don’t understand what you mean by that.

After a late-night staring at it, I got a functioning prototype.
I changed the bridge write to a digialWrite and added a local write.
Now pressing the button on either one toggles the LED on both.

Device A

#define BLYNK_PRINT Serial
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

char auth[] = "aaa";  // Token for Device A
//char auth[] = "bbb";  // Token for Device N
char ssid[] = "ssid-2";
char pass[] = "pass";
char server[] = "blynk-cloud.com";
int port = 8080;

// Bridge widget on virtual pin 1
WidgetBridge bridge1(V100);

// Set your LED and physical button pins here
const int btnPin1 = 12; // D6(gpio12)
const int ledPin1 = 5; // D1(gpio5)

// Timer for blynking
BlynkTimer timer;
void checkPhysicalButton1();

int ledState1 = LOW;
int btnpause = HIGH;

// Every time we connect to the cloud...
BLYNK_CONNECTED() {
  //bridge1.setAuthToken("aaa"); // Token of the Device A
  bridge1.setAuthToken("bbb"); // Token of the Device B
}

///////////////////////////////////////////////////////

void checkPhysicalButton1()
{
  if (digitalRead(btnPin1) == LOW) {
    // btnpause is used to avoid sequential toggles
    if (btnpause != LOW) {

      // Toggle LED state
      ledState1 = !ledState1;
      digitalWrite(ledPin1, ledState1);
      bridge1.digitalWrite(5, ledState1); 
    }
    btnpause = LOW;
  } else {
    btnpause = HIGH;
  }
}

///////////////////////////////////////////////////////





void setup()
{
  // Debug console
  Serial.begin(115200);
  Blynk.begin(auth, ssid, pass, server, port);

  pinMode(ledPin1, OUTPUT);
  pinMode(btnPin1, INPUT_PULLUP);
  digitalWrite(ledPin1, ledState1);


  // Call blynkAnotherDevice every second
  timer.setInterval(100L, checkPhysicalButton1);
}

void loop()
{
  Blynk.run();
  timer.run();
}

Device B

#define BLYNK_PRINT Serial
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

//char auth[] = "aaa";  // Token for Device A
char auth[] = "bbb";  // Token for Device B
char ssid[] = "ssid-2";
char pass[] = "pass";
char server[] = "blynk-cloud.com";
int port = 8080;

// Bridge widget on virtual pin 1
WidgetBridge bridge1(V100);

// Set your LED and physical button pins here
const int btnPin1 = 12; // D6(gpio12)
const int ledPin1 = 5; // D1(gpio5)

// Timer for blynking
BlynkTimer timer;
void checkPhysicalButton1();

int ledState1 = LOW;
int btnpause = HIGH;

// Every time we connect to the cloud...
BLYNK_CONNECTED() {
  bridge1.setAuthToken("aaa"); // Token of the Device A
  //bridge1.setAuthToken("bbb"); // Token of the Device B
}

///////////////////////////////////////////////////////

void checkPhysicalButton1()
{
  if (digitalRead(btnPin1) == LOW) {
    // btnpause is used to avoid sequential toggles
    if (btnpause != LOW) {

      // Toggle LED state
      ledState1 = !ledState1;
      digitalWrite(ledPin1, ledState1);
      bridge1.digitalWrite(5, ledState1); 
    }
    btnpause = LOW;
  } else {
    btnpause = HIGH;
  }
}

///////////////////////////////////////////////////////





void setup()
{
  // Debug console
  Serial.begin(115200);
  Blynk.begin(auth, ssid, pass, server, port);

  pinMode(ledPin1, OUTPUT);
  pinMode(btnPin1, INPUT_PULLUP);
  digitalWrite(ledPin1, ledState1);


  // Call blynkAnotherDevice every second
  timer.setInterval(100L, checkPhysicalButton1);
}

void loop()
{
  Blynk.run();
  timer.run();
}

I am not 100% yet as I would like to move back to using a virtualpin so that there is a persistent state of the pin. Meaning if the ESP is powered off and back on again, it will resume the current state of the other one. I think this can be done by writing to a virtual pin and then calling the state of that pin.

  // Request the latest state from the server
  Blynk.syncVirtual(V2);

Any ideas for why it wasn’t working before with V1 would be appreciated.
I will keep tinkering away.

Impossible to say, as you haven’t posted two sets of code that demonstrated what it is that you were trying-out.

However, I don’t think you can successfully use the same V100 pin on both device A and B.

We need to see what bridve virtual writes you are doing from one device to the other, and what virtual write handlers you have in place on each side.

I still think that you’d be better going for a simpler code structure to enable you to get your head around how the bridge process works, starting with one way bridging, then moving on to two
way bridging.

Pete.

I got it working!

I got it to send to v10 over the bridge using
bridge1.virtualWrite(V10, ledState1);

I got it to update the app using
Blynk.virtualWrite(V10, ledState1);

and I got it to pull the current state on a power-on using
Blynk.syncVirtual(V10);

Now I can press the button on either one and the LED will match the on or off toggle state, and if one loses power it will resume that state on a power-on.

Next I need to figure out how to scale to multiple buttons and LEDs.

Here is the current code. (Same exact code on A and B except for the auth swapped)

#define BLYNK_PRINT Serial
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

// Token for Device A
char auth[] = "aaa";  
// Token for Device B
//char auth[] = "bbb";  

char ssid[] = "ssid-2";
char pass[] = "pass";
char server[] = "blynk-cloud.com";
int port = 8080;

// Bridge widget on virtual pin 1
WidgetBridge bridge1(V100);

// Set your LED and physical button pins here
const int btnPin1 = 12; // D6(gpio12)
const int ledPin1 = 5; // D1(gpio5)

// Timer for blynking
BlynkTimer timer;
void checkPhysicalButton1();

//Set default states
int ledState1 = LOW;
int btnpause = HIGH;

// Every time we connect to the cloud...
BLYNK_CONNECTED() {
  // Token of the Device A
  //bridge1.setAuthToken("aaa"); 
  // Token of the Device B
  bridge1.setAuthToken("bbb"); 
  //Sync Current LED State
  Blynk.syncVirtual(V10);
}

////// Check button 1 /////////////////////////////////////////////////

void checkPhysicalButton1()
{
  if (digitalRead(btnPin1) == LOW) {
    // btnpause is used to avoid sequential toggles
    if (btnpause != LOW) {

      // Toggle LED state
      ledState1 = !ledState1;
      // Update Local Pin var
      digitalWrite(ledPin1, ledState1);
      // Update App Button Widget
      Blynk.virtualWrite(V10, ledState1);
      // Sends ledstate1 value to BLYNK_WRITE(V10) handler on receiving side. 
      bridge1.virtualWrite(V10, ledState1); 
    }
    btnpause = LOW;
  } else {
    btnpause = HIGH;
  }
}

///////////////////////////////////////////////////////

// Bridge Receiver for V10
BLYNK_WRITE(V10) {
  ledState1 = param.asInt();
  digitalWrite(ledPin1, ledState1);
}

void setup()
{
  // Debug console
  Serial.begin(115200);
  // Connect to Blynk
  Blynk.begin(auth, ssid, pass, server, port);
  
  // Write ledState1 to local pin
  pinMode(ledPin1, OUTPUT);
  pinMode(btnPin1, INPUT_PULLUP);
  digitalWrite(ledPin1, ledState1);


  // Call blynkAnotherDevice every second
  timer.setInterval(100L, checkPhysicalButton1);
}

void loop()
{
  Blynk.run();
  timer.run();
}

The principal is the same, but as you’ve discovered using Bridge is a little clunky.
If you decide to migrate to Blynk 2.0 then it will be worse, because Bridge isn’t currently implemented and you have to use the API instead.

If you’re planning to build a functioning smart home, as opposed to working on a school or hobby project, then there are better ways to do this, which include using MQTT and Node-Red and which totally eliminate the need for bridging, whilst still maintaining the same Blynk functionality.

Pete.

Thanks, yeah I use MQTT with HomeAssistant for my home automation. This was a separate project that required sync across the internet.

Still do-able via MQTT.

Pete.