How to continue to read a variable if I am in a loop to blink an LED

Hi everyone

I am developing a project that runs a BLDC motor, using an ESC that takes in input a PWM signal, and, based on thresholds in temperature and pressure, can go in stand- by mode or in overheating mode.
Other loads are connected to the board (SV1,SV2 and a fan)

the project has a momentary push button embedding a LED and a resistor for the LED.

IF the threshold of temperature is reached, the system goes in overheating mode. the motor stops, the fan is ON and LED start blinking.

the same happens if the pressure threshold is reached, so the system goes in stand-by mode, and the led blinks at a different frequency.

if I am in run mode, so P and T are below thresholds, the motor runs, and if the push button is pressed again, everything turn off. If I play with the code and sensors, using hot water to simulate the increasing of T and P, the states are reached correctly, the fan and the other loads are enabled correctly and when I am in run mode again, I can switch off the unit.

I have got problems in switching off the system when it is in overheating mode or stand-by mode. The variable “counter” I set, seems to be bypassed, so if I press the push button, while in stand-by or overheating mode, nothing happens.

I fear there is a loop where I am stuck in, but I don’t find which one…

If anybody could help…I attach here the code. Thank you very much.

#include <Servo.h>
Servo ESC; 


// macro for detection of rising edge and debouncing
/*the state argument (which must be a variable) records the current
  and the last 7 reads by shifting one bit to the left at each read.
  If the value is 15(=0b00001111) we have one rising edge followed by
  4 consecutive 1's. That would qualify as a debounced rising edge*/
#define DRE(signal, state) (state=(state<<1)|signal)==B00001111

// Rising state variables for each button
byte button1RisingState;

// macro for detection of falling edge and debouncing
/*the state argument (which must be a variable) records the current
  and the last 7 reads by shifting one bit to the left at each read.
  If the value is 240(=0b11110000) we have one falling edge followed by
  4 consecutive 0's. That would qualify as a debounced falling edge*/
#define DFE(signal, state) (state=(state<<1)|signal)==B11110000

// Falling state variables for each button
byte button1FallingState;

static unsigned long Delay_sv1 = 3000;
static unsigned long Delay_esc = 1000;

int counter = 0; /* counts if the push button has been clicked and even or odd number of times*/
int flag = 0; /*defines if the temperature already went above T_max*/
int flag2 = 0; /*defines if the pressure rose above P_max*/

int pres; /* Pressure */
int temp; /* Temperature */
int T_deg;
int P_bar;
 
int P_max = 14;
int P_min = 11; 
int P_air_on = 10; 
int P_air_off = 9; 

int T_max = 65;
int T_reset = 40;
int T_fan_on = 55;
int T_fan_off = 40;

#define sv1 4/* Oil electrovalve in relay 1 (PIN4)*/
#define sv2 5/* Air electrovalve in relay 2 (PIN5)*/
#define fan 7 /* Fan of heat exchanger in relay 4 (PIN7)*/
#define button 0/* momentary push button NO contact wired in IO1 [PIN0] with internal pull-up resistor in the micro ATmega enabled, and external 4K7 ohm pull-up resistor enabled. Contact "C" of the push button is wired in GND */
#define led 9/* LED+ in IO3 [PIN9] with embedded resistor in series to the anode, and LED- in GND. */ 

void setup() {
//  Serial.begin(9600);
  pinMode(button, INPUT); /* On the board place the jumper to enable the 4K7 ohm pull-up resistor at this input.*/
  pinMode(led, OUTPUT); /* remove the jumper in correspondence of IO3, not required pull-up resistor for outputs */
  pinMode(sv1, OUTPUT);
  pinMode(sv2, OUTPUT);
  pinMode(fan, OUTPUT);
  ESC.attach(13);
}

boolean readButtons() {

  // Read button states every 5 ms (debounce time):
  static unsigned long lastDebounce;
  if (millis() - lastDebounce >= 5) {
    lastDebounce = millis();

    // Rising edge (if switch is released)
    if (DRE(digitalRead(button), button1RisingState)) {
      int x = 1;
    }

    // Falling edge (if switch is pressed)
    if (DFE(digitalRead(button), button1FallingState)) {
      digitalWrite(led, !digitalRead(led)); // switch LED on or off
      counter = counter + 1;
      int x = 0;
    }
  }
}

void Inputs(){
  
  /*Check the pressure. Pressure in AIN1 [A0]*/
  pres = analogRead(A0); 
  P_bar = 0.030*(pres+0.75)-6.25; /*bar g*/

  /* Check the temperature:  Temperature in AIN2 [A1].*/
  temp = analogRead(A1);
  T_deg = 0.244*(temp+0.75)-100; /*deg °C*/

}

boolean FanOn(){
   
  if(T_deg >= T_fan_on){
    digitalWrite(fan, HIGH);
  }
  
  if(T_deg <= T_fan_off){
    digitalWrite(fan, LOW);
  }
   
}

boolean Flag(){
  
  if(T_deg >= T_max && flag == 0){
    flag = 1;
  }

  if(T_deg < T_reset && flag == 1){
    flag = 0;
  }

}

boolean Flag2(){
  
  if(P_bar >= P_max && flag2 == 0){
    flag2 = 1;
  }

  if(P_bar < P_min && flag2 == 1){
    flag2 = 0;
  }
  
}

boolean LED(){

  /* LED blinks each 600ms when in STAND-BY mode, due to pressure reaching the threshold P_max (temperature below T_max)*/
  if(P_bar >= P_min && flag2 == 1 && T_deg < T_max && flag == 0){
    digitalWrite(led, HIGH);
    delay(600);
    digitalWrite(led, LOW);
    delay(600);
  }
  /*LED blinks each 300ms when in OVERHEATING mode, due to temperature reaching the threshold */
  if(T_deg >= T_reset && flag == 1){
    digitalWrite(led, HIGH);
    delay(300);
    digitalWrite(led, LOW);
    delay(300);
  }
  if(P_bar < P_max && flag2 == 0 && T_deg < T_max && flag == 0){
    digitalWrite(led, HIGH);
  }
 
}

boolean SV2On(){
  
  /*if pressure >= 10bar, and the unit is NOT in the OVERHEATING state, sv2 opens*/ 
  if(P_bar >= P_air_on && T_deg < T_max && flag == 0){
    digitalWrite(sv2, HIGH);
  }  
      
  /* if pressure <= 9bar, or the unit is in the OVERHEATING state, sv2 closes*/
  if(P_bar <= P_air_off || T_deg >= T_max || flag == 1){
    digitalWrite(sv2, LOW);        
  }
  
}

boolean SV1On(){
  
  /*if the unit is in OVERHEATING MODE or in STAND-BY MODE, the sv1 closes*/
  if(T_deg >= T_max || flag == 1 || P_bar >= P_max || flag2 == 1){
    digitalWrite(sv1, LOW);
  }
  
  /*if the unit is with temperature below the T_max, and is not in OVERHEATING mode, and the Pressure is below the P_max threshold, and the unit is not in STAND-BY mode, sv1 is open*/
  if(T_deg < T_max && flag == 0 && P_bar < P_max && flag2 == 0){
    if (millis() >= Delay_sv1){
      digitalWrite(sv1, HIGH);
    }
  }
  
}

boolean Motor_Controller_On(){
  /*if the unit is with temperature below the T_max, and is not in OVERHEATING mode, and the Pressure is below the P_max threshold, and the unit is not in STAND-BY mode, the ESC spins the motor clockwise (2ms period square wave signal)*/
  if(T_deg < T_max && flag == 0 && flag2 == 0 && P_bar < P_max){
    if (millis() >= Delay_esc){
      ESC.writeMicroseconds(2000);
    }

    
  }
  /* if the unit is with temperature over threshold, or pressure over threshold, or the unit is in STAND-BY mode or in OVERHEATING mode, the ESC stops (1ms period square wave signal)*/
  if(T_deg >= T_max || flag == 1 || flag2 == 1 || P_bar >= P_max){
    if (millis() >= Delay_esc){
      ESC.writeMicroseconds(1000);
    }
  }
  
}

boolean ShutDown(){
  if(counter%2 == 0){
        
    digitalWrite(fan, LOW);
    digitalWrite(sv2, LOW);
    digitalWrite(sv1, LOW);
    
    if (millis() >= Delay_esc){
      ESC.writeMicroseconds(1000);
    }
     
  }
}

void loop(){
  
  /*Read Buttons*/
  readButtons();
  Serial.print("counter = ");
  Serial.println(counter);
  /*if the counter is odd, then the button went from OFF to ON*/
  if(counter%2 == 1){

    Inputs();
    Serial.print ("temp = ");
    Serial.println (T_deg);
    Serial.print ("pres = ");
    Serial.println (P_bar);

    FanOn();
    
    Flag();
//    Serial.print("flag = ");
//    Serial.println(flag);
    
    Flag2();
//    Serial.print("flag2 = ");
//    Serial.println(flag2);
    
    LED();
    SV2On();
    SV1On();
    Motor_Controller_On();
    
  }
  else{
    ShutDown();
  }
}

I have not looked at your code in detail but one thing that I notice is that once initialised to 1000 the value of Delay_esc is never changed. As a result all of the tests of the form

if (millis() >= Delay_esc)

will always return true after 1 second. Is that what you intend ?

What do you see if you print the value of counter inside the Shutdown() function ? In fact, is the Shutdown function ever called ?

Hi there, thank you for replying.

I have not looked at your code in detail but one thing that I notice is that once initialised to 1000 the value of Delay_esc is never changed.

the delay of 1s is constant. I don't need to change this value. the ESC stops the motor if conditions are reached, after 1s, always.

If conditions are reached, it is ok that the motor stops after this delay.

What do you see if you print the value of counter inside the Shutdown() function ? In fact, is the Shutdown function ever called ?

the ShutDown funct is called when I am in Run mode, and i press the button.
It should be called even when I am in stand-by mode or overheating mode...but actually it doesn't.

this is my problem. So if I am in this 2 states, apparently it is not called.

any suggestion?

the delay of 1s is constant.

The point is that after the code has been running for more than 1 second the value of millis() will be greater than 1000, Delay_esc will have a value of 1000 so the 3 tests will always return true so there is no delay. Is that what you want ?

If I understand the DRE and DFE macros correctly they require the button input to be in the same state for 7 consecutive reads to be regarded as valid, but when in standby mode the LED() function effectively does nothing for 1.2 seconds each time it is called. So in order for the input to be recognised as valid the button must be held down for 8.4 seconds

The point is that after the code has been running for more than 1 second the value of millis() will be greater than 1000, Delay_esc will have a value of 1000 so the 3 tests will always return true so there is no delay. Is that what you want ?

Actually, now I understand better your reply, and no, I would like the motor switching off 1s after the conditions in the if loop are verified :astonished: I need that delay... and testing i didn't notice there was no delay at all! with the comand

delay(1000);

the code was not working, so I decided to use millis() to count the time..
But actually you pointed out a problem that with my tests I didn't notice!!
as delay is not working properly and millis() , as you said, is not a solution either...what function would you suggest?

If I understand the DRE and DFE macros correctly they require the button input to be in the same state for 7 consecutive reads to be regarded as valid, but when in standby mode the LED() function effectively does nothing for 1.2 seconds each time it is called.  So in order for the input to be recognised as valid the button must be held down for 8.4 seconds

ok this is a problem!!! :astonished: :astonished: :astonished: :astonished: I need to be able to switch off the system every time I press the push button, in any condition.
How can I bypass this issue?

Thank you very much to point out these bugs, I couldn't see them/ find them

and millis() , as you said, is not a solution either...

Actually I said no such thing. The way you are using millis() is the problem.

You can blink LEDs using millis() for timing without using delay() thus preventing the reading of inputs being blocked.

Have a look at Using millis() for timing. A beginners guide, Several things at the same time and look at the BlinkWithoutDelay example in the IDE.

Thank you very much.

Hopefully I will solve these bugs.

I hope to find you here again in case I am stuck :smiley: please don't leave me ahahah

thank you again!

Hello

I fixed the problems about the LED, and the Counter variable…now as I fixed that bug, I have a new one:

I would need to close the relay 1 [pin4] with a delay of 5s: the relevant function is SV1On();

the relevant is the following (the rest has been modified accordingly to what written in previous replies, and it works…it is too long to be posted):

static unsigned long Delay_sv1 = 5000;


unsigned long previousMillis_sv1 = 0;
int sv1State = LOW;



boolean SV1On() {

  unsigned long currentMillis_2 = millis();

  /*if the unit is with temperature below the T_max, and is not in OVERHEATING mode,
  and the Pressure is below the P_max threshold, and the unit is not in STAND-BY mode,
  sv1 is opens after 2s of delay */

  if (T_deg < T_max && flag == 0 && P_bar < P_max && flag2 == 0) {
    if (currentMillis_2 - previousMillis_sv1 >= Delay_sv1) {
      previousMillis_sv1 = currentMillis_2;
      if (sv1State == LOW) {
        sv1State = HIGH;
      } 
      
      digitalWrite(sv1, sv1State);
    }
  }

  /*if the unit is in OVERHEATING MODE or in STAND-BY MODE, the sv1 closes immediately*/
  if (T_deg >= T_max || flag == 1 || P_bar >= P_max || flag2 == 1) {
    digitalWrite(sv1, LOW);
  }
}

when I click the push button I would need to see the LED simulating the load SV1, lighting up after 5s from when I pressed the button.

I tried several solutions without success.

It is the last thing I should fix…hopefully
thank you if anybody replies

I tried several solutions without success.

Would you care to share at least one of them ?

It should be relatively simple. Save the current value of millis() when the button becomes pressed. Each time round loop() check whether 5 seconds has elapsed since the start time. If so then turn on the LED. A boolean variable will come in handy to control whether the elapsed time test and LED action takes place or not. Set the boolean to true when the button becomes pressed and only do the timing/LED on when the boolean is true.

Sorry for that.
I thought it would have been only useless rubbish, as it was a rearrangement of variables such as currentMillis and lastMillis, checked in an if loop . I did not save the code for these quick tests.

:frowning:

I try to follow what you suggest. thank you.

Hello again :slightly_smiling_face: :slightly_smiling_face: :slightly_smiling_face: :slightly_smiling_face:

the code for the delay at the start, when I push the button , works:

void loop() {

  /*Read Buttons*/
  readButtons();
  Serial.print("counter = ");
  Serial.println(counter);
  Serial.print("check = ");
  Serial.println(check);
  
  /*if the counter is odd, then the button went from OFF to ON*/
  if (counter % 2 == 1) {

    Inputs();
    Serial.print ("temp = ");
    Serial.println (T_deg);
    Serial.print ("pres = ");
    Serial.println (P_bar);

    FanOn();

    Flag();
    Serial.print("flag = ");
    Serial.println(flag);

    Flag2();
    Serial.print("flag2 = ");
    Serial.println(flag2);

    LED();
    SV2On();
    Motor_Controller_On();
    
    if(check == 1){
        if(millis() - ButtonPressed >= Delay_sv1){
          SV1On();
        }
    } 
    
  }
  else {
    ShutDown();
  }
}

the function SV1On(); is

boolean SV1On(){
  
  /*if the unit is with temperature below the T_max, and is not in OVERHEATING mode, and the Pressure is below the P_max threshold, and the unit is not in STAND-BY mode, the sv1 opens after delay = Delay_sv1 */
  if (T_deg < T_max && flag == 0 && flag2 == 0 && P_bar < P_max){  
    unsigned long currentMillis_2 = millis(); 
    if(currentMillis_2 - previousMillis_sv1 >= Delay_sv1){   
      digitalWrite(sv1, HIGH);
    }
    previousMillis_sv1 = currentMillis_2;
  }
  /* if the system is in STAND-BY MODE or OVERHEATING MODE, the SV1 closes immediately */
  if (T_deg >= T_reset && flag == 1 || flag2 == 1 && P_bar >= P_min){
      digitalWrite(sv1, LOW);
  }
  
}

having initialised:

static unsigned long Delay_sv1 = 3000;

unsigned long previousMillis_sv1 = 0;

and having this function for the push button

boolean readButtons(){

  /* Read button states every 5 ms (debounce time)*/
  static unsigned long lastDebounce;
  if (millis() - lastDebounce >= 5){
    lastDebounce = millis();
    /* Rising edge (if switch is released)*/
    if (DRE(digitalRead(button), button1RisingState)){
      Serial.print("button is pressed");
    }

    /* Falling edge (if switch is pressed)*/
    if (DFE(digitalRead(button), button1FallingState)){
      digitalWrite(led, !digitalRead(led)); /*switch LED on or off*/
      counter = counter + 1;
      ButtonPressed = millis();
      check = 1;
    }
  }
  
}

I noticed that the delay for the load SV1 is only at the start, while I would like to keep this delay every time that it has to pass from state OFF to state ON. (see the if conditions in SV1On();).

I don’t understand why the fuction doesn’t delay the change of state from LOW to HIGH a I would like.

I tried also the code:

boolean SV1On(){

  unsigned long currentMillis_2 = millis(); 
  
  /*if the unit is with temperature below the T_max, and is not in OVERHEATING mode, and the Pressure is below the P_max threshold, and the unit is not in STAND-BY mode, the sv1 opens after delay = Delay_sv1 */
  if (T_deg < T_max && flag == 0 && flag2 == 0 && P_bar < P_max){  
    
    if(currentMillis_2 - previousMillis_sv1 >= Delay_sv1){   
      digitalWrite(sv1, HIGH);
    }
    previousMillis_sv1 = currentMillis_2;
  }
  /* if the system is in STAND-BY MODE or OVERHEATING MODE, the SV1 closes immediately */
  if (T_deg >= T_reset && flag == 1 || flag2 == 1 && P_bar >= P_min){
      digitalWrite(sv1, LOW);
  }
  
}

but it doesn’t do any delay when turning sv1 from LOW to HIGH.

what am I missing?

thank you n advance for any help!

Solved!

final version of the function:

boolean SV1On(){

  unsigned long currentMillis_2 = millis();
  /*if the unit is with temperature below the T_max, and is not in OVERHEATING mode, and the Pressure is below the P_max threshold, and the unit is not in STAND-BY mode, the sv1 opens after delay = Delay_sv1 */
  if (T_deg < T_max && flag == 0 && flag2 == 0 && P_bar < P_max){
    if(currentMillis_2 - previousMillis_sv1 >= Delay_sv1){
      digitalWrite(sv1, HIGH);
      previousMillis_sv1 = currentMillis_2;
    }
  }
  
  /* if the system is in STAND-BY MODE or OVERHEATING MODE, the SV1 closes immediately */
  if (T_deg >= T_reset && flag == 1 || flag2 == 1 && P_bar >= P_min){
    digitalWrite(sv1, LOW);
    previousMillis_sv1 = currentMillis_2;
  }
  
}

:slight_smile: