How to pass a parameter to Blynktimer construct?

Currently I have this:

  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;
  }

and

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); }

which is just plain silly. What I would like is to replace all of the above with one line::

timer.setTimer(TIME_STOP_MAINTENANCE, turnOffmaintenanceValve(i), 1);

but that renders the following error:

Arduino: 1.8.5 (Windows 7), Board: "WeMos D1 R1, 80 MHz, 4M (1M SPIFFS), v2 Lower Memory, Disabled, None, Only Sketch, 921600"

D:\Dropbox\ARDUINO\HEATING_Relay\Heating_Relay.ino: In function 'void maintenanceValve(int)':

Heating_Relay:132: error: invalid use of void expression

   timer.setTimer(TIME_STOP_MAINTENANCE, turnOffmaintenanceValve(i), 1);

                                                                      ^

exit status 1
invalid use of void expression

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

Does anyone know how to do this properly?

I’m not sure you can escape a similar construct because you cannot do that like that with the Timers.

You could write a single void() to actuate all valve’s. E.g. void turnValve(valveID, valveOnOff);

You could do with one timer, but you’d have to set flags for the ID and OnOff somewhere and have the void() act on that.

alas I can’t. Each valve maintanance timer gets reset as soon as the valve is switched, which (from a code perspective) can happen at random intervals for each valve separately. So each valve must have its own timer.

Ok, that’s good to know, but you could handle the timers in that void too, right? You would need 7 timers anyway.

Timers can be added/deleted on the fly by the way https://playground.arduino.cc/Code/SimpleTimer

erm yes, and that would roughly result in the above code! In the end I still need to invoke a separate timer for each valve.

Thank you, I know, there’s a lot more going on then just that piece of code I posted. But that shows the gist of it. Point is is that I have quite a fewof these constructs in my code for several things. I was just looking for a more elegant method to deal with this.

There probably is, but I’m not quite sure how to make it since I don’t really have an idea of the specifcations and purpose of the software.

-edit-

And about your actual question, it cannot be done. I tried… you can only fire a simple void from the timer without any parameters…

well… There are 7 valves that need to be switched (opened or closed) at least every 12 hours (or they get stuck). So they all have their own maintenance timer that EITHER:

  • activates a maintenance cycle after 12 hours OR
  • gets reset when the valve is switched during the operating process

If a maintenance cycle is started then that valve gets a ‘stop maintenance’ timer which runs once after 5 minutes (It takes a valve 3 minutes to change state).
During a maintenance cycle ALL processes are ignored. After 5 minutes the ‘stop maintenance’ function runs and resets all the flags, initiates a new ‘maintenance timer’ (for 12 hours) and sets the valve in the state it should be.

Thats it.

That sounds like a nice case to write a good piece of code for though. I’ll give it some more though, no promises though :slight_smile:

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.

1 Like

So you have like a bunch of ESP’s each turning on/off one or more pumps?

exactly. To be precise:
ESP_1: 1 valve
ESP_2: 1 valve
ESP_3: 4 valves
ESP_4: 1 valve
in total 7.
(and they all communicate with a central ESP that keeps track of their states and sends this to the blynk app)
edit: they turn on/off 7 valves and 4 pumps.