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.