BLYNK
HOME       📲 GETTING STARTED       📗 DOCS       ❓HELP CENTER       👉 SKETCH BUILDER

Quick question about Bridge widget

As of now, I was planning to put the 3 sensors to one ESP and make it the master. All of the slaves will just be turning on various devices/hardware for a time. That said, can I do all of the tests on the master, and have it tell the slave how long to activate the device for?

Edit - Another reason why I strongly suggest makers get a cheap 3D printer. They are so handy for quick designing custom project boxes for our projects. In both the small black case, as well as the Relays ESP, both utilize cellphone chargers to power the ESPs.

@GunnerTechTools, @Costas To your knowledge, is the following method possible, and if yes which data type can WidgetBridge be converted to? In the parent function, array positions are selected based on an app widget value, but in this project, I had to divide the previous grouping of 8 devices into 2 banks of 4, each with their own MCU/Auth Key.

  int bridgeX = Bridge2;
  if (x <= 3) {
    bridgeX = Bridge1;
  }
  else() {
    bridgeX = Bridge2;
  }

Sorry, you lost me here… not a clue what you are trying to do.

Assigning bridge name to a variable (based on sensor number?) to be used in calls somewhere in the code?

@mohan_sundaram Possibly?? But you assign a variable to a bridge like this…

WidgetBridge bridge1(V1); //Initiating Bridge Widget on V1

or this

WidgetBridge Fred(V1); //Initiating Bridge Widget on V1 

I want to control 4 devices from 1 using the bridge widget in a 1 master 3 slave configuration. So in the master sketch, I have defined

//Initiating Bridge Widget
WidgetBridge Bridge1(V0);   //Relays
WidgetBridge Bridge2(V1);   //Pumps 1-4
WidgetBridge Bridge3(V2);   //Pumps 5-8

BLYNK_CONNECTED() {
  Bridge1.setAuthToken("AUTH"); // Slave_Relays
  Bridge2.setAuthToken("AUTH"); // Slave_Pumps 1-4
  Bridge3.setAuthToken("AUTH"); // Slave_Pumps 5-8
  //Bridge4.setAuthToken("AUTH"); // Slave_XXX
}

Using a Step V widget, I flip between 8 positions that correlate to several array positions in the sketch. I want to designate positions 0-3 to direct the remaining flow of code to Bridge2 and array positions 4-7 I want diverted to Bridge3.

Why not just use a case switch statement and operate the appropriate bridge instead of assigning bridge names etc?

Well, you have already assigned identifications to the Bridges (Bridge1, Bridge2… etc), so I don’t think you can change their identification names mid-code, if that is what you are referring to.

According to the error I’m getting, I need to know what data type WidgetBridge is so that I can create the same type of variables, and then assign my array positions correctly.

I’m not so versed in coding and switch case is currently out of my knowledge. It took me the better part of a year to get a grasp on the arrays, but they work.

Switch and case are much easier to use than arrays.

I find the desire to bridge between 3 ESP boards, 1 master as the head, and 2 slaves as headless. That said, I’m trying to only have an app linked to the master ESP to interact with the overall project. The sensors all are controlled by the master as well and the slaves just apply power to relays or motors. So I aim to dial up a value with step widget, send the value to the master, and also send that value directly over the bridge, or copy the value to another variable to be sent over the bridge to the slave ESP. Would it be improper to ask for a pseudocode example without sharing my sketches? Until I figure out exactly what is and isn’t needed, both sketches are fairly large and I feel that sharing them at this time might confuse readers.

Edit - some processing would be done on each ESP. Master illuminates LED widgets in the app for the duration that the motors/relays are energized on the slaves.

if this is a serious long-term home automation project and theres’s the probability that in future you’ll want to add more slave devices then you should take a serious look at Node-Red and MQTT.

This is the system I use and it makes integrating devices so much easier. You still get the Blynk functionality, plus ease of integration with services such as Amazon Alexa. You have two-way communication between the master (which in this case is Node-Red running on a Raspberry Pi) and the slave devices.

Pete.

Thanks for the info Pete, but my code skills, though hard fought to earn over 4 years, are still very basic and restricted to the Arduino programming language. Working with ESPs are outside of my scope as well, but the Mega no longer functions well in my project so I needed to advance. Still, for now at least, I NEED to stay on my current path. I know my project can run reliably, I just need to fight my way through learning new things, ESP, Bridges, multiple devices sharing the responsibilities of what used to be a single MCUs job.

1 Like

You will need a project for each “slave” at least as far as setting up the device and AUTH… and of course code on the devices sides. But once setup, you should be able to effectively ignore those projects and control the devices via the master bridge project.

And setting up multiple devices in a single project (with Device Selector) should accomplish much the same thing without needing Bridge and separate projects.

Ok, I think I mis-explained my project, but here’s some code snippets to hopefully convey what I’m trying to do.

#define BLYNK_PRINT Serial          // Comment this out to     disable prints and save space
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <WidgetRTC.h>
#include <TimeLib.h>
#include <DHT.h>
#include <Wire.h>

char auth[] = "MASTER_AUTH";   // Cloud Server

//Initiating Bridge Widget
WidgetBridge Bridge1(V0);   //Relays
WidgetBridge Bridge2(V1);   //Pumps 1-4
WidgetBridge Bridge3(V2);   //Pumps 5-8

BLYNK_CONNECTED() {
  Bridge1.setAuthToken("SLAVE1"); // Slave_Relays
  Bridge2.setAuthToken("SLAVE2"); // Slave_Pumps 1-4
  Bridge3.setAuthToken("SLAVE3"); // Slave_Pumps 5-8
  //Bridge4.setAuthToken("AUTH"); // Slave_XXX
}
uint32_t msPerGallon = 33000; //ms per gallon
uint32_t ROstart = 0;
uint32_t countGallons;
boolean runningRO = false;
int ROpumpOn = 0;     //Blynk Triggered RO Pump
float totalGallons;   //Number of RO Gallons Selected in Widget
BLYNK_WRITE(V34) {
  totalGallons = param.asFloat();  // ROpump remote
}

void ROcheck()  //RO Pump = 34 seconds on time per gallon
{
  if (ROpumpOn == 1 && runningRO == false)            // Activates when Blynk button is toggled
  {
    Bridge1.virtualWrite(V61, totalGallons);          // Calculates length of runtime for pump
    terminal.print("Pumping:");
    terminal.print(totalGallons);
    terminal.println(" Gallons of RO");
    Blynk.virtualWrite(V33, 1);                     // Illuminates Blynk button widget
    runningRO = true;
    ROstart = millis();
    countGallons = msPerGallon * totalGallons;        // Calculates length of runtime for 
  }
  if (millis() - ROstart > countGallons)              // Determines when runtime ends
  {
    ROpumpOn = 0;
    runningRO = false;
    Blynk.virtualWrite(V32, 0);                 // Turn off lit button widget
    Blynk.virtualWrite(V33, 0);                 // Turn off lit button widget
  }
  terminal.flush();
}

I want the above sketch to retrieve the value from it’s app, then apply the value to a Vpin and send that value over to the below slave. The app will illuminate the button widget like an LED for the duration that the slave relay is energized.

//------------SLAVE RELAYS

#define BLYNK_PRINT Serial          // Comment this out to     disable prints and save space
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

char auth[] = "SLAVE1_AUTH";   // Cloud Server

char ssid[] = "network";
char pass[] = "password";

//Initiating Bridge Widget
WidgetBridge Bridge1(V0);   //Relays

uint32_t msPerGallon = 33000; //ms per gallon
uint32_t ROstart = 0;
uint32_t countGallons;
boolean runningRO = false;
int ROpumpOn = 0;     //Blynk Triggered RO Pump
float totalGallons;   //Number of RO Gallons Selected in Widget

BLYNK_WRITE(V61) {
  totalGallons = param.asFloat();  // ROpump remote
}
void ROcheck()  //RO Pump = 34 seconds on time per gallon
{
  if (totalGallons > 0 && runningRO == false)            // Activates when Blynk button is toggled
  {
    digitalWrite(ROpump, TURN_ON);
    runningRO = true;
    ROstart = millis();
    countGallons = msPerGallon * totalGallons;        // Calculates length of runtime for pump
  }
  if (millis() - ROstart > countGallons)              // Determines when runtime ends
  {
    digitalWrite(ROpump, TURN_OFF);
    runningRO = false;
  }  
}

By using the Bridge widget, I figure I can circumvent the need to manually switch between AUTHs using the selector widget. Am I misunderstanding that? I’ll use a selector widget if left with no other choice, but if possible, I would greatly prefer to use the bridges, and just work everything out in code. I realize figuring out all the back and forth will be a task, but once through it, I’ll have a worry free project again.

After some more farting around with the code, I’ve found that the above sketches more or less work the way I want so I was able to circumvent the need to use Device Selector. Why this is important to me is because I want to interact with the widgets to adjust quantity values only, and rely on the master to post appropriate values to respective devices as needed. It’s nothing that fascinates now, but as I continue to build out my project, I plan to add a small level of autonomy in that I set a flag that initiates a fairly long sequence of events involving several device and several motors/relays per each device. If I needed to continually switch the device selector, the ability to timely perform the tasks would be lost, but by working everything out in code, the sequence would then be able to set into motion in milliseconds. Still much more farting around to do, but I’m at least confident on how to proceed.

I’ve recently included Bridge into my project to control several ESP boards from a single ESP, and all to only use 1 app connected to the master ESP. I have a function that lets me scroll up a value with a Step V widget, and when I tick the button widget, the value is recorded into the master ESP, and also sent to the Bridge1 ESP and that slave ESP toggles a relay for a specified amount of time. That works great and is not my problem.

What I am having problems with is when trying to control the other 7 relays. Some will be controlled by RTC readings, and others by temp/humidity readings. In my previous project I also added in some button widgets to be able to turn the relays back on if they were coded to be off. It is these relays connected to slaveESP that I can’t gain the control of through the Bridge connection. Each time I test a new code edit, I also confirm that the original (semi complex) function works, and it’s good.

For the time being, I’d like to have the master ESP and app toggle the state of a Vpin, and have the slave ESP react to the state of the respective Vpins. Originally, I listed the V pins in the code, then changed to their variable names, and still no go. Here are the 2 sketches so far. I’m seeking assistance in my structuring for the bridge code. Once I know it, I will be further building the master sketch and beginning 2-3 more slave sketches, so I’ll have plenty of practice to sink any new skills.



#define BLYNK_PRINT Serial          // Comment this out to     disable prints and save space
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <WidgetRTC.h>
#include <TimeLib.h>
#include <DHT.h>
#include <Wire.h>

char auth[] = "masterAUTH";   // Cloud Server



//Initiating Bridge Widget
WidgetBridge Bridge1(V0);   //Relays
WidgetBridge Bridge2(V1);   //Pumps 1-4
WidgetBridge Bridge3(V2);   //Pumps 5-8

BLYNK_CONNECTED() {
  Bridge1.setAuthToken("slaveAUTH1"); // Slave_Relays
  Bridge2.setAuthToken("slaveAUTH2"); // Slave_Pumps 1-4
  Bridge3.setAuthToken("slaveAUTH3"); // Slave_Pumps 5-8
  //Bridge4.setAuthToken("AUTH"); // Slave_XXX
}

char ssid[] = "NetworkName";
char pass[] = "Password";

BlynkTimer timer;
byte blynkInterval = 3000;

WidgetTerminal terminal(V29);
WidgetRTC rtcWidget;            // requires RTC widget in app
DHT dhtA(13, DHT22);            // DHT instance named dhtA, (I/O pin, sensor type)
DHT dhtB(14, DHT22);            // DHT instance named dhtB, (I/O pin, sensor type)

//  DHT Reference Values - CHANGE THESE TO MANAGE YOUR ROOM'S CLIMATE - //
byte hiMaxTemp = 80;   // temp that triggers heat removal device(s) on
byte lowMaxTemp = 70;  // temp that triggers heat removal device(s) off
byte hiMinTemp = 55;   // temp that triggers heater on
byte lowMinTemp = 65;  // temp that triggers heater off
byte hiHum = 50;       // High humidity value that triggers dehumidifier on
byte lowHum = 40; // Low humidity value that triggers dehumidifier off

WidgetLED LEDa(V56);              //lightA Indicator
WidgetLED LEDb(V57);              //lightB Indicator
WidgetLED LEDc(V58);              //VentA Indicator
WidgetLED LEDd(V59);             //VentB Indicator
WidgetLED LEDe(V60);              //scrubberFan Indicator

#define lightA 16             //Relay 1
#define lightB 15             //Relay 2
#define pumpA 14              //Relay 3
#define pumpB 13              //Relay 4
#define ROpump 12             //Relay 5
#define scrubberFan 5        //Relay 6
#define VentA 4              //Relay 7
#define VentB 2              //Relay 8

#define TURN_ON 1 // TURN_ON and TURN_OFF are defined to account for Active LOW relays
#define TURN_OFF 0 // Used to switch relay states for on/off of 120VAC~ devices 

int pumpAon;          //Blynk Override to turn pumpA back on
int pumpBon;          //Blynk Override to turn pumpB back on

BLYNK_WRITE(V30) {
  pumpAon = param.asInt();  // pumpA remote
}
BLYNK_WRITE(V31) {
  pumpBon = param.asInt();  // pumpB remote
}

uint32_t msPerGallon = 33000; //ms per gallon
uint32_t ROstart = 0;
uint32_t countGallons;
boolean runningRO = false;
int ROpumpOn = 0;     //Blynk Triggered RO Pump
float totalGallons;   //Number of RO Gallons Selected in Widget

BLYNK_WRITE(V32) {    //V33 Reserved to illuminate button
  ROpumpOn = param.asInt();  // ROpump remote
}
BLYNK_WRITE(V34) {
  totalGallons = param.asFloat();  // ROpump remote
}

//-Digital Pins - Peristaltic Pump Variables - 00 Series Pins
const int pumpPin[8] = { 2, 4, 5, 12, 13, 14, 15, 16 };
int dosingLEDs[8]  = { V40, V41, V42, V43, V44, V45, V46, V47 };
uint32_t multiplier[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; //ms per ml
uint32_t startPump = 0;
uint32_t runCount;
bool pumpRunning;
const char *nuteType[8] = { "GH Armor Si", "GH Flora Blend", "GH CALiMAGic", "GH Kool Bloom",
                            "GH Flora Gro", "GH Flora Micro", "GH Flora Bloom", "GH pH Down"
                          }; //Text Printed to Terminal Widget^^

float DOSEml;      //Step Widget (0.25 per step, send step/NO, loop values ON)
int button = 0;   //Button Widget set to Switch
int x;           //Correlates Array Positions with Pump Motor Pins
int calibration;

BLYNK_WRITE(V36) {    //X = Pump Select
  x = param.asInt() - 1;
}
BLYNK_WRITE(V37) {
  DOSEml = param.asFloat();
}
BLYNK_WRITE(V38) {    //Pump Start
  button = param.asInt();
}
BLYNK_WRITE(V39) {
  calibration = param.asInt();
}
int AA;
int BB;
int CC;
int DD;
int EE;
int FF;
int GG;

BLYNK_WRITE(V60) {
  AA = param.asInt();  // lightA remote
}
BLYNK_WRITE(V61) {
  BB = param.asInt();  // lightB remote
}
BLYNK_WRITE(V62) {
  CC = param.asInt();  // pumpA remote
}
BLYNK_WRITE(V63) {
  DD = param.asInt();  // pumpB remote
}
BLYNK_WRITE(V65) {
  EE = param.asInt();  // scrubberFan remote
}
BLYNK_WRITE(V66) {
  FF = param.asInt();  // ventA remote
}
BLYNK_WRITE(V67) {
  GG = param.asInt();  // ventB remote
}
//---------------------------------------------------------------//
//----------------------------Functions--------------------------//
void checkBlynk() {
  unsigned long startConnecting = millis();
  unsigned int Timeout = 5000;
  while (!Blynk.connected())
  {
    Blynk.connect();
    if (millis() > startConnecting + Timeout)
    {
      break;
    }
  }
}

void climateRoutine()
{
  byte h1 = dhtA.readHumidity();          // f1 and h1 are fahrenheit and humidity readings
  byte f1 = dhtA.readTemperature(true);   // from DHT/A
  byte h2 = dhtB.readHumidity();          // f2 and h2 are fahrenheit and humidity readings
  byte f2 = dhtB.readTemperature(true);   // from DHT/B
  if (isnan(f1) || isnan(f2) || isnan(h1) || isnan(h2)) {
    terminal.println("Failed to read from a DHT sensor");
    terminal.flush();
    return;
  }
  Blynk.virtualWrite(V10, f1);      //  Set Virtual Pin 0 frequency to PUSH in Blynk app
  Blynk.virtualWrite(V11, h1);      //  Set Virtual Pin 1 frequency to PUSH in Blynk app
  Blynk.virtualWrite(V12, f2);      //  Set Virtual Pin 2 frequency to PUSH in Blynk app
  Blynk.virtualWrite(V13, h2);      //  Set Virtual Pin 3 frequency to PUSH in Blynk app
  //-------------------Bloom A Temp Test---------------------------------------//
  bool F1;
  if (f1 >= hiMaxTemp)
  {
    Bridge1.virtualWrite(FF, TURN_ON); 
    if (F1)
    {
      F1 = false;
      terminal.println("Exhausting the heat from Bloom A"); //  Text printed to terminal monitor
    }
  }
  else if (f1 <= lowMaxTemp)  
  {
    F1 = true;
    Bridge1.virtualWrite(FF, TURN_OFF); 
  }
  //-----------------------Bloom A Humidity Test-------------------------//
  bool H1;
  if (h1 >= hiHum)
  {
    Bridge1.virtualWrite(FF, TURN_ON);  
    if (H1)
    {
      H1 = false;
      terminal.println("Exhausting the RH from Bloom A"); 
    }
  }
  else if (h1 <= lowHum) 
  {
    H1 = true;
    Bridge1.virtualWrite(FF, TURN_OFF);
  }
  //-----------------------Bloom B Temp Test-----------------------------//
  bool F2;
  if (f2 >= hiMaxTemp)  
  {
    Bridge1.virtualWrite(GG, TURN_ON);  
    if (F2)
    {
      F2 = false;
      terminal.println("Exhausting the heat from Bloom B"); 
    }
  }
  else if (f2 <= lowMaxTemp)  
  {
    F2 = true;
    Bridge1.virtualWrite(GG, TURN_OFF);
  }
  //-----------------------Bloom B Humidity Test-------------------------//
  bool H2;
  if (h2 >= hiHum)  
  {
    Bridge1.virtualWrite(GG, TURN_ON);  
    if (H2)
    {
      H2 = false;
      terminal.println("Exhausting the RH from Bloom B"); 
    }
  }
  else if (h2 <= lowHum)  
  {
    H2 = true;
    Bridge1.virtualWrite(GG, TURN_OFF);
  }
  terminal.flush();
}

void displayDateTime()
{
  byte HOUR = hour();
  byte twelveHour = hour() - 12; // Variable used to display 13+ hours in 12 hour format
  byte zeroHour = 12;                // Variable use to convert "0" zero hour to display it as 12:00+
  byte displayHour;
  byte MIN = minute();
  byte SEC = second();
  char* meridian;

  if (hour() == 0)  // First we test if the hour reads "0"
  {
    displayHour = zeroHour;
    meridian = "AM";
  }
  else if (hour() >= 13)  // if no, Second we test if the hour reads "13 or more"
  {
    displayHour = twelveHour;
    meridian = "PM";
  }
  else
  {
    displayHour = hour();
    meridian = "AM";
  }
  if (Blynk.connected())
  {
    char Clock[16];
    char Date[16];
    sprintf(Clock, "%02d:%02d:%02d-%02s", displayHour, MIN, SEC, meridian);
    sprintf(Date, "%02d/%02d/%04d", month(), day(), year());
    Blynk.virtualWrite(V26, Clock);      //  Set Value Display frequency to PUSH in Blynk app
    Blynk.virtualWrite(V27, Date);      //  Set Value Display frequency to PUSH in Blynk app
  }
}
void timeRoutine()
{
  //------------------12/12 BloomA Light - 5AM-5PM
  //Adjust hours and minutes in accordance with 24 hour time format.
  //Create tests where true is ON time and false is OFF time.
  boolean lightAstate = false;
  bool Aon;
  if (hour() >= 5 && hour() <= 16) lightAstate = true;
  if (lightAstate == true)
  {
    Bridge1.virtualWrite(AA, TURN_ON);
    if (Aon)
    {
      Aon = false;
      terminal.println("Lights On In Bloom A");  //  Text printed to terminal monitor
    }
  }
  else
  {
    Aon = true;
    Bridge1.virtualWrite(AA, TURN_OFF);
  }

  //--------------------12/12 Bloom B Light - 5PM-5AM
  //lightB is lit during the opposing 12 hours to lightA to conserve current draw from HID ballasts.
  boolean lightBstate = false;
  bool Bon;
  if (lightAstate == false) lightBstate = true;
  if (lightBstate == true)
  {
    Bridge1.virtualWrite(BB, TURN_ON);
    if (Bon)
    {
      Bon = false;
      terminal.println("Lights On In Bloom B");  //  Text printed to terminal monitor
    }
  }
  else
  {
    Bon = true;
    Bridge1.digitalWrite(BB, TURN_OFF);
    Blynk.virtualWrite(V57, 0);
  }
  //---------------Bloom A Feed Times------------------------
  boolean pumpAstate = false;
  bool Arun;
  if (pumpAon == 1) pumpAstate = true;
  if (hour() == 6 && minute() >= 0 && minute() < 10) pumpAstate = true;    //6:00 am - 10 mins
  if (hour() == 8 && minute() >= 30 && minute() < 40) pumpAstate = true;  //8:30 am - 10 mins
  if (hour() == 11 && minute() >= 00 && minute() < 10) pumpAstate = true;  //11:00 am - 10 mins
  if (hour() == 13 && minute() >= 30 && minute() < 40) pumpAstate = true;  //1:30 pm - 10 mins
  if (hour() == 16 && minute() >= 0 && minute() < 10) pumpAstate = true;  //4:00 pm - 10 mins
  if (pumpAstate == true)
  {
    Blynk.virtualWrite(V30, 1);
    Bridge1.virtualWrite(CC, TURN_ON);
    if (Arun)
    {
      Arun = false;
      terminal.println("Pump A Is On");  //  Text printed to terminal monitor
    }
  }
  else
  {
    Arun = true;
    pumpAon = 0;
    Blynk.virtualWrite(V30, 0);
    Bridge1.virtualWrite(CC, TURN_OFF);
  }
  //---------------------------Bloom B Feed Times-------------------------------------
  boolean pumpBstate = false;
  bool Brun;
  if (pumpBon == 1) pumpBstate = true;
  if (hour() == 18 && minute() >= 0 && minute() < 10) pumpBstate = true;     //6:00 pm - 10 mins -//- 1 hour after light on
  if (hour() == 20 && minute() >= 30 && minute() < 40) pumpBstate = true;   //8:30 pm - 10 mins
  if (hour() == 23 && minute() >= 0 && minute() < 10) pumpBstate = true;     //11:00 pm - 10 mins
  if (hour() == 1 && minute() >= 30 && minute() < 40) pumpBstate = true;    //1:30 am - 10 mins
  if (hour() == 4 && minute() >= 0 && minute() < 10) pumpBstate = true;     //4:00 am - 10 mins
  if (pumpBstate == true)
  {
    Blynk.virtualWrite(V31, 1);
    Bridge1.virtualWrite(DD, TURN_ON);
    if (Brun)
    {
      Brun = false;
      terminal.println("Pump B Is On");  //  Text printed to terminal monitor
    }
  }
  else
  {
    Brun = true;
    pumpBon = 0;
    Blynk.virtualWrite(V31, 0);
    Bridge1.virtualWrite(DD, TURN_OFF);
  }
  terminal.flush();
}

void ROcheck()  //RO Pump = 34 seconds on time per gallon
{
  if (ROpumpOn == 1 && runningRO == false)            // Activates when Blynk button is toggled
  {
    Bridge1.virtualWrite(V64, totalGallons);          
    terminal.print("Pumping:");
    terminal.print(totalGallons);
    terminal.println(" Gallons of RO");
    Blynk.virtualWrite(V33, 1);                     // Illuminates Blynk button widget
    runningRO = true;
    ROstart = millis();
    countGallons = msPerGallon * totalGallons;        // Calculates length of runtime for
    Blynk.virtualWrite(V34, 0);
  }
  if (millis() - ROstart > countGallons)              // Determines when runtime ends
  {
    ROpumpOn = 0;
    runningRO = false;
    Blynk.virtualWrite(V32, 0);                 // Turn off lit button widget
    Blynk.virtualWrite(V33, 0);                 // Turn off lit button widget
  }
  terminal.flush();
}

void dosingPumps()
{
  /*  #define bridgeX = Bridge2;
    if ([x] <= 3) {
      bridgeX = Bridge1;
    }
    else() {
      bridgeX = Bridge2;
    }*/
  if (button == 1 && pumpRunning == false)
  {
    multiplier[x] = calibration;                      // Gets the value in [x] position from Blynk
    Blynk.virtualWrite(V36, 0);
    Blynk.virtualWrite(V37, 0);
    Blynk.virtualWrite(V38, 0);                        // Keeps button ON until fully executed
    pumpRunning = true;
    Bridge2.digitalWrite(pumpPin[x], HIGH);
    Blynk.virtualWrite(dosingLEDs[x], 255);           // [x] position Blynk indicator LED
    startPump = millis();
    runCount = DOSEml * multiplier[x];
    terminal.print("Dosing in: ");
    terminal.print(DOSEml);
    terminal.print(" milliliters of ");
    terminal.println(nuteType[x]);
  }
  if (millis() - startPump > runCount)
  {
    Bridge2.digitalWrite(pumpPin[x], LOW);
    Blynk.virtualWrite(dosingLEDs[x], 0);
    pumpRunning = false;
    button = 0;
  }
  terminal.flush();
}

//------------------------Functions------------------------------//
//---------------------------------------------------------------//

void setup()
{
  Serial.begin(115200);
  Wire.begin(SDA, SCL);                                   //Configures I2C for ESP8266
  Blynk.begin(auth, ssid, pass, "blynk-cloud.com", 8442); // Cloud Server
  //Blynk.begin(auth, ssid, pass, server_ip, 8442);       // Local Server
  WiFi.begin(ssid, pass);
  Blynk.config(auth);
  dhtA.begin();
  dhtB.begin();
  rtcWidget.begin();

  pinMode(13, INPUT_PULLUP);    // DHT22 use internal pullup resistors
  pinMode(14, INPUT_PULLUP);    // DHT22 use internal pullup resistors

  timer.setInterval(2002L, timeRoutine);         // 2 second intervals between timed routiness
  timer.setInterval(5001L, climateRoutine);      // 5 second intervals between climate routines
  timer.setInterval(1000L, displayDateTime);     // update the LCD Widget every 5 seconds
  //timer.setInterval(2007L, dosingPumps);         // 0.2 second interval to maintain accuracy (+/- .25)
  timer.setInterval(2003L, ROcheck);             // 1 second interval between RO pump routines
  timer.setInterval(blynkInterval, checkBlynk);  // check connection to server per blynkInterval

  //while (Blynk.connect() == false) {}

  for (int VV = 0; VV < 100; VV++) {   //Vpins 0-50 defaulted to zero
    Blynk.virtualWrite(VV, 0);
  }
  terminal.flush();
  delay(1000);
  Serial.println("setup complete.");
}

void loop()
{
  // only attempt Blynk-related functions when connected to Blynk
  if (Blynk.connected())
  {
    Blynk.run();
  }
  timer.run();
}

Slave Sketch

//------------SLAVE RELAYS

#define BLYNK_PRINT Serial          // Comment this out to     disable prints and save space
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

char auth[] = "slaveAUTH1";   // Cloud Server

char ssid[] = "NetworkName";
char pass[] = "Password";

//Initiating Bridge Widget
WidgetBridge Bridge1(V0);   //Relays
BlynkTimer timer;
byte blynkInterval = 3000;

uint32_t msPerGallon = 33000; //ms per gallon
uint32_t ROstart = 0;
uint32_t countGallons;
boolean runningRO = false;
float totalGallons;   //Number of RO Gallons Selected in Widget

int lightA = 16;             //Relay 1
int lightB = 15;             //Relay 2
int pumpA = 14;              //Relay 3
int pumpB = 13;              //Relay 4
int ROpump = 12;             //Relay 5
int scrubberFan = 5;         //Relay 6
int ventA = 4;               //Relay 7
int ventB = 2;               //Relay 8

int AA; //lightA vPIN
int BB; //lightB vPIN
int CC; //pumpA vPIN
int DD; //pumpB vPIN
int EE; //scrubberFan vPIN
int FF; //ventA vPIN
int GG; //ventB vPIN

#define TURN_ON LOW // TURN_ON and TURN_OFF are defined to account for Active LOW relays
#define TURN_OFF HIGH // Used to switch relay states for on/off of 120VAC~ devices 

BLYNK_WRITE(V60) {
  AA = param.asInt();  // lightA remote
}
BLYNK_WRITE(V61) {
  BB = param.asInt();  // lightB remote
}
BLYNK_WRITE(V62) {
  CC = param.asInt();  // pumpA remote
}
BLYNK_WRITE(V63) {
  DD = param.asInt();  // pumpB remote
}
BLYNK_WRITE(V64) {
  totalGallons = param.asFloat();  // ROpump remote
}
BLYNK_WRITE(V65) {
  EE = param.asInt();  // scrubberFan remote
}
BLYNK_WRITE(V66) {
  FF = param.asInt();  // ventA remote
}
BLYNK_WRITE(V67) {
  GG = param.asInt();  // ventB remote
}

//---------------------------------------------------------------//
//----------------------------Functions--------------------------//
void checkBlynk() {
  unsigned long startConnecting = millis();
  unsigned int Timeout = 5000;
  while (!Blynk.connected())
  {
    Blynk.connect();
    if (millis() > startConnecting + Timeout)
    {
      break;
    }
  }
}

void ROcheck()  //RO Pump = 34 seconds on time per gallon
{
  if (totalGallons > 0 && runningRO == false)            // Activates when Blynk button is toggled
  {
    Serial.print(totalGallons);
    Serial.println(" - Gallons");
    digitalWrite(ROpump, TURN_ON);
    runningRO = true;
    ROstart = millis();
    countGallons = msPerGallon * totalGallons;        // Calculates length of runtime for pump
  }
  if (millis() - ROstart > countGallons)              // Determines when runtime ends
  {
    Blynk.virtualWrite(V64, 0);
    totalGallons = 0;
    digitalWrite(ROpump, TURN_OFF);
    runningRO = false;
  }
}

void relayCommands()
{
  if (AA == 1){
    digitalWrite(lightA, TURN_ON);
  }
  else if (AA == 0){
    digitalWrite(lightA, TURN_OFF);
  }
  if (BB == 1){
    digitalWrite(lightB, TURN_ON);
  }
  else if (BB == 0){
    digitalWrite(lightB, TURN_OFF);
  }
  if (CC == 1){
    digitalWrite(pumpA, TURN_ON);
  }
  else if (CC == 0){
    digitalWrite(pumpA, TURN_OFF);
  }
  if (DD == 1){
    digitalWrite(pumpB, TURN_ON);
  }
  else if (DD == 0){
    digitalWrite(pumpB, TURN_OFF);
  }
  /*if (V64 == 1){
    digitalWrite(ROpump, TURN_ON);
  }
  else {
    digitalWrite(ROpump, TURN_OFF);
  }*/
  if (EE == 1){
    digitalWrite(scrubberFan, TURN_ON);
  }
  else if (EE == 0){
    digitalWrite(scrubberFan, TURN_OFF);
  }
  if (FF == 1){
    digitalWrite(ventA, TURN_ON);
  }
  else if (FF == 0){
    digitalWrite(ventA, TURN_OFF);
  }
  if (GG == 1){
    digitalWrite(ventB, TURN_ON);
  }
  else if (GG == 0){
    digitalWrite(ventB, TURN_OFF);
  }
}

//------------------------Functions------------------------------//
//---------------------------------------------------------------//

void setup()
{
  Serial.begin(115200);
  Blynk.begin(auth, ssid, pass, "blynk-cloud.com", 8442); // Cloud Server
  //Blynk.begin(auth, ssid, pass, server_ip, 8442);       // Local Server
  WiFi.begin(ssid, pass);
  Blynk.config(auth);

  pinMode(lightA, OUTPUT);
  pinMode(lightB, OUTPUT);
  pinMode(pumpA, OUTPUT);
  pinMode(pumpB, OUTPUT);
  pinMode(ROpump, OUTPUT);
  pinMode(scrubberFan, OUTPUT);
  pinMode(ventA, OUTPUT);
  pinMode(ventB, OUTPUT);

  digitalWrite(lightA, TURN_OFF);
  digitalWrite(lightB, TURN_OFF);
  digitalWrite(pumpA, TURN_OFF);
  digitalWrite(pumpB, TURN_OFF);
  digitalWrite(ROpump, TURN_OFF);
  digitalWrite(scrubberFan, TURN_OFF);
  digitalWrite(ventA, TURN_OFF);
  digitalWrite(ventB, TURN_OFF);

  timer.setInterval(250L, relayCommands);          // 25 second intervals between timed routiness
  timer.setInterval(201L, ROcheck);                  // .2 second interval between RO pump routines
  timer.setInterval(blynkInterval, checkBlynk);    // check connection to server per blynkInterval

  //while (Blynk.connect() == false) {}

  for (int VV = 0; VV < 100; VV++) {   //Vpins 0-50 defaulted to zero
    Blynk.virtualWrite(VV, 0);
  }
  delay(1000);
  Serial.println("setup complete.");
}

void loop()
{
  // only attempt Blynk-related functions when connected to Blynk
  if (Blynk.connected())
  {
    Blynk.run();
  }
  timer.run();
}

The function that DOES work well is called ROcheck. It controls a water pump connected to the relay and metes out 0.25-10.00 gallons of water, though the volume and frequency can easily be changed in the app setup. Once I learn the syntax of how to control the relays, I will work to flesh out my project more as I’d ultimately like to be able to send respective sensor readings from device to device so to allow the recipient to have enough information to process to execute a task in the event the comm link is severed. For now, I just want to get over this hurdle and gain the ability to turn things on or off over the bridge.

Funny thing: I’m currently doing roughly the same thing as you. My setup is
thermostat<–>central<–>relay
where central also communicates with the app.

Anyway I had a look at your code and couldn’t quite make sense of it, but its probably related to you not yet getting the syntax.

It took me a while to get use to it but essentially it boils down to:

  1. create a channel from device MASTER to SLAVE. You do this in the code of MASTER. As far as i can see you did that:
    WidgetBridge Bridge1(V0); //Relays
    BLYNK_CONNECTED() {
    Bridge1.setAuthToken(“slaveAUTH1”); // Slave_Relays
    etc.
  2. Sending anything from MASTER to SLAVE goes over channel V0, so best to NOT use it for anything else.
  3. You can now use (in MASTER code)
    Bridge1.digitalwrite to directly update a physical pin or
    Bridge1.virtualWrite to update a virtual pin on SLAVE. Lets assume you do the latter and update V2
  4. then on SLAVE you need an ‘event handler’ for V2 which is
    BLYNK_WRITE(V2) {
    //this code is activated as soon as V2 is remotely changed by MASTER
    //retrieving any send value or values over V2 is done with:
    value = param.asInt();
    }

That’s it. Communication the other way round is also possible but then you have to open up a channel on SLAVE as well (to master) and basically the same applies as i’ve just described.

1 Like

Thanks for the reply @wolph42 . As best as I can tell, my sketches adhere to your instructions. V0 is for the bridge itself and is never referred to again. I’m using V60-V67 with V64 reserved as a float() for th ROcheck() function. The rest are ints. I generically named then in sequence AA-GG, skipping V64. To cover my bets, I duplicated the Vpin reference in both sketches. In master sketch

BLYNK_WRITE(V60) {
  AA = param.asInt();  // lightA remote
}
if (test == true)
{
  Bridge1.virtualWrite(AA = 1);
}

and in slave sketch

BLYNK_WRITE(V60) {
  AA = param.asInt();  // lightA remote
}
if (AA == 1)
{
  digitalWrite(pin, TURN_ON);
}

Assuming the above relationship generates a value of 1, puts it into int AA, sends that over Bridge1 in the AA vPin of V60, the slave should adjust the slaves digital pin accordingly, correct?