jonblack/arduino-fsm

Possible problem on timed_transitions

Opened this issue · 3 comments

Consider these transitions:

fsm.add_timed_transition(&state_waitfortriggerack, &state_waitfortriggerack, 3000, NULL);
fsm.add_transition(&state_waitfortriggerack, &state_rpinotworking, TIMEOUT, NULL);

and then I am doing a:

fsm.trigger(TIMEOUT);

I was expecting the loop of the 3000ms to stop, but it keeps calling the entry function of state_waitfortriggerack. The fsm.trigger(TIMEOUT) is actually changing the state and doing its thing, but the timed transition keeps repeating.

Ping, anyone has managed to use timed transactions to same state?

Hi, this is what I used and it seemed to work:

/* ----------------------------------------------------- */
#include "Fsm.h"
#include "Button2.h";
/* ----------------------------------------------------- */
#define TIMEOUT 1000
/* ----------------------------------------------------- */
void waitfortriggerack() {
  Serial.println("waitfortriggerack");
}

void rpinotworking() {
  Serial.println("rpinotworking");
}
/* ----------------------------------------------------- */
State state_waitfortriggerack("wait", &waitfortriggerack);
State state_rpinotworking("rpinotworking", &rpinotworking);

Fsm fsm(&state_waitfortriggerack);
Button2 btn = Button2(D0);
/* ----------------------------------------------------- */
void btn_released(Button2& btn) {
  Serial.println("CLICK");
  fsm.trigger(TIMEOUT);
}
/* ----------------------------------------------------- */
void setup() {
  Serial.begin(9600);
  delay(50);

  fsm.add_timed_transition(&state_waitfortriggerack, &state_waitfortriggerack, 3000, NULL);
  fsm.add_transition(&state_waitfortriggerack, &state_rpinotworking, TIMEOUT, NULL);
  
  btn.setReleasedHandler(btn_released);
}
/* ----------------------------------------------------- */
void loop() {
  btn.loop();
  fsm.run_machine();
}
/* ----------------------------------------------------- */

This is the output - I pressed the button after a little while:

waitfortriggerack
waitfortriggerack
waitfortriggerack
waitfortriggerack
waitfortriggerack
CLICK
rpinotworking

After the button press, the machine keeps the RPI state.

In case anyone runs into this, there's a limitation that isn't super obvious in the FSM code as it is implemented. The state isn't recorded as being changed until after the on_enter is called on the new state. As a result, if you use trigger to send an event from the start of the new state, it won't find a transition from that new state to the next state.

i.e., if you have three states "one", "two" and "three", and you have a timed transition from two back to itself, it'll get called forever if you try to do a trigger(three) in the start method for two.

I fixed it in a local copy of the library by moving the m_current_state = transition->state_to; line in make_transition above the transition->state_to->on_enter() call.

Its a behavioral change, but because there's no real documentation for the library its unclear if the prior behavior is as-intended or not.