Thanks. In case it helps here’s the current code:
#include <W42_Thermostat_Header.h>
#define ESP_NAME "ESP_RELAY_FLOOR_1"
char auth[] = auth_R_FLOOR_1;
const int FLOOR = 1; //used for initialization!
enum FLOOR_INDEX { FLOOR_1_LIVING, FLOOR_2_STUDY, FLOOR_3_BATH, FLOOR_3_BED, FLOOR_3_LAUNDRY, FLOOR_3_ARTHUR, FLOOR_4_CASPER}; //so: 0,1,2,3,4,5,6
String nameRLY[7] = {"FLOOR_1_LIVING", "FLOOR_2_STUDY", "FLOOR_3_BATH", "FLOOR_3_BED", "FLOOR_3_LAUNDRY", "FLOOR_3_ARTHUR", "FLOOR_4_CASPER"};
// ROOM_INDEX 0=LIVING, 1=STUDY, 2=BATH, 3=BED, 4=LAUNDRY, 5=ARTHUR, 6=CASPER
const int VHO[7] = {13,23,33,43,53,63,73}; //vpin for heatOn
const int VMO[7] = {14,24,34,44,54,64,74}; //vpin for maintenanceOn
const int VSA[4] = {15,25,35,45}; //vpin for ALIVE
// Note that there is no mem difference between const and define, however define is more bug prone!
// the pin that the Relay is attached to: LOW LVL TRIGGER (so GRND is ON)
// most rooms are connected to the 1st pin only D2. Only 3rd floor has 4 pins in use).
// so the order is: LIVING=D2, STUDY=D2, BATH=D2, BED=D5, LAUNDRY=D6, ARTHUR=D6, CASPER=D2
const int HEATER_RELAY_PIN[7] = {D2, D2, D2, D5, D6, D7, D2}; //safest pins to use are D1 and D2, then 5,6, and 7. The latter 3 go HIGH on boot then LOW
const int HEATER_RELAY_PIN_PUMP = D1; // the pin that the Relay is attached to: LOW LVL TRIGGER (so GRND is ON)
//debug mode:
//const long TIME_START_MAINTENANCE = 1000 * 60 * 12; // time to start maintenance cycle when valve state has not changed
//const long TIME_STOP_MAINTENANCE = 1000 * 60 * 6; // duration of the maintenance cycle
const long TIME_START_MAINTENANCE = 1000 * 15; // time to start maintenance cycle when valve state has not changed
const long TIME_STOP_MAINTENANCE = 1000 * 10; // duration of the maintenance cycle
const long TIME_UPDATE_CENTRAL = 1000; // update Blynk app every second
///////////////////////////////////VARIABLES////////////////////////////////////////////////
//constructor for timer
//SimpleTimer timer; // initialize timer construct
BlynkTimer timer;
WidgetBridge bridge_central(V0);
bool heatOn[7] = {false,false,false,false,false,false,false}; // turn on the heat or not (so: open valve or not)
bool maintenanceOn_V[7] = {false,false,false,false,false,false,false}; // open/close valve every so many hours
int maintenanceTimer_V[7]; // identifier of the valvemaintenance timer
bool maintenanceOn_P = false; // Make sure the pump runs 5m every 12 hours
int maintenanceTimer_P; // identifier of the pump maintenance timer
///////////////////////////////////VARIABLES////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////FUNCTIONS////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
BLYNK_CONNECTED() {
//do not use syncall as you should reset maintenance and send a trigger to central
bridge_central.setAuthToken(auth_CENTRAL);
}
void updateCentral(int i){
Serial.print("Updating CENTRAL H/M: ");
Serial.print(heatOn[i]);
Serial.print(" / ");
Serial.println(maintenanceOn_V[i]);
Serial.println();
bridge_central.virtualWrite(VHO[i], heatOn[i], ESP_NAME); //V13
bridge_central.virtualWrite(VMO[i], maintenanceOn_V[i]); //V14 send to central, central send to thermostat, thermostat must NOT send updates during cycle!!
digitalWrite(HEATER_RELAY_PIN[i], heatOn[i]?LOW:HIGH); // activate = LOW, deactivate = HIGH
}
//Read updates coming in from CENTRAL, which is only HeatOn !!
BLYNK_WRITE_DEFAULT(){
//all pins used consist out of 2 digits
int pin = request.pin;
int one = pin % 10; //this tells the type of operation //ten is the ESP but that can only be this one! Else you dont receive any update here!
int ten = pin/10 % 10 - 1; //this tells which device it came from (V10 = 1-1 = LIVING = 0)
int i = ten;
if(one = 3){
bool oldheatOn = heatOn[i];
heatOn[i] = (bool) param.asInt(); //store value that came via Bridge from CENTRAL
if(oldheatOn != heatOn[i]){
Serial.print("heatOn updated by central: ");
Serial.println(heatOn[i]);
Serial.print("Resetting maintenance timer");
digitalWrite(HEATER_RELAY_PIN[i], heatOn[i]?LOW:HIGH); // activate = LOW, deactivate = HIGH
runPump();
resetMaintenanceTimerValve(i);
}
/* else if(one = 9){
bool reset = (bool) param.asInt();
if(reset){
Blynk.virtualWrite(VRS,0);
delay(100);
ESP.restart();
delay(1000);
} */
}
}
void resetMaintenanceTimerValve(int i){
//A Maintanance cycle is initiated every 12 hours if NOTHING has happened
//Thus if SOMETHING happened; the 12 hour timer resets.
Serial.print("MaintenanceTimer for valve is reset to: ");
timer.restartTimer(maintenanceTimer_V[i]);
Serial.println(millis()/1000);
}
void turnOffmaintenanceValve(int i){
//This is called on a 1 time timer of TIME_STOP_MAINTENANCE seconds so the cycle is automatically stopped
Serial.println("Turning off valve maintenance cycle, resetting maintenance timer");
maintenanceOn_V[i] = false;
heatOn[i] = !heatOn[i]; //revert to original heat settings
resetMaintenanceTimerValve(i);
updateCentral(i);
}
void turnOffmaintenanceValve_0(){ turnOffmaintenanceValve(0); }
void turnOffmaintenanceValve_1(){ turnOffmaintenanceValve(1); }
void turnOffmaintenanceValve_2(){ turnOffmaintenanceValve(2); }
void turnOffmaintenanceValve_3(){ turnOffmaintenanceValve(3); }
void turnOffmaintenanceValve_4(){ turnOffmaintenanceValve(4); }
void turnOffmaintenanceValve_5(){ turnOffmaintenanceValve(5); }
void turnOffmaintenanceValve_6(){ turnOffmaintenanceValve(6); }
void maintenanceValve(int i){
/*the purpose of this cycle is to change the valve position at least
every 12 hours to prevent it from getting stuck.
This cycle ONLY starts when the valve position has not changed for
12 hours, its timer gets reset when the valve position changes*/
Serial.println("Valve maintenance Cycle started. Heat is turned: ");
maintenanceOn_V[i] = true;
heatOn[i] = !heatOn[i]; //This actually changes the Valve state so it moves
Serial.println(heatOn[i] ? "ON" : "OFF");
//turn off the maintenance cycle after 5 minutes (routine is run 1 time)
//after that the system resumes its old cycle
Serial.println("Starting 'turn off' valve cycle timer");
switch(i){
case 0: timer.setTimer(TIME_STOP_MAINTENANCE, turnOffmaintenanceValve_0, 1); break;
case 1: timer.setTimer(TIME_STOP_MAINTENANCE, turnOffmaintenanceValve_1, 1); break;
case 2: timer.setTimer(TIME_STOP_MAINTENANCE, turnOffmaintenanceValve_2, 1); break;
case 3: timer.setTimer(TIME_STOP_MAINTENANCE, turnOffmaintenanceValve_3, 1); break;
case 4: timer.setTimer(TIME_STOP_MAINTENANCE, turnOffmaintenanceValve_4, 1); break;
case 5: timer.setTimer(TIME_STOP_MAINTENANCE, turnOffmaintenanceValve_5, 1); break;
case 6: timer.setTimer(TIME_STOP_MAINTENANCE, turnOffmaintenanceValve_6, 1); break;
}
updateCentral(i);
}
void maintenanceValve_0(){ maintenanceValve(0); }
void maintenanceValve_1(){ maintenanceValve(1); }
void maintenanceValve_2(){ maintenanceValve(2); }
void maintenanceValve_3(){ maintenanceValve(3); }
void maintenanceValve_4(){ maintenanceValve(4); }
void maintenanceValve_5(){ maintenanceValve(5); }
void maintenanceValve_6(){ maintenanceValve(6); }
//////////////////////PUMP////////////////////
void runPump(){
if(maintenanceOn_P){ //run pump regardless of valve states
Serial.println("START PUMP MAINTENANCE");
digitalWrite(HEATER_RELAY_PIN_PUMP, LOW); // activate = LOW, deactivate = HIGH
}else{
if(heatOn[0]||heatOn[1]||heatOn[2]||heatOn[3]||heatOn[4]||heatOn[5]||heatOn[6]){ //run pump IF a valve is open aka heatOn = true
Serial.println("START PUMP");
digitalWrite(HEATER_RELAY_PIN_PUMP, LOW); // activate = LOW, deactivate = HIGH
resetMaintenanceTimerPump(); // reset maint. timer for pump as its now running.
}else{
Serial.println("STOP PUMP");
digitalWrite(HEATER_RELAY_PIN_PUMP, HIGH);
}
}
}
void resetMaintenanceTimerPump(){
//A Maintanance cycle is initiated every 12 hours if PUMP HAS NOT RUN
//Thus if the PUMP did run: the 12 hour timer resets.
Serial.println("MaintenanceTimer for pump is reset");
timer.restartTimer(maintenanceTimer_P);
}
void turnOffmaintenancePump(){
//This is called on a 1 time timer of TIME_STOP_MAINTENANCE seconds so the cycle is automatically stopped
Serial.println("STOP PUMP MAINTENANCE, resetting maintenance timer");
maintenanceOn_P = false;
runPump();
resetMaintenanceTimerPump();
}
void maintenancePump(){
Serial.println("Pump maintenance Cycle started.");
maintenanceOn_P = true;
runPump();
//turn off the maintenance cycle after 5 minutes (routine is run 1 time)
Serial.println("Starting 'turn off' pump cycle timer");
timer.setTimer(TIME_STOP_MAINTENANCE, turnOffmaintenancePump, 1);
}
//////////////////////PUMP////////////////////
void alive(){
bridge_central.virtualWrite(VSA[FLOOR-1], 200); //send 200 because it will update V15 LED of CENTRAL to that value indicating that its alive
}
void blinkOn(){
digitalWrite(2, HIGH); // turn the LED on (HIGH is the voltage level)
}
void blinkOff(){
digitalWrite(2, LOW); // turn the LED off (LOW is the voltage level)
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////END FUNCTIONS////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
void setup() {
/////////////////////////////////////BLYNK//////////////////////////////
// Serial.begin(115200);
Serial.begin(9600);
Serial.println("Booting");
Blynk.begin(auth, ssid, password, server, port);
while (Blynk.connect() == false) { } // Wait until connected
/////////////////////////////////////BLYNK//////////////////////////////
/////////////////////////////////////OTA//////////////////////////////
Serial.print("EPS Name: ");
Serial.println(ESP_NAME);
ArduinoOTA.setHostname(ESP_NAME);
// ArduinoOTA.setPassword("admin");
ArduinoOTA.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else // U_SPIFFS
type = "filesystem";
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start updating " + type);
});
ArduinoOTA.onEnd([]() {
Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
Serial.println("OTA Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
/////////////////////////////////////OTA//////////////////////////////
/////////////////////////////////////INIT PINS & FUNCTION TIMERS//////////////////////////////
Serial.println("Initiating all timed functions and Pins");
//set it up depending on which floor this relay is
switch(FLOOR){
case 1:
pinMode(HEATER_RELAY_PIN[0], OUTPUT); // The actual relay for valves
digitalWrite(HEATER_RELAY_PIN[0], HIGH); // activate = LOW, deactivate = HIGH
maintenanceTimer_V[0] = timer.setInterval(TIME_START_MAINTENANCE, maintenanceValve_0); //id required to reset it when required
updateCentral(0); //also make sure that central has he latest settings and that if a maintenance cycle was running it is reset.
break;
case 2:
pinMode(HEATER_RELAY_PIN[1], OUTPUT);
digitalWrite(HEATER_RELAY_PIN[1], HIGH);
maintenanceTimer_V[1] = timer.setInterval(TIME_START_MAINTENANCE, maintenanceValve_1);
updateCentral(1);
break;
case 3:
for(int i=2; i<=5; i++){
pinMode(HEATER_RELAY_PIN[i], OUTPUT);
digitalWrite(HEATER_RELAY_PIN[i], HIGH);
updateCentral(i);
}
maintenanceTimer_V[2] = timer.setInterval(TIME_START_MAINTENANCE, maintenanceValve_2);
maintenanceTimer_V[3] = timer.setInterval(TIME_START_MAINTENANCE, maintenanceValve_3);
maintenanceTimer_V[4] = timer.setInterval(TIME_START_MAINTENANCE, maintenanceValve_4);
maintenanceTimer_V[5] = timer.setInterval(TIME_START_MAINTENANCE, maintenanceValve_5);
break;
case 4:
pinMode(HEATER_RELAY_PIN[6], OUTPUT);
digitalWrite(HEATER_RELAY_PIN[6], HIGH);
maintenanceTimer_V[6] = timer.setInterval(TIME_START_MAINTENANCE, maintenanceValve_6);
updateCentral(6);
break;
}
pinMode(HEATER_RELAY_PIN_PUMP, OUTPUT); // The actual relay for pump
digitalWrite(HEATER_RELAY_PIN_PUMP, HIGH); // activate = LOW, deactivate = HIGH
maintenanceTimer_P = timer.setInterval(TIME_START_MAINTENANCE, maintenancePump); //id required to reset it when required
/////////////////////////////////////INIT PINS//////////////////////////////
/////////////////////////////////////ALIVE//////////////////////////////
Serial.println("Starting 'alive' routine...");
pinMode(2, OUTPUT);
timer.setInterval(1000, alive); //send update to CENTRAL every second indicating your still online
timer.setInterval(2000, blinkOff); //led on/off every second
delay(2100);
timer.setInterval(2000, blinkOn);
/////////////////////////////////////ALIVE//////////////////////////////
}
void loop() {
timer.run();
Blynk.run();
ArduinoOTA.handle();
}
edit: the ‘floor’ part might be a bit confusing. The point is is that this code is used for 4 different groupson 4 different floors. Each floor has its own id and different amounts of valves. So way a tthe top I can changes floor_1 into _2 or 3 or 4 and the code complies for that specific group.