satate machine self reset?

Hi to all... In the pasts days I was working on a proyect that need some state machines. In the theory all is good, but bad thing happens if the machines are interrupted by an external factor (In my case the temperature sensor get a wrong reading)

So, my basic structure is something like this;

void loop()
{
rawTemp = get.temperature
temperature();
termostat();
}

void temperature ()
{

    if (sensor == badReading )  temperature = unknow;
       else if(sensor == goodReading) 
        {
         if ( temC > highsetPoint) temperature = HIGH
         if ( temC < lowsetPoint) temperature = LOW
         }

void termostat()
{

    if(temperature == unknow)       stopAll();
      else if( temperature == HIGH) refrigerationON();
      else if(temperature == LOW)   refrigerationOFF();
}

refrigerationON ///the fun begins yeiiii
{

        if( refrigeration == true ) // bool to make the cycleof the machine just once 
    
             {
                switch(c) 
                   case 1: break; //the system
                   case 2: break; // is on 
                   case 3: refrigeration = false; break;
}

The problem, is I need to make the sequence of steps in each refrigeration ON and OFF just once as long the function gets call. the boolean guard above do that and allow me to start the components just once.

PROBLEM:
The function gets interrumped by a sensor error, or any other of disturbance.

Solution(???):
Make the machine starts form zero each time, do its job and stop until the next time.

I was thinking in something like:

static enum{ noSate, begin, end } machineState;

refrigerationON ///the fun begins yeiiii
{

if(refrigerattion == true && machineState == begin) //I dont think this works, will prevent the machine from fuctioning 
    { //clears machine } 

      if( refrigeration == true ) // bool to make the cycleof the machine just once 
          machineState = begin;
             {
                switch(c) 
                   case 1: break; //the system
                   case 2: break; // is on 
                   case 3: 
                            refrigeration = false; 
                            machineState = end;
                            break;
}

Im very confused, The idea is to use one or two static variables in to the fuction to know if it dont end properly, an if its the case.

Or in the best case scenario, use the static variables to re-start the machine the first time the main fuction call it.

The interest or the need to make it in the subrutine is to make each subrutine start and stop with out the need to insert more viariables in the code to do so, or depend of another subrutines to end properly to restart this.

I cant figure out.

Any one with more ideas?

Thanks.

-Alex.

Show all the code.

A faulty reading is an alarm "state". Does your code know how to deal with alarms such as a failed sensor or if someone has forgotten to close the door?

Hi
It sounds like your sensor error is not a rare affair, what is causing it.
Is it intermittent or a permanent failure error.
I agree you need some way to handle the error, so you do not have a program crash or lockup.

Tom... :slight_smile:

AlexLPD:
but bad thing happens if the machines are interrupted by an external factor (In my case the temperature

...SNIP...

Solution(???):
Make the machine starts form zero each time, do its job and stop until the next time.

I don't for a moment think that is the appropriate solution. For example, if you are on a train that gets delayed, you would not think of returning to your home and starting the journey again on a different train.

The correct solution is to deal with the error when it happens.

You have not said whether the error is just a spurious reading that is out of bounds or whether it is out of bounds because of some incorrect use of the refrigerator. I assume it is the former. In which case you should try to figure out why the reading is incorrect. You need to make sure that the number of spurious readings cannot increase so that they swamp the correct readings.

Assuming there is no risk of such swamping your code could easily be made to ignore some readings,

...R

HI and thanks for te replys.

In deed the whole system operarse in a ds1820 temperature probe, this probe rearly shows the error.

The old -127.00
It took us 2weeks to actual se one.

now I add the errol sensor in to the code... but dont make a lot of sense to stop for one error.

Would be better to ignore one or two bogus reads.

I think but I don't know how to properly make a filter, to discard one or two readings but to stop the system if the sensor don't provide a real temperature.

@AlexLPD, please read over your Posts after you post them and, if necessary correct any typing errors.

I think but I don't know how to properly make a filter, to discard one or two readings but to stop the system if the sensor don't provide a real temperature.

First you need to decide how you will identify bogus values. For example it might be reasonable to reject any temperature value that varies from the previous reading (or, better, from the average of the last N readings) by more than (say) 5 degrees.

And you could also count how many bogus readings arise in succession and sound an alarm if there are too many.

...R

static enum{ noSate, begin, end } machineState;

This looks like not enough states. Obviously there's a "start" or "noState" at the beginning but from there, where does it go? Does it go straight to "run" or is it waiting for some start button to be pushed?

When it detects an error, then it should jump to an error state. There may be many different errors, each one requires its own state. This can print the error to Serial, light an LED or whatever.

Then how does it get out of the error state? Does it wait 5 seconds, see if the temperature reading has resumed normal operation and then go back to "run" or does it go to "stop"?

The idea with a state machine is to put all of the various different configurations into one variable to keep track of. Rather than having a "begin" state which does different things depending on if the "refrigeration" variable is true or false, you should have two states: "beginNoRefrig" and "beginRefrig". Then the combination of buttons pushed and sensor readings will control which state it goes into. Every state should have defined entry and exit arrows and each state will have a maximum of one action, for example "startMotor", "waitForMotorToReachSpeed" and so on.

MorganS:
This looks like not enough states. Obviously there's a "start" or "noState" at the beginning but from there, where does it go? Does it go straight to "run" or is it waiting for some start button to be pushed?

When it detects an error, then it should jump to an error state. There may be many different errors, each one requires its own state. This can print the error to Serial, light an LED or whatever.

Then how does it get out of the error state? Does it wait 5 seconds, see if the temperature reading has resumed normal operation and then go back to "run" or does it go to "stop"?

The idea with a state machine is to put all of the various different configurations into one variable to keep track of. Rather than having a "begin" state which does different things depending on if the "refrigeration" variable is true or false, you should have two states: "beginNoRefrig" and "beginRefrig". Then the combination of buttons pushed and sensor readings will control which state it goes into. Every state should have defined entry and exit arrows and each state will have a maximum of one action, for example "startMotor", "waitForMotorToReachSpeed" and so on.

Well, I see your point, but in the moment I was trying to do a variable to know if the machine has ended it's execution or should te start from new.

A single variable to the entire machine must be something like;

{Nostate, Beginref, startfan1, startfan2, startcompresor, endRef.}stateRefrigeration

But, let's say I call the start refrigeration as the temperature keeps up. This means this machin will be, restarting each time the loop call it.

How to make the machine to turn the system one, and then wait. Or if is called and has no end properly. Restart the variables.???

In deed

Robin2:
@AlexLPD, please read over your Posts after you post them and, if necessary correct any typing errors.

First you need to decide how you will identify bogus values. For example it might be reasonable to reject any temperature value that varies from the previous reading (or, better, from the average of the last N readings) by more than (say) 5 degrees.

And you could also count how many bogus readings arise in succession and sound an alarm if there are too many.

...R

In deed the filtering of the temperature is crucial.

I was thinking in something like;

If (errorreading > 6) stopaall ();

If ( Rawtemp < -30 )
{ errorreading++
}
Else {
Temperatures [a] = Rawtemp
A++
}

Systemtemp = temperatures ÷5
}

Is psudocode, of course. Sorry for any typos. I'm on the cellphone rigthnow.

-Alex

"Turn the machine on and wait"

Wait for what? What change in the inputs makes it move into or out of the "wait" state?

I use a lot more states than that. Especially if I'm using buttons that require debouncing. Imagine I have a system with a start button and I need to hold that button for 5 seconds before it will start. The "wait" state must set all the outputs to a safe state. The only way it can leave that state is when the start button is pushed. Then it goes into a "startDebounce" state. There's two ways to leave that state. If the button is released (bounces) then it goes back to "wait". If 5000 milliseconds has passed then it goes to the "startHold" state, which waits until the button is released. Imagine if the person held the button down for 6 seconds. Then we can go to the "run" state.

So I've used 4 states there, just for a simple start button: "wait" "startDebounce" "startHold" and "run".

I see.. in this current system we aren't using buttons. The chiller must do its job when is on. Now I think I see your point.

So let's say temperature is high. Then we need the refrigeration on machine to do its job.

Static Enum { refBegin, turnfan1ON, turnfan2ON, runCom, refEnd }

The main idea is to have a state machine that turn on the refrigeration, an one machine to turn it off, a third machine will alternate the ventilation, all of 3 will be governed by the temperature.

I try to make a diagram.
-Alex

Maybe if we Chek temperature;

AlexLPD:
Is psudocode, of course. Sorry for any typos. I'm on the cellphone rigthnow.

That's the sort of thing I had in mind.

You also need to set errorReading back to 0 when you get one (or a number of) correct readings. And I think errorCount would be a better name.

...R

Well This a new tye o state machine, Is different in a number of aspects;
no Boolean guard,
not using var++, instead we declare; netxStep =4,5,6,
the lcd printing is in another switch case, as it is needed.

I think I crakn the self reset, but Im very insecure of the local variables. and how they will perform in this task. Please take a look at it and let me know your opinions.

void setup(){};

void loop()
{
refrigerattionOFF(); 
}


/*
This machine must Turn off al the elements of the system and 
know if has endend correclty in case it not, clear all variables
before re-start. 
Also must do the cycle just once its called from the loop. 
*/

void refrigerattionOFF()
{
          static enum { noState , called , compresorOFF,
                         fan1OFF, fan2OFF, refriOFFend } refriOFF; 

          static int lastRefriOFFstate;
          static int lcdRefriOFF;      // whats its value of the constant calling of this subrotuine? 
                                             // it re-start from 0 at each loop or maintain the last know state? 

          static unsigned long  refriOFFdelay = 1000; // better be here more easy to modify in this    routine     
          static unsigned long  refriOFFtime;

if( lastRefriOFFstate != 0 && refriOFF != refriOFFend ) refriOFF = noState; // if the machine has no end properly start   //over.
 
    switch ( refriOFF )
            {
         case noState :     
                       refriOFF = called; 
                       lastRefriOFFstate = refriOFF; 
                       break;
                       
         case called:    
                     lcdRefriOFF = 1;   
                     refriOFFtime = millis();   
                           if( millis() - refriOFFtime > refriOFFdelay)
                             {
                             refriOFF = compresorOFF;
                             lastRefriOFFstate = refriOFF;
                             refriOFFtime = millis(); 
                             }
                     break;  
  
         case compresorOFF:  
                     lcdRefriOFF = 2;
                   //  digitalWrite( compresor, HIGH ); 
                           if( millis() - refriOFFtime > refriOFFdelay)
                             {
                              refriOFF = fan1OFF;
                              lastRefriOFFstate = refriOFF;
                              refriOFFtime = millis(); 
                             }
                    
                     break;
  
         case fan1OFF : 
                     lcdRefriOFF = 3;
                   //  digitalWrite(fan1, HIGH);
                            if( millis() - refriOFFtime > refriOFFdelay)
                             {
                              refriOFF = fan2OFF;
                              lastRefriOFFstate = refriOFF;
                              refriOFFtime = millis(); 
                             }
                     
                     break;

         case fan2OFF:
                     lcdRefriOFF = 4; 
                     //    digitalWrite(fan2, HIGH);
                    if( millis() - refriOFFtime > refriOFFdelay)
                             {
                              refriOFF = refriOFFend;
                              lastRefriOFFstate = refriOFF;
                              refriOFFtime = 0;
                             }
                      break; 
                      
         case refriOFFend:       
                lastRefriOFFstate = 0;
                lcdRefriOFF = 5;
                }
                

//----------------------to print out the display
    
         switch (lcdRefriOFF)
                {
            case 1:   
                 //  lcd.setCursor(0,0);  lcd.print(F("                    "));             
                 //  lcd.setCursor(0,0);  lcd.print(F(" REFRIGERATION END  "));             
                   break;              

            case 2:   
                 //  lcd.setCursor(0,0);  lcd.print(F("                    "));             
                 //  lcd.setCursor(0,0);  lcd.print(F(" COMPRESSOR OFF     "));             
                   break;              
            
            case 3:   
                 //  lcd.setCursor(0,0);  lcd.print(F("                    "));             
                 //  lcd.setCursor(0,0);  lcd.print(F(" FAN 1 OFF          "));             
                   break;                                              

            case 4:   
                  // lcd.setCursor(0,0);  lcd.print(F("                    "));             
                  // lcd.setCursor(0,0);  lcd.print(F(" FAN 2 OFF          "));             
                   break;                           

            case 5:   
                  // lcd.setCursor(0,0);  lcd.print(F("                    "));                       
                   break; 
        }
}

Robin2:
That's the sort of thing I had in mind.

You also need to set errorReading back to 0 when you get one (or a number of) correct readings. And I think errorCount would be a better name.

...R

Well this is an idea to the temperatures, please take a look at it, and tell me your toughs.

void temperatureReading()
{
  static enum{ unkow, highTem, lowTemp, inRange }                  temperature; 
  static enum{ errorReading, goodReading }                         readingStatus ;
  static enum{ termostatError, highTermo, lowTermo, termoInRange } termostatState;
  static int errorCount;
  static int maxError = 5;

  static const int reads = 5; //we make 5 reading to average
  static float readings [reads];
  static float tempSystem;

  //-----------------------------------sensor error cheking 
     if ( rawTemp < -30.0 )
              {  
                 readingStatus = errorReading; 
              }
           else if ( readingStatus = goodReading );
  
  //-----------------------------------temperature 
     if (readingStatus == errorReading) 
        {
               errorCount++;  
           if ( errorCount > maxError ) // if we exceed the max of bogus readings on a row  
              {
                  temperature = unkow;   // we declare the system temperate as uknow stop the system as long failure 
              }
        }
  
     else if (readingStatus == goodReading ) //if we got a nice reading 
        {
           errorCount = 0;
           
           static int readIndex; 
           static float total;
           
           if(readIndex > reads ) readIndex = 0; //make 0 the index 
           
           readings[readIndex] = rawTemp;
           
               for( int c = 0; c < readIndex ; c++ )
                   {
                       total += readings[c];
                   }
        
               tempSystem = (total / readIndex );
   
           readIndex++;
        }            
  
  //-----------------------------------system temperature reading and handling 
  
if (tempSystem >= 7.00);                     termostatError = highTermo;
if (tempSystem <= 3.00);                     termostatError = lowTermo;
if (tempSystem > 3.00 && tempSystem < 7.00); termostatError = termoInRange;
  

}//end of temperature reading.
  1. When you define the enum, don't just make one variable. By giving the enum a name, it becomes a type, like int and boolean are types. Then you can make several variables of that same type.

While an enum can be coerced [jammed, forced, mangled] into an integer, it is best if all variables holding the state are of the same type. That's why the following code would cause an error with your current variable declarations.

if( lastRefriOFFstate != noState && refriOFF != refriOFFend )

So create the enum and give it a name in global scope, then the local variables in the function can use that as their type.

         case called:    
                     lcdRefriOFF = 1;   
                     refriOFFtime = millis();   
                           if( millis() - refriOFFtime > refriOFFdelay)

When you just put millis() into refriOFFtime, what do you think the if statement will see? It will see that no time has passed, so it will never be true.

This is one of the strange things about state machines. If a state needs a timer, you need to set that timer when you leave the previous state. So whichever state leads into this state should set the timer to the current millis at that time. Then this state can sit watching 1000 milliseconds slowly tick away.

It looks like your other states get this right, but you'll never reach them because of this error.

  1.                             lastRefriOFFstate = refriOFF;
    DONT DO THIS! A state machine doesn't need to know where it came from. If it's time to turn on the compressor then it's always time to turn on the compressor. It doesn't need to know what happened before.

Now obviously, in the real world, you do need to know that the compressor was off for the appropriate time and that there were no errors that might make it a bad idea to turn it on. So those are states. If you have a physical condition that must wait 5 seconds before starting the compressor then make a state for that. Then it can look at the inputs to decide where to go to the next state, which might be an error state that flashes the LCD until somebody comes over to close the door or actually starting the compressor.

  1. Your states seem very linear. Each state can only go to one following state. If this is true, then you didn't need a state machine in the first place. State machines are great where there are branches and decisions. A lot of states should be checking for error conditions.

You might have a state that runs the compressor for 5 minutes. Then it jumps to another state called "checkTemperature" which checks that the temperature is dropping as quickly as you expect. If the temperature is not dropping, then it should jump to an error state. If it is refrigerating properly, it might loop back to the same "5 minute" state so that it knows to check again 5 minutes from now.

But that "5 minute" state might have some error conditions which stop it immediately - it can jump to another type of error.

Thanks for the observations.

While an enum can be coerced [jammed, forced, mangled] into an integer, it is best if all variables holding the state are of the same type. That's why the following code would cause an error with your current variable declarations.
Code: [Select]
if( lastRefriOFFstate != noState && refriOFF != refriOFFend )

Im sorry do you mean it will create an error because both are non very well defined states? or variables types?
like ; If (int a != int b && bool c != bool d) ??

You mean like;

enum int { unkow =0, refriBegin=1, turnOnComp=3, turnFan1=4, turnFan2=5, refriend=6 } refristate= 6;

or like;

enum int { unkow, refriBegin, turnOnComp, turnFan1, turnFan2, refriend } refristate;

2.- yes is an terrible error in the timming in there, the millis must be in the past state.

3.-I see your point, I just trying to know if the state machine has ended properly.
If the code is corrected, do you think will be self-restarting? I have many doubts with the static variables, and how to use them properly on the code. Still they are incredible handy to make this routines "transportables" from one code to another.

4.- if there is no a state machine, whath will be another solution for this? I know the turn off and turn on of the system is very linear, it has to be in a specific order to be good. and the order and the timming are not variables, at least not with my actual skills at the moment. At the first I was using a switch case with a timmer, but also yields to errors if there is an error in between the activation or de-activation. (system keeps refrigeratting, system start the compresor, with out the fans, or the fans without the compresor, evens calls the refriON or refriOFF routine, but if they were not ended properly the boolean true needed to begin the cycle is not active, and therefore the function dont enter to do its job.

( a temperature reaction control will be better, I have some drafs but is not a complete code; It the temperature goes high at a "normal" rate, then we wait to the set point, if the temperature goes up too fast, then we star the refrigeration before the set point, or if the door is open we start the refrigeration before reach the temperature limit.) Is a nice and interesting problem to solve, but for now I have to settle down to understand and make this code error proof.

May be the most appropriated thing to do is to; make a state machine for the temperature, and some sort of linear activation for the refrigerattion, I will make some drafs and I will came back for more advice.

Thanks for the help.
-Alex.

AlexLPD:
Well this is an idea to the temperatures, please take a look at it, and tell me your toughs.

You are still not checking your typing. It's not a difficult thing to correct silly errors.

My thought is that if it works it's fine.

I don't think I would make it quite so complex - but I have not studied it carefully - maybe the apparent complexity is required.

...R

Robin2:
You are still not checking your typing. It's not a difficult thing to correct silly errors.

My thought is that if it works it's fine.

I don't think I would make it quite so complex - but I have not studied it carefully - maybe the apparent complexity is required.

...R

Is there a simpler way?

I got the control in my shop now, we are test it. In a random manner appears a sensor error and from there all stops, but the sensor reading is good. A bug on the software.

I will re think a way to make this more simple.

Thanks for the comments.

-Alex.
-Alex.

Well you need to track down the source of that stoppage. Pump the data out to the serial monitor. See what was the last thing it sent.

In your environment, I suspect it could be almost anything. It could be sensor errors or it could be interference on the power wires coming from almost anywhere in your plant. Start looking for web pages with hints on reducing interference and power spikes coming into your circuit.

MorganS:
Well you need to track down the source of that stoppage. Pump the data out to the serial monitor. See what was the last thing it sent.

I agree completely.

You might also write a short program that just collects and displays data from the sensor giving problems.

...R