SM_SIMPLE State Machine
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
.