Avoid system stall while a physical process happens over time

Hi there

I've made the code as shown. I display a preset temperature and a preset humidity. Those can be changed via 4 buttons +/- 0.1. This works and so does reading the current values. Everything's fine here.

Now I want to use a couple of relays to control a heater and a humidifier to adjust the invironment until the values meet the presets. This will not happen instantly and if I make the code wait for the right values (could be more than 10 minutes) the system will stall and leave me unable to e.g. adjust preset meanwhile.

My question:
How will I make the heating and humidifying processes run and still be able to adjust presets at the same time?

Kind regards,
Chris

#include <LCD5110_Basic.h>
#include <Button.h>    
#include <Adafruit_Sensor.h>
#include <DHT.h>
    
#define knap1 A1     
#define knap2 A2  
#define knap3 A3  
#define knap4 A4  
#define DHTPIN 3

int relae1 = 4;
int relae2 = 5;

#define DHTTYPE DHT11  
DHT dht(DHTPIN, DHTTYPE);

#define PULLUP true        
#define INVERT true        
#define DEBOUNCE_MS 20  

LCD5110 myGLCD(8,9,10,11,12);
extern uint8_t SmallFont[];
extern uint8_t MediumNumbers[];
extern uint8_t BigNumbers[];
  
float setTemp=37.0;
float setFugt=55.0;

Button KnapTempPlus(knap1, PULLUP, INVERT, DEBOUNCE_MS);    
Button KnapTempMinus(knap2, PULLUP, INVERT, DEBOUNCE_MS);    
Button KnapFugtPlus(knap3, PULLUP, INVERT, DEBOUNCE_MS);    
Button KnapFugtMinus(knap4, PULLUP, INVERT, DEBOUNCE_MS);    

void setup()
{
myGLCD.InitLCD();
dht.begin();
Serial.begin (9600);
pinMode(13, OUTPUT); // sluk onboard LED 
digitalWrite(13,LOW); // sluk onboard LED  
pinMode(relae1,OUTPUT);
pinMode(relae2,OUTPUT);
myGLCD.setFont(SmallFont);
myGLCD.print("Temp:", 0, 0);
myGLCD.print("~", 24, 12);
myGLCD.print("Fugt:", 0, 34);
myGLCD.print("%", 24, 42);
myGLCD.setFont(MediumNumbers);
myGLCD.print("-------", 0, 22);

}

void loop()
{
tjekKnapper();
float aktuelFugt = dht.readHumidity();
float t = dht.readTemperature(true);
float aktuelTemp = (float((((t - 32) * 5) / 9)));
myGLCD.setFont(SmallFont);
myGLCD.printNumF(float(setTemp), 1, 0, 8);
myGLCD.printNumF(float(setFugt), 1, 0, 42);
myGLCD.setFont(MediumNumbers);
myGLCD.printNumF(float(aktuelTemp), 1, 36, 0);
myGLCD.printNumF(float(aktuelFugt), 1, 36, 34);
}

void tjekKnapper()
{
tjekKnapTempPlus();
tjekKnapTempMinus();
tjekKnapFugtPlus();
tjekKnapFugtMinus();
}


void tjekKnapTempPlus()
{
KnapTempPlus.read();                   
if (KnapTempPlus.wasPressed()) {     
setTemp +=0.1;
  } 
}

void tjekKnapTempMinus()
{
KnapTempMinus.read();                   
if (KnapTempMinus.wasPressed()) {     
setTemp -=0.1;
  } 
}

void tjekKnapFugtPlus()
{
KnapFugtPlus.read();                   
if (KnapFugtPlus.wasPressed()) {     
setFugt +=0.1; 
   }
}

void tjekKnapFugtMinus()
{
KnapFugtMinus.read();                   
if (KnapFugtMinus.wasPressed()) {     
setFugt -=0.1;
  } 
}

chrven:
This will not happen instantly and if I make the code wait for the right values (could be more than 10 minutes) the system will stall and leave me unable to e.g. adjust preset meanwhile.

Then don't use blocking code, like delay() and while().
Use millis() to manage your timing.

Study the "BlinkWithoutDelay" example that comes with the IDE.
Leo..

Hi Leo. Thanks for the reply.

Wawa:
Then don't use blocking code, like delay() and while().
Use millis() to manage your timing.

I thought about millis, but the heating and humidifying durations will vary. There's probably a workaround, but my sense of logic is incapable of cracking it.... :o

I thought about millis, but the heating and humidifying durations will vary

That's like saying tomorrow is going to be shorter than usual because my clock is running fast.

Forget about your project for now, and study that 'BlinkWithoutDelay" example with LEDs.
The concept is really easy, but it has to click into place.

Think of what you do when you have to go to work.
You don't just sit there and stare at the clock.
You have set a time in your mind that you have to go, and check on a regular base if that point in time has passed (while you're doing something else).

There is a clock inside the micro that runs constantly.
All you have to do in loop() is to check if an previously marked/stored time plus a set interval has passed.

That interval can ofcourse be changed (button/pot/etc.) in code.
Leo..

Wawa:
Then don't use blocking code, like delay() and while().
Use millis() to manage your timing.

Study the "BlinkWithoutDelay" example that comes with the IDE.
Leo..

What code are you looking at?

I see no delay or while.. :o
Sorry the OP is thinking of using them...

Tom... :slight_smile:

The demo Several Things at a Time is an extended example of BWoD and illustrates the use of millis() to manage timing without blocking. It may help with understanding the technique.

...R

You just check for the humidity and temperature values (once a second or so is probably more than enough), and check for the buttons (you can do that as often as possible). That way you can simply react to all the inputs as they happen.

Time it takes to heat/humidify of course changes. You'll also have to switch off the two before you reach the setpoint as your system will continue to change for a while. Heating will also affect humidity: higher temperature makes relative humidity drop. So this is a rather complex control system if you want any precision out of it.

What happens if you add something simple like below?

void loop()
{
  tjekKnapper();
  float aktuelFugt = dht.readHumidity();
  float t = dht.readTemperature(true);
  float aktuelTemp = (float((((t - 32) * 5) / 9)));
  myGLCD.setFont(SmallFont);
  myGLCD.printNumF(float(setTemp), 1, 0, 8);
  myGLCD.printNumF(float(setFugt), 1, 0, 42);
  myGLCD.setFont(MediumNumbers);
  myGLCD.printNumF(float(aktuelTemp), 1, 36, 0);
  myGLCD.printNumF(float(aktuelFugt), 1, 36, 34);

  // ADDED
  if(aktuelTemp < setTemp)
  {
    // switch a relay on
  }
  else
  {
    // switch a relay off
  }
}

You might want to add a bit of hysteresis if the temperature fluctuates quickly.

Using millis() for delays, requires a slight shift in thinking...
It’s been mentioned many times, but here goes.
Broad generalisation follows

Millis() is not magic, it’s like the second hand on a clock (millis hand?!)
Nothing you do will stop the hand and time moving forward other than pulling the power out...

So keep this in mind... as long as your sketch is running, and not stuck in some ‘blocking’ loop, the hands of the clock will keep ticking along.

  • millis is also still counting, but you can’t see it, because your code is too busy in delay() or other code that is sucking up (blocking) every cpu cycle until it ends...

Note we haven’t mentioned anything about what your program does yet - it’s irrelevant! We need to understand the clock is always ticking away in the background.

Ok, so now you want to change state of a LED. in 3.6 seconds from now...
Sure, you could use delay(3600), but nothing else can happen for the next 3.6 seconds...
So, why not take a look at the wall clock, and take a note of the time...
...now.

Carry on doing whatever else tickles your digital fancy (remember the clock is still ticking on the wall), and occasionally take a glance back at the millis() clock when you’re ‘free’ (typically each time around loop).
If the time...
...now! is 3600 mS or greater* than it was when we started looking - that means the interval has elapsed, and we can perform that scheduled LED state change.
If you’re doing other things, you can keep track of the time ‘again’, or even keep track of multiple times and durations. There is only one wall clock.

It’s not rocket science, but does require a bit of understanding, and logical thinking to keep your ducks in a row.