Máquina de estados: Librería FSM

máquinas de estados se pueden abordar multitud de problemas, es lo que más se utiliza en automatización industrial.
otro de los ejemplos basicos que se se puede utilizar es para

no he podido mirarlo aun pero como todo lo que desarrolla Igor será un trabajo excepcional

3 HURRAS POR IGOR ¡¡¡ :slight_smile:

Igor eres un crack, este finde tenía pensado escribir una para el MMI de un controlador de una cámara climática y es muy posible que tu librería cumpla con lo que necesito. Ya os contare si me decanto por estos derroteros. Muchas gracias por tu contribución. Publica la en el playground que seguro que la usa la gente!

Yo la he creado para una cosilla que estoy haciendo, ya que andaba hace tiempo con ganas de crearme algo genérico.
Resulta bastante sencillo, ya que sólo hay que crearse una tabla de estados (en forma de dos estructuras que se guardan en memoria de programa). Te deja bastante flexibilidad y si necesitas modificar el flujo de tu programa o añadir nuevas cosas, no necesitas reescribrir el código.

Ya me contaréis si la usáis...De momento está en versión "beta", por eso sólo la he publicado por aquí. En paralelo la estoy usando en un proyecto, pero he creído que a lo mejor podría ser de utilidad.

:wink:

Hola Igor

Como no sigo rigurosamente los temas de debate, y el lector de RSS no me habia avisado de este hilo no lo he visto hasta ahora.

Interesante tu planteamiento y lamento no haber tenido conocimiento de el hace un par de semanas, me habria ahorrado algo de trabajo, ya que he tenido que hacer algo similar para gestionar una lavadora que tiene averiada la tarjeta de control y la tengo substituida con un Arduino.

Tu planteamiento es similar al usado en PLCs para control de procesos.

Me he mirado el pdf porque no me da tiempo de echarle un ojo a todo, esta noche si puedo me lo miro. Por lo que he visto en el pdf, y para acabar de dar la vuelta a la rosca, te aconsejo que añadas funciones en las transiciones (no solo en los estados). Esto simplifica las máquinas y las hace más eficaces (no tienes que añadir tantos estados inutiles). A parte, he visto que guardas las cosas en la memoria de programa en vez de en la RAM. La mayoria de las veces, es la memoria del programa la que va justa, y la RAM suele ir más suelta (básicamente porque es más barata para los fabricantes de chips).

Como colofón, estaría bien aprovechar que puedes usar C++ para Arduino convirtiendo las máquinas en objetos. De este modo es mucho más simple instanciarlos, y así crear todas las máquinas de estados que necesites.

Que micro usas tu que tiene mas Ram que Flash de programa?? La SRAM es la que es menor que la flash de programa (2kb versus 32 kb)

Acerca funciones en las transiciones, estoy de acuerdo con lo que dices. Tambien se podria anyadir funciones a la entrada, durante estado activo y a la salida del estado.... Pero claro, todo esto complica la programacion.
Para hacerlo en plan objetos existen ya cosas.... por ejemplo smc te crea las clases y tal... http://smc.sourceforge.net/
Tambien hay QP para Arduino....

Mi objetivo era tener algo mucho mas basico que todo eso. Una simple lookup table de transiciones de estados con las funciones a ejecutar.

Saludos y gracias por las sugerencias.

:wink:

Nota.- estoy en ordenador ingles y no tengo ni acentos ni enyes.

Buen trabajo Igor!!

Ahora que lo comentas hace tiempo que tengo ganas de incarle el diente a QP

Has hecho algo con QP¿

Alternativa, implementar el patrón state machine. Pero tal y como esta articulado tiene buena pintan. Añadir callback en las transiciones seria lo ideal pero: una maquina de Mealy siempre se puede modelar con una de Moore y viceversa.

Una vez mas, me parece un librería muy flexible tal y como esta.

Ouch, me lié, pensaba que era memoria EEPROM, vaya pajarada :blush:

No pasa nada.... :smiley:

Hola a todos. Alguno podría indicarme si la máquina de estados es mas potente que el Grafcet.
Este último lo conozco, pero de la FSM no tengo ni idea. Ademas buscando por google para documentarme
tampoco he aclarado que es mejor si Mealy o Moore.

Bueno, gracias por adelantado.

Jaume
Barcelona

:astonished: ole!

Tengo un link que explica ambos, (en ingles)

Es sobre un microondas y se ve las diferencias de ambos.
Cual es mejor? la moraleja de este documento dice que ninguno pero ambos juntos.

A nivel de cómputo son igual de potentes.

Si quereís demostración matemática, la teoría de automatas dice:

Si no os quereís leer el rollo, al final:
Una maquina mealy es equivalente a una Moore y viceversa.

Al final, la elección entre un tipo de diseño Moore o Mealy es más bien a qué esté acostumbrado el desarrollador. Si:
Para ti una transición entre estados implica un cambio en la salida. Elige Mealy

Para ti un estado implica una salida . Elige Moore.

Saludos,
Aritz R

Acabo de hacerme un programita rápido usando mi librería FSM. Se trata de medir el tiempo de 0 a 100 kph en el coche, recogiendo la velocidad del bus CAN.... un quema embragues!!

Es un todo en uno!!

  • Tiene como utilizar filtros hardware para el bus CAN con nuestra librería.
  • Como ir cambiando de menus en el LCD según eventos
  • Programación mediante Máquina de estados.

El programa es tán sencillo como:

// -------------------------------------------------------------------------------------
// SECUduino: Chrono 0 to 100 kph
// By Igor Real
// 22/10/2011
// -------------------------------------------------------------------------------------

#include <FSM.h>
#include <LiquidCrystal.h>
#include <CAN.h>
#include "myStates.h"

#define delayShowData  7000

//VARIABLES
unsigned char myEvent;
char myByteRec;
unsigned long my0to100;
unsigned long t0;
unsigned long t_prev;
float Speed;
char bUpdateLCD;

LiquidCrystal LCD(13,12, 11, 10, 9, 8, 7);

void setup()
{
  
  LCD.begin(20,4);
  bUpdateLCD=1;
  
  CAN.begin(100);
  uint16_t  Filters[6];
  uint16_t Masks[2];
  Masks[0]=0x3FF;
  Masks[1]=0x3FF;
  Filters[0]=0x351;  //Buffer0
  Filters[2]=0x351;  //Buffer1
  CAN.SetFilters(Filters,Masks);  //Only messages with ID=0x351 are allowed in both Rx buffers
  
  FSM.begin(FSM_NextState,nStateMachine,FSM_State,nStateFcn,STATE1);
}

void loop()
{
  ReadEvents();
  FSM.Update();
}


void ReadEvents(void)
{
  
  if (CAN.CheckNew() )
  {
    CAN.ReadFromDevice(&CAN_RxMsg);
    Speed=0.005*(256*CAN_RxMsg.data[2]+CAN_RxMsg.data[1]);
    LCD.setCursor(0,1);
    LCD.print("Speed= ");
    LCD.print((int)Speed);
  }
  
  if (Speed<=0){
    FSM.AddEvent(EV_Stop);
  }else if (Speed>=100){
    FSM.AddEvent(EV_100);
  }else if ( (Speed>0) && (Speed<100) ){
    FSM.AddEvent(EV_Start);
    bUpdateLCD=1;
  }
  
}

void func1(void)
{
  if (bUpdateLCD)
  {  
    LCD.clear();
    LCD.setCursor(0,0);
    LCD.print("  *- SECUduino -*");
  
    LCD.setCursor(0,2);
    LCD.print("State 1");
    
    bUpdateLCD=0;
  }
}

void func2(void)
{
  t0=millis();
  LCD.setCursor(0,2);
  LCD.print("State 2");
  FSM.AddEvent(0);
}

void func3(void)
{
  LCD.setCursor(0,2);
  LCD.print("State 3");
}

void func4(void)
{
  my0to100=millis()-t0;
  t_prev=millis();
  LCD.setCursor(0,2);
  LCD.print("State 4");
  
  LCD.setCursor(0,3);
  LCD.print((float)my0to100/(float)1000.0);
  LCD.print(" s");
  
  while(millis()-t_prev<=delayShowData);
  FSM.AddEvent(EV_Wait);  
}

Las estructuras usadas son:

const FSMClass::FSM_State_t FSM_State[] PROGMEM= {
// STATE,STATE_FUNC
{STATE1,func1},
{STATE2,func2},
{STATE3,func3},
{STATE4,func4},
};

const FSMClass::FSM_NextState_t FSM_NextState[] PROGMEM= {
// STATE,EVENT,NEXT_STATE
{STATE1,EV_Start,STATE2},
{STATE2,0,STATE3},
{STATE3,EV_100,STATE4},
{STATE3,EV_Stop,STATE1},
{STATE4,EV_Wait,STATE1},
};

Cuando vaya al coche, grabaré otro video....

]:smiley:

menuda aceleracion Igor !! hahaha
como te dije ya implemente la funcion 0 a 100 pero sin estados y me funciono de maravilla, pero creo que con tu codigo es tambien muy claro.
me sirve de mucho el ejemplo, sobre todo para el tema de mascaras.
a ver si encuentro tiempo y lo pruebo.
genial trabajo Igor.

Perfecto! Pon tu código por aquí y así hay dos enfoques diferentes de un mismo problema.

:wink:

no puedo poner toooooodo el codigo porque es enorme, a ver si saco un rato y extraigo el codigo y creo un miniprograma que solo haga eso, mientras tanto...

  void performance_accel(){
        
      if (OBD_DATA == true)
        {
          unsigned long elapse_time;
          elapse_time = millis() - start_time_elapse;
          /*
          Serial.print("Last speed: ");        Serial.print(last_SPEED_rx_data);  
          Serial.print("\tCurrent speed: ");   Serial.print(SPEED_rx_data);
          Serial.print("\tStart Millis: ");    Serial.print(start_time_elapse);
          Serial.print("\tCurrent Millis: ");    Serial.print(millis());
          Serial.print("\tElapse Millis: ");   Serial.print(elapse_time);
          Serial.println();
          */
          // performance_status = 0 -> ready to start test (0 kmh)
          // performance_status = 1 -> test started...reading (V > 0 kmh y < 100 y no han habido deceleraciones)
          // performance_status = 2 -> test aborted por deceleracion  o   test finalizado, esperando V = 0 kmh
          
          // Check if car is stopped
          if ( SPEED_rx_data == 0)
          {
            start_time_elapse = millis();
            performance_status = 0; // ready to start test
          }
          
          
          // READY TO START TEST -           
          if ( (performance_status == 0) && (SPEED_rx_data > 0) )
                {
                    performance_status = 1;  // test started...recording
                    Serial.println("Performance timing STARTED !!!");
                }

          
          // RUNNING - 
          if (  (performance_status == 1)    &&    (SPEED_rx_data >= 100)    )
          {
                Serial.print("Performance timing COMPLETED"); Serial.print(" Time: "); Serial.println(elapse_time);
                  if (elapse_time < best_0_to_100)
                      {
                          Serial.println(" ******* NEW PERFORMANCE RECORD ***** ");
                          best_0_to_100 = elapse_time;
                      }
                performance_status = 2;
          }
          
          
          // RUNNING - Stop the test if deceleration before 100 kmh
          if (    (performance_status == 1)    &&    (SPEED_rx_data < last_SPEED_rx_data)    )
          {
              Serial.println("Performance timing STOPPED");
              performance_status = 2;
          }
         
        } // end if obd = true
        
      }

como he dicho luego lo pulo un poco y que quede mas presentable

ja,ja,ja. Está muy bien! Lo has creado con una máquina de estados.... tu performance_status no es más que una variable con el estado actual.
Hay muchas maneras de hacerlo: con if, con switch, con lockup tables, etc.

:wink:

Alguno podría indicarme si la máquina de estados es mas potente que el Grafcet.

El concepto es similar. Me acuerdo que cuando me explicaron Grafcet, como teníamos autómatas Omron que sólo tenía lenguaje ladder, nos dibujabamos el diagrama a mano y lo programábamos con biestables, que los usábamos para simular cada estado.

Me refiero con ésto, que al final que si Petri, Grafcet, UML, Mealy, Moore, etc son conceptos similares. Tampoco soy un experto, pero al final me parece un poco todo lo mismo. Actualmente me toca mirarme bastante código escrito en Stateflow (+ Simulink) de Matlab, y es más de lo mismo...

La librería FSM, simplemente es un paso más a tener un switch gigante, ya que en modo look up table, te queda más limpio y fácil de leer. Es una librería bastante básica... podrías ir ampliando cosillas ( un buen libro gratuito http://www.tte-systems.com/books/pttes ).

Has hecho algo con QP¿

No. Alguna vez he intentado leer algún capítulo de su libro, pero no aguanto más de 5 min...es un somnífero cojonudo!! :wink: