SM_SIMPLE state machine

SM_SIMPLE is provided to wait for processing such as state transitions, waiting for timeouts, and completion of transmission in the sample code.

The following is a basic code excerpt from SM_SIMPLE.

#include <SM_SIMPLE>

enum class STATE : uint8_t {
	INIT = 0,
	SENSOR,
	TX,
	TX_WAIT_COMP,
	GO_SLEEP
};

SM_SIMPLE<STATE> step;

begin() {
  ...
  step.init(); //initialize
}

loop() {
  do {
    switch(step.state()) {
    case STATE::INIT:
      ...
      step.next(STATE::SENSOR);
    break;
    
    case STATE::SENSOR:
      ...
      step.next(STATE::TX);
    break;
    
    case STATE::TX:
      if (/*success on tx request*/) {
        step.set_timeout(100); // set timeout as 100ms
        step.clear_flag();
          
        step.next(STATE::TX_WAIT_COMP);
      }
    break;
    
    case STATE::TX_WAIT_COMP:
      if (step.is_timeout()) the_twelite.reset_system(); // is timeout?
      if (step.is_flag_ready()) sleepNow(); // is set the flag?
    break;
    
    ...
    }
  } while(step.b_more_loop());
}

void on_tx_comp(mwx::packet_ev_tx& ev, bool_t &b_handled) {
	step.set_flag(ev.bStatus);
}

void sleepNow() {
	step.on_sleep(false); // reset state machine.
  the_twelite.sleep(10000); // 10sec
}

Explanation

To use SM_SIMPLE, you need to define an enum class as a list of states. In the above, it is defined as STATE. The class object is generated as SM_SIMPLE<STATE> step; with this stage as a parameter. The generated class object should be initialized by .setup().

enum class STATE : uint8_t {
	INIT = 0,
	SENSOR,
	TX,
	TX_WAIT_COMP,
	GO_SLEEP
};

SM_SIMPLE<STATE> step;

void setup() {
  step.init();
}

The initial state of SM_SIMPLE has the value 0, corresponding to STATE::INIT in the above example. To get the current state, use .state() and use it as a judgment expression in the switch clause of the do while statement as in the above example.

loop() {
  do {
    switch(step.state()) {
    case STATE::INIT: // State with value 0
    ...

Call .next() for state transitions. If the state changes, b_more_loop() is set to true and the loop in the do while clause is executed again. In the example, calling .next(STATE::TX) from the STATE::SENSOR state will cause the loop to be executed again and the case STATE::TX: clause will also be executed. If the state is not changed, the do while loop is escaped and the loop() is terminated once. Wait until the next call to loop().

  do {
    switch(step.state()) {
    ...
    case STATE::SENSOR:
      ...
      step.next(STATE::TX); // (1)state transition
    break;
    
    case STATE::TX: // (3) Called in the second loop
      if (/*success on tx request*/) {
      ...
    }
  } while (b_more_loop()); // (2) loop continue check

If you want to wait for processing such as completion of transmission, call .clear_flag(), and then signal the completion of processing by .set_flag(uint32_t) in another callback function or the like. Parameters of type uint32_t specified here can be read from .get_flag_value().

If you want to process a timeout, you can record the time when you call .set_timeout(uint32_t) and check if the timeout time has elapsed with .is_timeout().

    case STATE::TX:
      if (/*success on tx request*/) {
        step.set_timeout(100); // set timeout
        step.clear_flag();
          
        step.next(STATE::TX_WAIT_COMP);
      }
    break;
    
    case STATE::TX_WAIT_COMP:
      if (step.is_timeout()) ...; // timeout
      if (step.is_flag_ready()) ...; // is set the flag?
    break;
...

// an event of tx completion
void on_tx_comp(mwx::packet_ev_tx& ev, bool_t &b_handled) {
	step.set_flag(ev.bStatus); // set the flag
}

SM_SIMPLE will be used again when returning from sleep, but it should always be called .on_sleep(bool) before sleep. If you put false in the parameter, it will start from the 0 state after recovery, and if you put true, it will resume from the state just before sleep.

void sleepNow() {
	step.on_sleep(false); // reset state machine.
  the_twelite.sleep(10000); // 10sec
}

Source Code

The following is the source code for SM_SIMPLE.

// very simple class to control state used in loop().
template <typename STATE>
class SM_SIMPLE {
	uint32_t _u32_flag_value;  // optional data when flag is set.
	uint32_t _ms_start;		// system time when start waiting.
	uint32_t _ms_timeout;	// timeout duration

	STATE _step;			  // current state
	STATE _step_prev;		// previous state
	bool_t _b_flag; 		// flag control.
public:
	// init
	void setup() { memset(this, 0, sizeof(SM_SIMPLE)); }
	// call befoer sleeping (save state machine status)
	void on_sleep(bool b_save_state = false) {
		STATE save = _step;
		setup();
		if(b_save_state) _step = _step_prev = save;
	}

	// state control
	void next(STATE next) { _step = next; } // set next state
	STATE state() { return _step; } // state number
	bool b_more_loop() { // if state is changed during the loop, set true
		if (_step != _step_prev) { _step_prev = _step; return true; }
		else return false;
	}

	// timeout control
	void set_timeout(uint32_t timeout) {
		_ms_start = millis();
		_ms_timeout = timeout;
	}
	bool is_timeout() { return (millis() - _ms_start) >= _ms_timeout; }

	// flag control
	void clear_flag() { _b_flag = false; _u32_flag_value = 0; }
	void set_flag(uint32_t u32_flag_value = 0) {
		_b_flag = true;
		_u32_flag_value = u32_flag_value; }
	uint32_t get_flag_value() { return _u32_flag_value; }
	bool is_flag_ready() { return _b_flag; }
};
  • Contents may change depending on the version.

  • The main body will be stored in SM_SIMPLE.hpp in the mwx library resource folder.

最終更新