This is the multi-page printable view of this section. Click here to print...

Return to the regular view of this page

As of 2025-07-24

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.