/      日本語

SM_SIMPLE State Machine

State management
SM_SIMPLE is used in sample code for handling state transitions, waiting for timeouts, transmission completion, and similar processing.

Here is a basic example of 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(); // Initialization
}

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 (/* Transmission request successful */) {
        step.set_timeout(100); // Set timeout
        step.clear_flag(); // Waiting for completion

        step.next(STATE::TX_WAIT_COMP);
      }
    break;

    case STATE::TX_WAIT_COMP:
      if (step.is_timeout()) the_twelite.reset_system(); // Timeout
      if (step.is_flag_ready()) sleepNow(); // Flag was set
    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); // 10 sec
}

Explanation

To use SM_SIMPLE, define an enum class that lists the states. In the example above, this is defined as STATE. You create a class object like SM_SIMPLE<STATE> step; using the enum as the template parameter. Then call .setup() on the object to initialize it.

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 a value of 0, which corresponds to STATE::INIT in the above example. To get the current state, use .state() and use it in a switch statement inside a do while loop as shown.

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

To transition states, call .next(). When the state changes, b_more_loop() returns true and the do while loop executes again. In the example, calling .next(STATE::TX) from STATE::SENSOR causes another loop iteration, executing the case STATE::TX: block. If the state does not change, the loop exits, and loop() ends until the next call.

  do {
    switch(step.state()) {
    ...
    case STATE::SENSOR:
      ...
      step.next(STATE::TX); // (1) State transition
    break;

    case STATE::TX: // (3) Called on second loop
      if (/* Transmission request successful */) {
      ...
    }
  } while (b_more_loop()); // (2) Loop continuation check

To wait for completion of processing (e.g., transmission complete), call .clear_flag() and later call .set_flag(uint32_t) from a callback or similar to signal completion. You can retrieve the passed uint32_t value using .get_flag_value().

To handle timeouts, call .set_timeout(uint32_t) to store the current time, then use .is_timeout() to check if the timeout duration has elapsed.

    case STATE::TX:
      if (/* Transmission request successful */) {
        step.set_timeout(100); // Set timeout
        step.clear_flag(); // Wait for completion

        step.next(STATE::TX_WAIT_COMP);
      }
    break;

    case STATE::TX_WAIT_COMP:
      if (step.is_timeout()) ...; // Timeout
      if (step.is_flag_ready()) ...; // Flag was set
    break;
...

// Transmission complete event
void on_tx_comp(mwx::packet_ev_tx& ev, bool_t &b_handled) {
	step.set_flag(ev.bStatus); // Set the flag
}

To continue using SM_SIMPLE after waking from sleep, be sure to call .on_sleep(bool) before going to sleep. If you pass false, the state machine resets to state 0 upon waking; if true, it resumes from the pre-sleep state.

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

Source Code

Below 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 vary depending on the version.
  • The source is located in the MWX library source folder under SM_SIMPLE.hpp.