Not ;) Giving up with understanding timers

Hi. Been trying to resolve this for 4 or 5 weeks for now. Sure that is simple, just i cannot get it.
So making central heating controller with Wemos, Nextion and Blynk. Ideally would be to check temp sensor once a second. If setpoint is higher than current temp, heating comes on for one minute. Since temp and setpoint are equal, heating goes off for at least one minute. I been trying lots of different ways, obviously i don`t understand this timer business. So i am asking your help with it. Code attached.

//NEXTION //////////////////////////////////////////////////////////////////////////////////////////
#define BLYNK_PRINT Serial
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <SoftwareSerial.h>
#include <Nextion.h>
#include <BME280I2C.h>
#include <Wire.h>

BME280I2C bme;
WidgetBridge bridge1(V1);
BlynkTimer timer;

char auth[] = "*********************************";
char ssid[] = "M******";
char pass[] = "*********";

#define led 2
int readings[60];
SoftwareSerial nextion(12, 13);
Nextion myNextion(nextion, 9600);

int diff;
int setpoint;
int pinData;
float temp;


int readIndex = 0;              // the index of the current reading
int total = 0;                  // the running total
int average = 20; 

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

BLYNK_WRITE(V1)  // Setpoint reading from BLYNK
{
  int setpoint = param.asInt();
  myNextion.setComponentValue("Heat.n1" , setpoint);
}

BLYNK_WRITE(V30)
{
  int diff = param.asInt();
  myNextion.setComponentValue("page0.diff" , diff);
}

BLYNK_CONNECTED() {
  bridge1.setAuthToken("5a281e480ac14cad9e124d91a09e80a2"); // Token of the hardware B
}

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

void sendSensor()
{
  BME280::TempUnit tempUnit(BME280::TempUnit_Celcius);
 float temp = bme.temp();
  int hum = bme.hum();


  total = total - readings[readIndex];
  // read from the sensor:
  readings[readIndex] = temp;
  // add the reading to the total:
  total = total + readings[readIndex];
  // advance to the next position in the array:
  readIndex = readIndex + 1;

  // if we're at the end of the array...
  if (readIndex >= 60) {
// ...wrap around to the beginning:
readIndex = 0;
  }

  // calculate the average:
  average = total / 60;

 
  

  Blynk.virtualWrite(V6, average);
  Blynk.virtualWrite(V7, temp);
  Blynk.virtualWrite(V5, hum);
  myNextion.setComponentValue("Heat.n0" , average);
myNextion.setComponentText ("page0.t1", String (temp, 2));
int diff = myNextion.getComponentValue("page0.diff");
  Serial.print(diff);
  Serial.print("    ");
  Serial.print(hum);
  Serial.print("   ");
  Serial.println(average);

}

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

void setPoint() {

  int setpoint =  myNextion.getComponentValue("Heat.n1");
  Blynk.virtualWrite(V1, setpoint);
}

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

void calc() {

  Serial.println("      calc     ");
  float temp = myNextion.getComponentValue("Heat.n0");
  int setpoint = myNextion.getComponentValue("Heat.n1");
 
  if (setpoint <= temp) {
timer.setTimeout(10000L * 6, offTime);
  } else {
timer.setTimeout(10000L * 6, ontime);
  }

}

void offTime() {

  Serial.println("///////////OFF////////////");
  digitalWrite(led, LOW);
  Blynk.virtualWrite(V20, 0);
  bridge1.digitalWrite(5, LOW);
  myNextion.setComponentText("Heat.t2" , "off");
  myNextion.setComponentText("page0.t0" , "off");
myNextion.setComponentText ("page0.t1", String (temp, 2));

}

void ontime() {
  Serial.println("-------------ON--------------");
  digitalWrite(led, HIGH);
  Blynk.virtualWrite(V20, 255);
  bridge1.digitalWrite(5, HIGH);
  myNextion.setComponentText("Heat.t2" , "on");
  myNextion.setComponentText("page0.t0" , "on");
  myNextion.setComponentText ("page0.t1", String (temp, 2));
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup() {


  Serial.begin(9600);
  Blynk.begin(auth, ssid, pass);
  while (Blynk.connect() == false) {
// Wait until Blynk is connected
  }
  while (!Serial) {} // Wait
  bridge1.digitalWrite(5, LOW);
  Wire.begin();
 rtc.begin();
  while (!bme.begin())
  {
Serial.println("Could not find BME280 sensor!");
delay(1000);
  }

  pinMode(led, OUTPUT);
  myNextion.init();
  // Setup a function to be called every second
  timer.setInterval(1000L , sendSensor);
  timer.setInterval(300L, setPoint);
  timer.setInterval(1000L * 60, calc);


}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop() {

  Blynk.run();
  timer.run();

}

It is working overall, temperature readings in the room are within 0.75 degrees. But it is not doing what i am trying to achieve. So when i turn it on, first On or Off action happens after quite a while. Also often in serial monitor i can see that setpoint is 23, temperature is 21 and still getting OFF cycle being activated.

I think one of your issues is that you should have different timer objects for your different timer id’s that are calling sendSensor, setPoint and calc. Have a look at SimpleTimer library documentation. It’s essentially the same as BlynkTimer.

Also setTimeout only calls the function after the timeout period has expired. So it will not do anything for 60 seconds, and then it will call offtime or ontime depending on the set point.

Before you spend too much time working-out why this isn’t working, it might be worth you re-thinking your strategy. The traditional way of doing temperature control is to use hysteresis. This uses a plus and minus temperature tolerance to ensure that the heating doesn’t kick-in and turn off too frequently.
You could have different high and low tollerence values, but less assume that you use 0.25 degrees for both the plus and minus values.
If your target temperature is 22 degrees and the heating is currently off, the heating won’t kick-in until the room temperature drops to 21.75 degrees (0.25 below target). When the heating does kick-in, it will run until the room temperature is 22.25 degrees (0.25 above target).
You can tweak the values to get the desired effect with your particular heating system.

Also, why check the room temperature every second?
I’d have thought that every 5 seconds would be the absolute highest frequency you would consider. Some sensors like the DHT11/22 are quite slow and you won’t get a sensible reading if you query them every second.

I’m currently working on a heating controller that uses a Nextion, MQTT, Node-Red, Blynk and Alexa. I’ll have a couple of temperature sensors in the room that are averaged to give a temperature reading. The primary heating systems are an inverter aircon system and a couple of wall mounted fans. The aircon is controlled by infrared and the wall heaters by a couple of Sonoff S20’s. During the summer it will also control cooling, using the aircon and a ceiling fan which is also controlled by infrared.
It’s starting to come together at the moment, but there’s still a lot to do!

Good luck with your project.

Pete.

2 Likes

Thank you for advice. Hysteresis is too smart for me. Look i cannot figure out simple timer. Room temperature i am checking every second while i am tinkering around my code.

Sorry of being annoying, could anyone write a piece of code for me, to understand timers usage? Say read pot on A1 once a second, if value < 500, turn led on for 5 seconds, off for 5 seconds. After that read a pot once a second. I am stuck otherwise. Thank you in advance.

Well OK, but one reason we recommend you search around this forum, Google, etc, is that while someone can write you some code… you still may not understand how it works… or it may not fit your needs exactly, thus needing more explanation, etc.

Also then this ‘usefull’ code only sorta benefits you … unless later on, someone else takes the time and effort to search for it :stuck_out_tongue:

So to be clear… we do try to help, and will assist with code snippets when one at least shows they have tried… as you have done :+1: So take the time to break this supplied code down in your mind and you will slowly learn how it works…

You should be able to just fill in your WiFi/Auth info and flash this as is… although it doesn’t do any Blynk stuff on the App, just a simple Potentiometer and LED timed function. I did test it on my setup, and then modified for yours… so, hopefully not, but there may be a compile error or two for you to figure out :innocent:

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

BlynkTimer timer;

char auth[] = "*********************************";
char ssid[] = "M******";
char pass[] = "*********";

#define led 2
int Flag = 0;

BlynkTimer timer;


void setup()
{
  pinMode(led, OUTPUT);  // Declare LED pin as output

  Blynk.begin(auth, ssid, pass);

// Special lambda abstraction method of processing timer & called commands in compact form
  timer.setInterval(1000L, []() {  // Constant timer that check analog port every second
    if (analogRead(A1) >= 500 && Flag == 0) {  // Only processes following command if both analog and flag conditions are correct
      Flag = 1;  // Set flag to prevent further Pot action until ready again
      FlashLED();  //Run LED function
    }
  });

}


void FlashLED() {
  digitalWrite(led, HIGH);  // Turns ON LED

// Special lambda abstraction method of processing timer & called commands in compact form
  timer.setTimeout(5000L, []() {  // Oneshot countdown timer that will turn OFF LED in 5 seconds
    digitalWrite(led, LOW);  // Turns OFF LED
  });
  
// The preceding and following timers are processed immediately one after another, thus the staggered timeing

// Special lambda abstraction method of processing timer & called commands in compact form
  timer.setTimeout(10000L, []() {   // Oneshot countdown timer that will reset Pot check flag in 10 seconds
    Flag = 0;  // Resets flag
  });

}


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

I am still trying to understand timers myself and am quite proud of this example :stuck_out_tongue: It is however, using a more compact form of the normal timer layout… that usually consists of the timer/timeout command, pointing to another void function, that then contains the action commands. Check here for what I mean (just one of many repeated examples spread around this forum).

Thank you, thank you so much! One last thing i want to ask:

In this sample line timer.setTimeout(5000L, []) what [ and ] does?

Aside from that description, I don’t really know :stuck_out_tongue: But you can study up on it if you have the free time :smiley:

https://www.google.ca/search?q=lambda+c%2B%2B+tutorial&cad=h