Weird timming problem [SOLVED]

EXTRACT:
I'm using two state machines to turn on one time, and then off a time a fan. The On fractions works good but the off timer makes a very weird thing and roll over the int byte to -3456 witch prevent the correct functioning of the machine.

As was mentioned I have a state machine, something like;

CycleFans()
wasOn = true;
fanON - STATE MACHINE

Do you really think we can figure it out from those three lines of code? Post the whole thing. I know you know better.

I don’t really understand what you’re saying, but with a 16-bit signed integer the most significant bit (the leftmost bit) is used for the +/- sign and it will “flip” to negative if you try to “count” over 32,767. Then because singed integers use two’s compliment, if you continue to count, the numbers will start counting down.

There is a bug somewhere in the code you haven't shown us.

My first guess is that you're using a datatype other than unsigned long to store values returned by millis, though...

Im sorry, I was doing a draft, here is the copmplete code;

The problem is in the OFF state machine, The ON works

//=====================================LIBRERIAS==================================================//
#include <Wire.h>
#include <OneWire.h>
#include <dht.h>                     //Libreria para DTH
#include <DallasTemperature.h>       //Libreria Ds1820
#include <LiquidCrystal_I2C.h>       //Libreria IC2 

//=====================================COSNTRUCTORES =============================================//

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

const byte dif1 = 6; 
const byte dif2 = 7; 

byte currentFan  = 0; 
boolean fanWasOn = true; 
boolean pasWasOn = false; //para prueba 
byte fans[]={dif1, dif2}; 

//-----------VAR FOR THE ON STATE MACHINE 
unsigned long delaysONfans[4] = {3000, 6000,   9000, 12000 }; 
unsigned long fanONtime  = 0;   //the time for the function On 
int secondON = 0; 
int pasSeconON = 0; 
int pasSecondON = 0; 

byte fanCase = 2;
boolean cycleON = false; 
boolean timerON = false; 
byte ventON = 1; 

//-------para prueba 
byte pasCurrentFan = 1; 
byte pasTimerON = 0; 

//----var for the off state machine 
boolean timmerOFF = false; 
boolean pasTimerOFF = true;

unsigned long fanOFFTime = 0;   //the time for the function Off 
unsigned long delayOFFFans[4] = {6000, 12000, 18000, 24000 }; 
int ventOFF = 1; 
int pasVentOFF = 0; 
int secondOFF = 0;
int pasSecondOFF =0 ; 



void setup(){
  Serial.begin(9600);
  lcd.begin(20,4);
}


void loop(){
cycleFans(); 
}


void cycleFans(){
//-----roll over the variable to keep starting only two fans 
if(currentFan > 2) currentFan = 0; 

//-----this only allow one state and start each SM  


//prints once the changue of the variable 
if(fanWasOn =! pasWasOn){
  Serial.println("--El ciclo ha cambiado--");
  pasWasOn = fanWasOn ; 
}

//prints once the name of the fans 
if(currentFan != pasCurrentFan){
    if(currentFan == 0) Serial.println( "  VENTILADOR IZQUIERDO  " ); Serial.println("                      ");     
    if(currentFan == 1) Serial.println( "   VENTILADOR  DERECHO   " );Serial.println("                      ");      
    pasCurrentFan = currentFan;
}


// SM for the Ventilation ON 
/*
switch(ventON){  //if the fan was on, we will use the apropiated timmer 
  
  case 1 : // start the variables. 
          if(timerON != pasTimerON)
              {
              Serial.println("Caso 1 ventilacion Encendida");          
              lcd.clear(); 
              lcd.setCursor(0,0);  lcd.print(F(" CASO 1  "));
              pasTimerON = timerON; 
              }     
          fanONtime = millis() - 100  ;     // start the cronometer each time 
          ventON++;                         //jump to the next SM state
          break;
  
  case 2 : //print the lcd to keep data clear 
          Serial.println("Caso 2 ventilacion Encendida");
          Serial.println("------IMPRIMIENDO LCD-------"); 
          
          lcd.clear(); 
          lcd.setCursor(0,0);  lcd.print(F(" UN SOLO VENTILADOR "));
          lcd.setCursor(0,1);  lcd.print(F("   ENCENDIDO POR    ")); 
          lcd.setCursor(0,2);  lcd.print("                      ");
          lcd.setCursor(0,3);  lcd.print(F("    TEMP 10.00 C    "));
          ventON++;//jump to the next SM state
          Serial.print ("VentON = "); Serial.println (ventON);
          break;
  
  case 3 : 
          // start the timmer while the fans is keep OFF
          Serial.println("Caso 3 ventilacion Encendida");
          Serial.print ("millis    = "); Serial.println ( millis() ); 
          Serial.print ("fanONtime = "); Serial.println (fanONtime); 
          Serial.print ("M-FON time= "); Serial.println (millis() - fanONtime); 
          Serial.print ("Delay     = "); Serial.println (delaysONfans[fanCase]); 
          
          Serial.println("-------------------------------------------------");
          
          if( millis() - fanONtime <= delaysONfans[fanCase])      //chek if the time has expired 
              {
                digitalWrite(fans[currentFan], LOW);            //turn on a fan during the current delay on 
                    secondON =  (( delaysONfans[fanCase] - millis() ) / 1000 );
                    
                 if ( secondON - pasSecondON != 0 )  //this will only prints on the lcd one second elapsed 
                    {                 
                    lcd.setCursor(0,2);  lcd.print("                    ");
                    lcd.setCursor(0,2);  lcd.print( secondON );
                    Serial.println (secondON );
                    //Serial.print(pasSecondON); Serial.println(secondON); 
                    pasSecondON = secondON; 
                    }
              }
           else {   ventON++; } // if the timmer is off jump to the next state of the FM  
           break; 
  
  case 4 : //re -start all the variables, inicialized the next state of the FM
         Serial.println("Caso 4 ventilacion Encendida");
         Serial.println("   REINICIANDO VARIABLES "); 
         secondON = 0; 
         pasSecondON = 0; 
         fanWasOn = false;
         ventON = 0;  
         ventOFF = 1; 
         lcd.clear();  
          break;

}//end of function 


*/
// SM for the Ventilation OFF

switch(ventOFF){  //if the fan was on, we will use the apropiated timmer 
  
  case 1 : // start the variables. 
          if(timmerOFF != pasTimerOFF)
              {
              Serial.println("Caso 1 ventilacion Apagada");          
              //lcd.clear(); 
              //lcd.setCursor(0,0);  lcd.print(F(" CASO 1  "));
              pasTimerOFF = timmerOFF; 
              }     
          fanOFFTime = millis() - 10  ;     // start the cronometer each time 
          ventOFF++;                         //jump to the next SM state
          break;
  
  case 2 : //print the lcd to keep data clear 
          Serial.println("Caso 2 ventilacion Apagada  ");
          Serial.println("------IMPRIMIENDO LCD-------"); 
          
          lcd.clear(); 
          lcd.setCursor(0,0);  lcd.print(F("VENTILACION APAGADA "));
          lcd.setCursor(0,1);  lcd.print(F("        POR         ")); 
          lcd.setCursor(0,2);  lcd.print("                      ");
          lcd.setCursor(0,3);  lcd.print(F("    TEMP 10.00 C    "));
          ventOFF++;//jump to the next SM state
          Serial.print ("VentOFF = "); Serial.println (ventOFF);
          break;
  
  case 3 : 
          // start the timmer while the fans is keep OFF
          Serial.println("Caso 3 ventilacion Apagada");
          Serial.print ("millis    = "); Serial.println ( millis() ); 
          Serial.print ("fanOfftime= "); Serial.println (fanOFFTime); 
          Serial.print ("Delay     = "); Serial.println (delayOFFFans[fanCase]); 
          Serial.print ("M-OFF time= "); Serial.println (millis() - fanOFFTime); 
          Serial.println("-------------------------------------------------");

          if( millis() - fanOFFTime < 12000 )     //chek if the time has expired 
          //if( millis() - fanOFFTime < delayOFFFans[fanCase])     //chek if the time has expired 
              {
                digitalWrite(fans[currentFan], HIGH);            //turn OFF a fan during the current delay on 
                    secondOFF = (( delayOFFFans[fanCase] - millis() ) / 1000 );

                // if ( pasSecondOFF >= 0 )              //this will only prints on the lcd one second elapsed 
                 
                 if ( secondOFF - pasSecondOFF != 0 )  //this will only prints on the lcd one second elapsed 
                    {                 
                    lcd.setCursor(0,2);  lcd.print("                    ");
                    lcd.setCursor(0,2);  lcd.print( secondOFF );
                    Serial.print ("pasSecondOFF = "); Serial.println(pasSecondOFF);
                    Serial.print ("SecondOFF    = "); Serial.println(secondOFF); 
                    pasSecondOFF = secondOFF; 
                    }
                
              }
              
           else {   ventOFF++; } // if the timmer is off jump to the next state of the FM  
           break; 
  
  case 4 : //re -start all the variables, inicialized the next state of the FM
          Serial.println("Caso 4 ventilacion Apagada");
          Serial.println("   REINICIANDO VARIABLES "); 
          secondOFF = 0; 
          pasSecondOFF = 0;           
          ventOFF = 0;  
          ventON  = 1; 
          fanWasOn = false;
          lcd.clear(); 
          break;

}//end of function 


}

I make a small changue in here; if( millis() - fanOFFTime < 12000 ) //chek if the time has expired

To see if was working, And does,
Sorry the code has a lots of serial prints for debuggin I dont erase them in case they come handy.

I think the problem occurs In the above statment because in the serial output I can se something like;

pas second = 0
second = -3452 //or something

weird sutuff is both SM are simetrical, the Off one is a copy of the first, and the first works just fine,
each by their side acttually works just fine, at the moment when I put the two together is when this start to happen.

The idea is;
turn Fan 1 -ON - 10Sec then Turn Fan 1 -OFF - 20 Sec
turn Fan 2 -ON - 10Sec then Turn Fan 2 -OFF - 20 Sec

rollover Fan

turn Fan 1 -ON - 10Sec then Turn Fan 1 -OFF - 20 Sec
turn Fan 2 -ON - 10Sec then Turn Fan 2 -OFF - 20 Sec

Keep it doing while the subroutine is called.

So is maybe a roll over value or something.

In any case, If some one has a better way to struct the code I will greatly apreciatted.

I do this way because the old nested if was to much problem to know whats hapenning.

Thanks in advance.
-Alex.

Well this is even more odd than I tougth at first, know Im sure the problem lies on the if statments, however, they don seem doing their job;

I modify some pieces of code, like this;

 // start the timmer while the fans is keep OFF
          Serial.println("Caso 3 ventilacion Apagada");
          Serial.print ("millis    = "); Serial.println ( millis() ); 
          Serial.print ("fanOfftime= "); Serial.println (fanOFFTime); 
          Serial.print ("M-OFF time= "); Serial.println (millis() - fanOFFTime); 
          Serial.print ("Delay     = "); Serial.println (delayOFFFans[fanCase]); 
          

          unsigned long TimeOffElapsed ;                                      // < Ad this variable watch the SO
                        TimeOffElapsed = millis() - fanOFFTime; 
          Serial.print ("T elapsed = "); Serial.println (TimeOffElapsed); 
          Serial.println("-------------------------------------------------");
          if( TimeOffElapsed < delayOFFFans[fanCase])     //chek if the time has expired 
              {
                digitalWrite(fans[currentFan], HIGH);            //turn OFF a fan during the current delay on 
                    secondOFF = (( delayOFFFans[fanCase] - millis() ) / 1000 );
                int delayOFF  = (( delayOFFFans[fanCase] ) / 1000 );
                
                // if ( pasSecondOFF >= 0 )              //this will only prints on the lcd one second elapsed 
                 
                 Serial.print ("---->pasSecondOFF = "); Serial.println (pasSecondOFF); 
                 Serial.print ("---->secondOFF    = "); Serial.println (secondOFF); 
                 
                 if ( secondOFF - pasSecondOFF <= delayOFF && secondOFF != 0  )
                    {                 
                    lcd.setCursor(0,2);  lcd.print("                    ");
                    lcd.setCursor(0,2);  lcd.print( secondOFF );
                    pasSecondOFF = secondOFF; 
                    Serial.print ("---->pasSecondOFF = "); Serial.println (pasSecondOFF); 
                    Serial.print ("---->secondOFF    = "); Serial.println (secondOFF); 
                    }
              
              }
          
           else {  
                 Serial.println("-------------------------------------------------");
                 Serial.println("---------------ELSE HAS START--------------------");
                 Serial.println("-------------------------------------------------");
                 ventOFF++; } // if the timmer is off jump to the next state of the FM  
           break;

The last serial output;

---->pasSecondOFF = 1
---->secondOFF    = -30409
---->pasSecondOFF = -30409
---->secondOFF    = -30409
Caso 3 ventilacion Apagada
millis    = 18254
fanOfftime= 172
M-OFF time= 18120
Delay     = 18000
T elapsed = 18147

So… the "if( TimeOffElapsed < delayOFFFans[fanCase]) " has been skiped?

Clearly we are watching something like so;

if( 18147 < 1800) { timmer } //this is not true !!!
else { break finite state machine } //why don’t skip it?

Why?
Why???
Why??? ???

The whole code;

I remove the ON state machine because this is the one it fails!!! Weird. I will leave the file in here to you can see complete, thanks.

//=====================================LIBRERIAS==================================================//
#include <Wire.h>
#include <OneWire.h>
#include <dht.h>                     //Libreria para DTH
#include <DallasTemperature.h>       //Libreria Ds1820
#include <LiquidCrystal_I2C.h>       //Libreria IC2 

//=====================================COSNTRUCTORES =============================================//

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

const byte dif1 = 6; 
const byte dif2 = 7; 

byte currentFan  = 0; 
boolean fanWasOn = true; 
boolean pasWasOn = false; //para prueba 
byte fans[]={dif1, dif2}; 

//-----------VAR FOR THE ON STATE MACHINE 
unsigned long delaysONfans[4] = {3000, 6000,   9000, 12000 }; 
unsigned long fanONtime  = 0;   //the time for the function On 
int secondON = 0; 
int pasSeconON = 0; 
int pasSecondON = 0; 

byte fanCase = 2;
boolean cycleON = false; 
boolean timerON = false; 
byte ventON = 1; 

//-------para prueba 
byte pasCurrentFan = 1; 
byte pasTimerON = 0; 

//----var for the off state machine 
boolean timmerOFF = false; 
boolean pasTimerOFF = true;

unsigned long fanOFFTime = 0;   //the time for the function Off 
unsigned long delayOFFFans[4] = {6000, 12000, 18000, 24000 }; 
int ventOFF = 1; 
int pasVentOFF = 0; 
int secondOFF = 0;
int pasSecondOFF = 1 ; 



void setup(){
  Serial.begin(9600);
  lcd.begin(20,4);
}


void loop(){
cycleFans(); 
}


void cycleFans(){
//-----roll over the variable to keep starting only two fans 
if(currentFan > 2) currentFan = 0; 

//-----this only allow one state and start each SM  


//prints once the changue of the variable 
if(fanWasOn =! pasWasOn){
  Serial.println("--El ciclo ha cambiado--");
  pasWasOn = fanWasOn ; 
}

//prints once the name of the fans 
if(currentFan != pasCurrentFan){
    if(currentFan == 0) Serial.println( "  VENTILADOR IZQUIERDO  " ); Serial.println("                      ");     
    if(currentFan == 1) Serial.println( "   VENTILADOR  DERECHO   " );Serial.println("                      ");      
    pasCurrentFan = currentFan;
}



*/
// SM for the Ventilation OFF

switch(ventOFF){  //if the fan was on, we will use the apropiated timmer 
  
  case 1 : // start the variables. 
          if(timmerOFF != pasTimerOFF)
              {
              Serial.println("Caso 1 ventilacion Apagada");          
              //lcd.clear(); 
              //lcd.setCursor(0,0);  lcd.print(F(" CASO 1  "));
              pasTimerOFF = timmerOFF; 
              }     
          fanOFFTime = millis() - 10  ;     // start the cronometer each time 
          ventOFF++;                         //jump to the next SM state
          break;
  
  case 2 : //print the lcd to keep data clear 
          Serial.println("Caso 2 ventilacion Apagada  ");
          Serial.println("------IMPRIMIENDO LCD-------"); 
          
          lcd.clear(); 
          lcd.setCursor(0,0);  lcd.print(F("VENTILACION APAGADA "));
          lcd.setCursor(0,1);  lcd.print(F("        POR         ")); 
          lcd.setCursor(0,2);  lcd.print("                      ");
          lcd.setCursor(0,3);  lcd.print(F("    TEMP 10.00 C    "));
          ventOFF++;//jump to the next SM state
          Serial.print ("VentOFF = "); Serial.println (ventOFF);
          break;
  
  case 3 : 
          // start the timmer while the fans is keep OFF
          Serial.println("Caso 3 ventilacion Apagada");
          Serial.print ("millis    = "); Serial.println ( millis() ); 
          Serial.print ("fanOfftime= "); Serial.println (fanOFFTime); 
          Serial.print ("M-OFF time= "); Serial.println (millis() - fanOFFTime); 
          Serial.print ("Delay     = "); Serial.println (delayOFFFans[fanCase]); 
          

          unsigned long TimeOffElapsed ; 
                        TimeOffElapsed = millis() - fanOFFTime; 
          Serial.print ("T elapsed = "); Serial.println (TimeOffElapsed); 
          Serial.println("-------------------------------------------------");
          if( TimeOffElapsed < delayOFFFans[fanCase])     //chek if the time has expired 
              {
                digitalWrite(fans[currentFan], HIGH);            //turn OFF a fan during the current delay on 
                    secondOFF = (( delayOFFFans[fanCase] - millis() ) / 1000 );
                int delayOFF  = (( delayOFFFans[fanCase] ) / 1000 );
                
                // if ( pasSecondOFF >= 0 )              //this will only prints on the lcd one second elapsed 
                 
                 Serial.print ("---->pasSecondOFF = "); Serial.println (pasSecondOFF); 
                 Serial.print ("---->secondOFF    = "); Serial.println (secondOFF); 
                 
                 if ( secondOFF - pasSecondOFF <= delayOFF && secondOFF != 0  )
                    {                 
                    lcd.setCursor(0,2);  lcd.print("                    ");
                    lcd.setCursor(0,2);  lcd.print( secondOFF );
                    pasSecondOFF = secondOFF; 
                    Serial.print ("---->pasSecondOFF = "); Serial.println (pasSecondOFF); 
                    Serial.print ("---->secondOFF    = "); Serial.println (secondOFF); 
                    }
              
              }
          
           else {  
                 Serial.println("-------------------------------------------------");
                 Serial.println("---------------ELSE HAS START--------------------");
                 Serial.println("-------------------------------------------------");
                 ventOFF++; } // if the timmer is off jump to the next state of the FM  
           break; 
  
  case 4 : //re -start all the variables, inicialized the next state of the FM
          Serial.println("Caso 4 ventilacion Apagada");
          Serial.println("   REINICIANDO VARIABLES "); 
          secondOFF = 0; 
          pasSecondOFF = 1;           
          ventOFF = 0;  
          ventON  = 1; 
          fanWasOn = false;
          lcd.clear(); 
          break;

}//end of function 


}

ejemplo_FM_para_ventiladores.ino (8.86 KB)

If you're trying to find if the ON time has ended, the usual way is:

if (millis() - startTime >= onTime{.......}

Any other way may cause rollover problems.

Well I solved, the problem was not in to the first I statment was in the nested
“if ( secondOFF - pasSecondOFF <= delayOFF && secondOFF != 0 )”

I add some local variables to keep more control the code. In case anyone ever comes to need an example, wil leave the code in here.

Thanks a lot
-Alex.

ejemplo_FM_para_ventiladores.ino (9.92 KB)