From 3ea2aa4bd3855b8ace0ad832f9ec18c6585de687 Mon Sep 17 00:00:00 2001 From: Thelmos Date: Sat, 12 Mar 2016 02:15:40 +0100 Subject: [PATCH] v2.2.0 * Add `on_state()` handler to states * New `run_machine()` method to invoke machine execution (includes a `check_timed_transitions()` call) * New `timed_switchoff.ino` example sketch to ilustrate new `on_state()` and `run_machine()` funcionality * Corrections: - `make_transition()` correctly initialices timed transitions start milliseconds (`make_transition()` is now a fsm method) - Initial state `on_enter()` handler is now correctly executed on fsm first run - Removed `Serial.println(now);` trace in _Fsm.cpp_ - Correct initialization of `m_num_timed_transitions` --- Fsm.cpp | 73 +++++--- Fsm.h | 12 +- README.md | 12 ++ examples/light_switch/light_switch.ino | 7 +- examples/multitasking/multitasking.ino | 18 +- examples/timed_switchoff/Fsm.cpp | 174 +++++++++++++++++++ examples/timed_switchoff/timed_switchoff.ino | 71 ++++++++ library.properties | 2 +- 8 files changed, 330 insertions(+), 39 deletions(-) create mode 100644 examples/timed_switchoff/Fsm.cpp create mode 100644 examples/timed_switchoff/timed_switchoff.ino diff --git a/Fsm.cpp b/Fsm.cpp index 8a73782..9b932ca 100644 --- a/Fsm.cpp +++ b/Fsm.cpp @@ -16,8 +16,9 @@ #include "Fsm.h" -State::State(void (*on_enter)(), void (*on_exit)()) +State::State(void (*on_enter)(), void (*on_state)(), void (*on_exit)()) : on_enter(on_enter), + on_state(on_state), on_exit(on_exit) { } @@ -26,7 +27,9 @@ State::State(void (*on_enter)(), void (*on_exit)()) Fsm::Fsm(State* initial_state) : m_current_state(initial_state), m_transitions(NULL), - m_num_transitions(0) + m_num_transitions(0), + m_num_timed_transitions(0), + m_initialized(false) { } @@ -90,20 +93,22 @@ Fsm::Transition Fsm::create_transition(State* state_from, State* state_to, void Fsm::trigger(int event) { - // Find the transition with the current state and given event. - for (int i = 0; i < m_num_transitions; ++i) + if (m_initialized) { - if (m_transitions[i].state_from == m_current_state && - m_transitions[i].event == event) + // Find the transition with the current state and given event. + for (int i = 0; i < m_num_transitions; ++i) { - m_current_state = m_transitions[i].make_transition(); - return; + if (m_transitions[i].state_from == m_current_state && + m_transitions[i].event == event) + { + Fsm::make_transition(&(m_transitions[i])); + return; + } } } } - -void Fsm::check_timer() +void Fsm::check_timed_transitions() { for (int i = 0; i < m_num_timed_transitions; ++i) { @@ -114,13 +119,11 @@ void Fsm::check_timer() { transition->start = millis(); } - else - { + else{ unsigned long now = millis(); - Serial.println(now); if (now - transition->start >= transition->interval) { - m_current_state = transition->transition.make_transition(); + Fsm::make_transition(&(transition->transition)); transition->start = 0; } } @@ -128,18 +131,44 @@ void Fsm::check_timer() } } +void Fsm::run_machine() +{ + // first run must exec first state "on_enter" + if (!m_initialized) + { + m_initialized = true; + if (m_current_state->on_enter != NULL) + m_current_state->on_enter(); + } + + if (m_current_state->on_state != NULL) + m_current_state->on_state(); + + Fsm::check_timed_transitions(); +} -State* Fsm::Transition::make_transition() +void Fsm::make_transition(Transition* transition) { + // Execute the handlers in the correct order. - if (state_from->on_exit != NULL) - state_from->on_exit(); + if (transition->state_from->on_exit != NULL) + transition->state_from->on_exit(); + + if (transition->on_transition != NULL) + transition->on_transition(); - if (on_transition != NULL) - on_transition(); + if (transition->state_to->on_enter != NULL) + transition->state_to->on_enter(); + + m_current_state = transition->state_to; - if (state_to->on_enter != NULL) - state_to->on_enter(); + //Initialice all timed transitions from m_current_state + unsigned long now = millis(); + for (int i = 0; i < m_num_timed_transitions; ++i) + { + TimedTransition* ttransition = &m_timed_transitions[i]; + if (ttransition->transition.state_from == m_current_state) + ttransition->start = now; + } - return state_to; } diff --git a/Fsm.h b/Fsm.h index f58757f..afbb3dd 100644 --- a/Fsm.h +++ b/Fsm.h @@ -26,9 +26,9 @@ struct State { - State(void (*on_enter)(), void (*on_exit)()); - + State(void (*on_enter)(), void (*on_state)(), void (*on_exit)()); void (*on_enter)(); + void (*on_state)(); void (*on_exit)(); }; @@ -45,8 +45,10 @@ class Fsm void add_timed_transition(State* state_from, State* state_to, unsigned long interval, void (*on_transition)()); + void check_timed_transitions(); + void trigger(int event); - void check_timer(); + void run_machine(); private: struct Transition @@ -56,7 +58,6 @@ class Fsm int event; void (*on_transition)(); - State* make_transition(); }; struct TimedTransition { @@ -68,6 +69,8 @@ class Fsm static Transition create_transition(State* state_from, State* state_to, int event, void (*on_transition)()); + void make_transition(Transition* transition); + private: State* m_current_state; Transition* m_transitions; @@ -75,6 +78,7 @@ class Fsm TimedTransition* m_timed_transitions; int m_num_timed_transitions; + bool m_initialized; }; diff --git a/README.md b/README.md index 4ffa89a..d7715ac 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,18 @@ feature branch. # Changelog +**2.2.0 - 12/03/2016** + +* Add `on_state()` handler to states +* New `run_machine()` method to invoke machine execution (includes a `check_timed_transitions()` call) +* New `timed_switchoff.ino` example sketch to ilustrate new `on_state()` and `run_machine()` funcionality +* Corrections: + - `make_transition()` correctly initialices timed transitions start milliseconds (`make_transition()` is now a fsm method) + - Initial state `on_enter()` handler is now correctly executed on fsm first run + - Removed `Serial.println(now);` trace in _Fsm.cpp_ + - Correct initialization of `m_num_timed_transitions` + + **2.1.0 - 21/11/2015** * Add timed transitions diff --git a/examples/light_switch/light_switch.ino b/examples/light_switch/light_switch.ino index ac7d1c3..93fcd2e 100644 --- a/examples/light_switch/light_switch.ino +++ b/examples/light_switch/light_switch.ino @@ -1,10 +1,10 @@ -#include +#include "Fsm.h" // State machine variables #define FLIP_LIGHT_SWITCH 1 -State state_light_on(on_light_on_enter, &on_light_on_exit); -State state_light_off(on_light_off_enter, &on_light_off_exit); +State state_light_on(on_light_on_enter, NULL, &on_light_on_exit); +State state_light_off(on_light_off_enter, NULL, &on_light_off_exit); Fsm fsm(&state_light_off); // Transition callback functions @@ -53,6 +53,7 @@ void setup() void loop() { + // No "fsm.run_machine()" call needed as no "on_state" funcions or timmed transitions exists delay(2000); fsm.trigger(FLIP_LIGHT_SWITCH); delay(2000); diff --git a/examples/multitasking/multitasking.ino b/examples/multitasking/multitasking.ino index 11cc94d..79871bc 100644 --- a/examples/multitasking/multitasking.ino +++ b/examples/multitasking/multitasking.ino @@ -2,7 +2,7 @@ // multitasking on an arduino. Two LED's are turned on and off at irregular // intervals; the finite state machines take care of the transitions. -#include +#include "Fsm.h" #define LED1_PIN 10 #define LED2_PIN 11 @@ -27,11 +27,11 @@ void on_led2_off_enter() { digitalWrite(LED2_PIN, LOW); } -State state_led1_on(&on_led1_on_enter, NULL); -State state_led1_off(&on_led1_off_enter, NULL); +State state_led1_on(&on_led1_on_enter, NULL, NULL); +State state_led1_off(&on_led1_off_enter, NULL, NULL); -State state_led2_on(&on_led2_on_enter, NULL); -State state_led2_off(&on_led2_off_enter, NULL); +State state_led2_on(&on_led2_on_enter, NULL, NULL); +State state_led2_off(&on_led2_off_enter, NULL, NULL); Fsm fsm_led1(&state_led1_off); Fsm fsm_led2(&state_led2_off); @@ -50,8 +50,8 @@ void setup() { void loop() { - fsm_led1.check_timer(); - fsm_led2.check_timer(); - - delay(100); + fsm_led1.run_machine(); + fsm_led2.run_machine(); + delay(200); } + diff --git a/examples/timed_switchoff/Fsm.cpp b/examples/timed_switchoff/Fsm.cpp new file mode 100644 index 0000000..9b932ca --- /dev/null +++ b/examples/timed_switchoff/Fsm.cpp @@ -0,0 +1,174 @@ +// This file is part of arduino-fsm. +// +// arduino-fsm is free software: you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// arduino-fsm is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +// for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with arduino-fsm. If not, see . + +#include "Fsm.h" + + +State::State(void (*on_enter)(), void (*on_state)(), void (*on_exit)()) +: on_enter(on_enter), + on_state(on_state), + on_exit(on_exit) +{ +} + + +Fsm::Fsm(State* initial_state) +: m_current_state(initial_state), + m_transitions(NULL), + m_num_transitions(0), + m_num_timed_transitions(0), + m_initialized(false) +{ +} + + +Fsm::~Fsm() +{ + free(m_transitions); + free(m_timed_transitions); + m_transitions = NULL; + m_timed_transitions = NULL; +} + + +void Fsm::add_transition(State* state_from, State* state_to, int event, + void (*on_transition)()) +{ + if (state_from == NULL || state_to == NULL) + return; + + Transition transition = Fsm::create_transition(state_from, state_to, event, + on_transition); + m_transitions = (Transition*) realloc(m_transitions, (m_num_transitions + 1) + * sizeof(Transition)); + m_transitions[m_num_transitions] = transition; + m_num_transitions++; +} + + +void Fsm::add_timed_transition(State* state_from, State* state_to, + unsigned long interval, void (*on_transition)()) +{ + if (state_from == NULL || state_to == NULL) + return; + + Transition transition = Fsm::create_transition(state_from, state_to, 0, + on_transition); + + TimedTransition timed_transition; + timed_transition.transition = transition; + timed_transition.start = 0; + timed_transition.interval = interval; + + m_timed_transitions = (TimedTransition*) realloc( + m_timed_transitions, (m_num_timed_transitions + 1) * sizeof(TimedTransition)); + m_timed_transitions[m_num_timed_transitions] = timed_transition; + m_num_timed_transitions++; +} + + +Fsm::Transition Fsm::create_transition(State* state_from, State* state_to, + int event, void (*on_transition)()) +{ + Transition t; + t.state_from = state_from; + t.state_to = state_to; + t.event = event; + t.on_transition = on_transition; + + return t; +} + +void Fsm::trigger(int event) +{ + if (m_initialized) + { + // Find the transition with the current state and given event. + for (int i = 0; i < m_num_transitions; ++i) + { + if (m_transitions[i].state_from == m_current_state && + m_transitions[i].event == event) + { + Fsm::make_transition(&(m_transitions[i])); + return; + } + } + } +} + +void Fsm::check_timed_transitions() +{ + for (int i = 0; i < m_num_timed_transitions; ++i) + { + TimedTransition* transition = &m_timed_transitions[i]; + if (transition->transition.state_from == m_current_state) + { + if (transition->start == 0) + { + transition->start = millis(); + } + else{ + unsigned long now = millis(); + if (now - transition->start >= transition->interval) + { + Fsm::make_transition(&(transition->transition)); + transition->start = 0; + } + } + } + } +} + +void Fsm::run_machine() +{ + // first run must exec first state "on_enter" + if (!m_initialized) + { + m_initialized = true; + if (m_current_state->on_enter != NULL) + m_current_state->on_enter(); + } + + if (m_current_state->on_state != NULL) + m_current_state->on_state(); + + Fsm::check_timed_transitions(); +} + +void Fsm::make_transition(Transition* transition) +{ + + // Execute the handlers in the correct order. + if (transition->state_from->on_exit != NULL) + transition->state_from->on_exit(); + + if (transition->on_transition != NULL) + transition->on_transition(); + + if (transition->state_to->on_enter != NULL) + transition->state_to->on_enter(); + + m_current_state = transition->state_to; + + //Initialice all timed transitions from m_current_state + unsigned long now = millis(); + for (int i = 0; i < m_num_timed_transitions; ++i) + { + TimedTransition* ttransition = &m_timed_transitions[i]; + if (ttransition->transition.state_from == m_current_state) + ttransition->start = now; + } + +} diff --git a/examples/timed_switchoff/timed_switchoff.ino b/examples/timed_switchoff/timed_switchoff.ino new file mode 100644 index 0000000..657a435 --- /dev/null +++ b/examples/timed_switchoff/timed_switchoff.ino @@ -0,0 +1,71 @@ +#include "Fsm.h" + +/* + * FSM Library sample with user and timed + * transitions. + * Uses a button and Arduino builtin led, + * button can be replaced just grounding + * pin. + */ + +// Used pins +#define LED_PIN 13 +#define BUTTON_PIN 8 + +//Events +#define BUTTON_EVENT 0 + +int buttonState = 0; + +/* state 1: led off + * state 2: led on + * transition from s1 to s2 on button press + * transition back from s2 to s1 after 3 seconds or button press + */ +State state_led_off(&led_off, &check_button, NULL); +State state_led_on(&led_on, &check_button, NULL); +Fsm fsm(&state_led_off); + +// Transition functions +void led_off() +{ + Serial.println("led_off"); + digitalWrite(LED_PIN, LOW); +} + +void led_on() +{ + Serial.println("led_on"); + digitalWrite(LED_PIN, HIGH); +} + +void check_button() +{ + int buttonState = digitalRead(BUTTON_PIN); + if (buttonState == LOW) { + Serial.println("button_pressed"); + fsm.trigger(BUTTON_EVENT); + } +} + +// standard arduino functions +void setup() +{ + Serial.begin(9600); + + pinMode(LED_PIN, OUTPUT); + pinMode(BUTTON_PIN, INPUT_PULLUP); + + fsm.add_transition(&state_led_off, &state_led_on, + BUTTON_EVENT, NULL); + fsm.add_timed_transition(&state_led_on, &state_led_off, 3000, NULL); + fsm.add_transition(&state_led_on, &state_led_off, BUTTON_EVENT, NULL); + Serial.println("Setup END"); +} + +void loop() +{ + // Call fsm run + fsm.run_machine(); + delay(100); +} diff --git a/library.properties b/library.properties index 6322ea2..9b7c873 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=arduino-fsm -version=2.1.0 +version=2.2.0 author=Jon Black maintainer=Jon Black sentence=A library for implementing a finite state machine