├── .gitattributes ├── .gitignore ├── README.md ├── .clang-format ├── examples ├── extern_LED │ ├── .vscode │ │ └── settings.json │ ├── main.cpp │ ├── Atm_led_mcp.h │ └── Atm_led_mcp.cpp ├── blink_modular │ ├── blink_modular.ino │ ├── Atm_blink.h │ └── Atm_blink.cpp ├── fade │ └── fade.ino ├── knight_rider2 │ ├── knight_rider2.ino │ ├── Atm_sweep.h │ └── Atm_sweep.cpp ├── led_test │ └── led_test.ino ├── led_fuel_gauge │ └── led_fuel_gauge.ino ├── sos1 │ └── sos1.ino ├── button │ └── button.ino ├── sos3 │ └── sos3.ino ├── sos2 │ └── sos2.ino ├── knight_rider3 │ └── knight_rider3.ino ├── knight_rider1 │ └── knight_rider1.ino ├── frere_jacques │ ├── frere_jacques.ino │ └── musical_notes.h ├── nuclear_missile_launcher │ └── nuclear_missile_launcher.ino └── blink │ └── blink.ino ├── extras └── update.sh ├── src ├── atm_counter.hpp ├── atm_timer_millis.hpp ├── atm_timer_millis.cpp ├── atm_counter.cpp ├── Atm_command.hpp ├── Atm_analog.hpp ├── atm_serial_debug.hpp ├── Atm_step.hpp ├── Atm_encoder.hpp ├── Atm_digital.hpp ├── Atm_fan.hpp ├── atm_connector.hpp ├── Atm_timer.hpp ├── Atm_bit.hpp ├── Machine.hpp ├── Atm_comparator.hpp ├── Atm_fade.hpp ├── Atm_button.hpp ├── Automaton.cpp ├── Atm_led.hpp ├── Atm_controller.hpp ├── Atm_fan.cpp ├── Atm_analog.cpp ├── Atm_digital.cpp ├── Atm_command.cpp ├── Automaton.h ├── atm_connector.cpp ├── Atm_player.hpp ├── Atm_bit.cpp ├── Atm_encoder.cpp ├── Atm_timer.cpp ├── Atm_comparator.cpp ├── Atm_step.cpp ├── Atm_button.cpp ├── Atm_controller.cpp ├── Atm_fade.cpp ├── Atm_player.cpp ├── Atm_led.cpp └── Machine.cpp ├── library.json ├── library.properties ├── LICENSE ├── platformio.ini └── keywords.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.hex 2 | *.swp 3 | *.dox 4 | .development 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Automaton 2 | 3 | Automaton - Reactive State Machine Framework for Arduino 4 | 5 | Read the [Wiki](https://github.com/tinkerspy/Automaton/wiki) for more information. 6 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | BasedOnStyle: Google 3 | IndentWidth: 2 4 | UseTab: Never 5 | ColumnLimit: 160 6 | SpacesInParentheses: true 7 | AllowShortFunctionsOnASingleLine: false 8 | -------------------------------------------------------------------------------- /examples/extern_LED/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "terminal.integrated.env.linux": { 3 | "PATH": "/home/ian/.platformio/penv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin" 4 | } 5 | } -------------------------------------------------------------------------------- /extras/update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -x 2 | 3 | clang-format -i src/*.h src/*.cpp src/*.hpp 4 | 5 | ( 6 | chmod -x *.cpp *.hpp *.h *.ino 7 | chmod -x */*.cpp */*.hpp */*.h */*.ino 8 | chmod -x */*/*.cpp */*/*.hpp */*/*.h */*/*.ino 9 | ) 2> /dev/null 10 | 11 | git add . 12 | 13 | -------------------------------------------------------------------------------- /examples/blink_modular/blink_modular.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Atm_blink.h" 3 | 4 | Atm_blink led; 5 | 6 | void setup() { 7 | led.begin( 4, 200 ); // Setup a blink machine on pin 4 8 | led.trigger( led.EVT_ON ); // Turn it on 9 | } 10 | 11 | void loop() { 12 | automaton.run(); 13 | } 14 | 15 | -------------------------------------------------------------------------------- /examples/extern_LED/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "Adafruit_MCP23017.h" 4 | 5 | 6 | Adafruit_MCP23017 ioext; 7 | Atm_led_mcp led(ioext); 8 | 9 | 10 | void setup() { 11 | ioext.begin(); 12 | led.begin(6, true); 13 | led.On(); 14 | } 15 | 16 | void loop() { 17 | automaton.run(); 18 | } 19 | -------------------------------------------------------------------------------- /examples/fade/fade.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int led1Pin = 5; 4 | int led2Pin = 6; 5 | 6 | Atm_fade led1, led2; 7 | 8 | void setup() { 9 | led1.begin( led1Pin ).blink( 200 ).fade( 5 ); 10 | led2.begin( led2Pin ).blink( 500 ).fade( 10 ); 11 | led1.start(); 12 | led2.start(); 13 | } 14 | 15 | void loop() { 16 | led1.cycle(); 17 | led2.cycle(); 18 | } 19 | -------------------------------------------------------------------------------- /examples/knight_rider2/knight_rider2.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Atm_sweep.h" 3 | 4 | Atm_sweep sweep; 5 | Atm_button button; 6 | 7 | void setup() { 8 | 9 | sweep.begin( 4, 5, 6, 7, 8, 9 ) 10 | .speed( 50 ) 11 | .trigger( sweep.EVT_START ); 12 | 13 | button.begin( 2 ) 14 | .onPress( sweep, sweep.EVT_TOGGLE ); 15 | 16 | } 17 | 18 | void loop() { 19 | automaton.run(); 20 | } 21 | -------------------------------------------------------------------------------- /src/atm_counter.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Automaton.h - Reactive State Machine Framework for Arduino. 3 | * Published under the MIT License (MIT), Copyright (c) 2015-2016, J.P. van der Landen 4 | * */ 5 | 6 | #pragma once 7 | 8 | #include "Arduino.h" 9 | 10 | class atm_counter { 11 | public: 12 | uint16_t value; 13 | void set( uint16_t v ); 14 | uint8_t expired( void ); 15 | uint16_t decrement( void ); 16 | }; 17 | -------------------------------------------------------------------------------- /src/atm_timer_millis.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Automaton - Reactive State Machine Framework for Arduino. 3 | * Published under the MIT License (MIT), Copyright (c) 2015-2016, J.P. van der Landen 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "Arduino.h" 9 | 10 | class atm_timer_millis { 11 | public: 12 | uint32_t value; 13 | void set( uint32_t v ); 14 | void setFromNow( Machine* machine, uint32_t v ); 15 | int expired( Machine* machine ); 16 | }; 17 | -------------------------------------------------------------------------------- /examples/led_test/led_test.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // This example demonstrates running state machines logging their state changes to the serial port 4 | 5 | int ledPin = LED_BUILTIN; 6 | int buttonPin = 2; 7 | 8 | Atm_led led; 9 | Atm_button button; 10 | 11 | void setup() { 12 | Serial.begin( 9600 ); 13 | led.trace( Serial ); 14 | button.trace( Serial ); 15 | 16 | led.begin( ledPin ) 17 | .blink( 1000 ); 18 | 19 | button.begin( buttonPin ) 20 | .onPress( led, led.EVT_TOGGLE ); 21 | } 22 | 23 | void loop() { 24 | automaton.run(); 25 | } 26 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Automaton", 3 | "version": "1.0.3", 4 | "keywords": "state, machine", 5 | "description": "A multi tasking table driven finite state machine framework for Arduino", 6 | "authors": 7 | { 8 | "name": "Tinkerspy", 9 | "email": "tinkerspy@myown.mailcan.com", 10 | "url": "https://github.com/tinkerspy" 11 | }, 12 | "repository": 13 | { 14 | "type": "git", 15 | "url": "https://github.com/tinkerspy/Automaton" 16 | }, 17 | "frameworks": "arduino", 18 | "platforms": "atmelavr" 19 | } 20 | -------------------------------------------------------------------------------- /examples/blink_modular/Atm_blink.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Atm_blink: public Machine { 6 | 7 | public: 8 | Atm_blink( void ) : Machine() {}; 9 | 10 | short pin; 11 | atm_timer_millis timer; 12 | 13 | enum { IDLE, LED_ON, LED_OFF }; // STATES 14 | enum { EVT_TIMER, EVT_ON, EVT_OFF, ELSE }; // EVENTS 15 | enum { ENT_ON, ENT_OFF }; // ACTIONS 16 | 17 | Atm_blink & begin( int attached_pin, uint32_t blinkrate ); 18 | Atm_blink & trace( Stream & stream ); 19 | int event( int id ); 20 | void action( int id ); 21 | }; 22 | -------------------------------------------------------------------------------- /examples/extern_LED/Atm_led_mcp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Atm_led_mcp.h 3 | * 4 | * Created on: 09.12.2017 5 | * Author: ian 6 | */ 7 | 8 | #ifndef SRC_ATM_LED_MCP_H_ 9 | #define SRC_ATM_LED_MCP_H_ 10 | 11 | #include "Adafruit_MCP23017.h" 12 | #include 13 | 14 | class Atm_led_mcp: public Atm_led { 15 | public: 16 | Atm_led_mcp(Adafruit_MCP23017& _gpio); 17 | 18 | private: 19 | Adafruit_MCP23017& gpio; 20 | 21 | protected: 22 | virtual void initLED(); 23 | virtual void switchOn(); 24 | virtual void switchOff(); 25 | virtual void setBrightness(int value); 26 | }; 27 | 28 | #endif /* SRC_ATM_LED_MCP_H_ */ 29 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Automaton 2 | version=1.0.3 3 | author=Tinkerspy 4 | maintainer=Tinkerspy 5 | sentence=A multi tasking table driven finite state machine framework 6 | paragraph=An event driven framework that allows you to create Arduino applications that consist of concurrently running state machines interacting with each other. Use the bundled machines or create your own following the tutorial. Contains reusable bundled machines for handling leds (fade & blink), buttons, serial commands, analog input (with moving average), pulses and timers and more. 7 | category=Other 8 | url=https://github.com/tinkerspy/Automaton/wiki 9 | architectures=* 10 | -------------------------------------------------------------------------------- /examples/knight_rider2/Atm_sweep.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Atm_sweep: public Machine { 6 | 7 | public: 8 | Atm_sweep( void ) : Machine() {}; 9 | 10 | enum { IDLE, U0, U1, U2, U3, U4, U5, D4, D3, D2, D1, D0 }; 11 | enum { EVT_TIMER, EVT_START, EVT_STOP, EVT_TOGGLE, ELSE }; 12 | enum { ENT_L0, ENT_L1, ENT_L2, ENT_L3, ENT_L4, ENT_L5, ENT_OFF }; 13 | 14 | Atm_sweep & begin( int p1, int p2, int p3, int p4, int p5, int p6 ); 15 | Atm_sweep & speed( uint32_t v ); 16 | Atm_sweep & trace( Stream & stream ); 17 | 18 | private: 19 | short pin[6]; 20 | atm_timer_millis timer; 21 | 22 | int event( int id ); 23 | void action( int id ); 24 | }; 25 | -------------------------------------------------------------------------------- /examples/led_fuel_gauge/led_fuel_gauge.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Turning a pot on A0 will change a led gauge on pins 4, 5, 6, 7, 8, 9 4 | 5 | Atm_comparator cmp; 6 | Atm_led led[6]; 7 | Atm_step step; 8 | 9 | static uint16_t threshold_list[] = { 100, 300, 500, 700, 900, 1000 }; 10 | static short pin_list[] = { 4, 5, 6, 7, 8, 9 }; 11 | 12 | void setup() { 13 | 14 | cmp.begin( A0, 50 ) 15 | .threshold( threshold_list, sizeof( threshold_list ), true ) 16 | .onChange( true, step, Atm_step::EVT_STEP ) 17 | .onChange( false, step, Atm_step::EVT_BACK ); 18 | 19 | step.begin(); 20 | for ( short i = 0; i <= 5; i++ ) { 21 | led[i].begin( pin_list[i] ); 22 | step.onStep( i, led[i], Atm_led::EVT_TOGGLE ); 23 | } 24 | } 25 | 26 | void loop() { 27 | automaton.run(); 28 | } 29 | -------------------------------------------------------------------------------- /src/atm_timer_millis.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "Automaton.h" 3 | 4 | /* 5 | * atm_timer_millis::set( v ) - Sets a millis timer to 'v' 6 | * 7 | */ 8 | 9 | void atm_timer_millis::set( uint32_t v ) { 10 | value = v; 11 | } 12 | 13 | /* 14 | * atm_timer_millis::setFromNow( Machine* machine, uint32_t v ) - 15 | * Sets the timer to be expired v millis from now 16 | */ 17 | void atm_timer_millis::setFromNow( Machine* machine, uint32_t v ) { 18 | value = millis() - machine->state_millis + v; 19 | } 20 | 21 | /* 22 | * atm_timer_millis::expired( this ) - Checks a millis timer for expiry (== 0) 23 | * This is a rollover-safe 32 bit unsigned integer comparison 24 | * 25 | */ 26 | 27 | int atm_timer_millis::expired( Machine* machine ) { 28 | return value == ATM_TIMER_OFF ? 0 : millis() - machine->state_millis >= value; 29 | } 30 | -------------------------------------------------------------------------------- /src/atm_counter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Automaton - Reactive State Machine Framework for Arduino. 3 | Published under the MIT License (MIT), Copyright (c) 2015-2016, J.P. van der Landen 4 | */ 5 | 6 | #include "Automaton.h" 7 | 8 | /* 9 | * atm_counter::set( v ) - Sets a countdown counter to 'v' 10 | * 11 | */ 12 | 13 | void atm_counter::set( uint16_t v ) { 14 | value = v; 15 | } 16 | 17 | /* 18 | * atm_counter::decrement() - Decrements a countdown counter 19 | * 20 | */ 21 | 22 | uint16_t atm_counter::decrement( void ) { 23 | return value > 0 && value != ATM_COUNTER_OFF ? --value : 0; 24 | } 25 | 26 | /* 27 | * atm_counter::expired() - Checks a countdown counter for expiry (== 0) 28 | * 29 | */ 30 | 31 | uint8_t atm_counter::expired() { 32 | return value == ATM_COUNTER_OFF ? 0 : ( value > 0 ? 0 : 1 ); 33 | } 34 | -------------------------------------------------------------------------------- /examples/sos1/sos1.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | Atm_led led; 4 | 5 | const int pin = 4; 6 | const int dotTime = 100; 7 | const int dashTime = 300; 8 | const int waitTime = 200; 9 | const int longwaitTime = 500; 10 | const int longerwaitTime = 1000; 11 | 12 | void setup() { 13 | led.begin( pin ); 14 | } 15 | 16 | void loop() { 17 | // Set the led to blink 3 times and trigger it 18 | led.blink( dotTime, waitTime, 3 ).start(); 19 | 20 | // Loop until the blinking has finished 21 | while ( led.cycle().state() ); 22 | 23 | // Cycle idly for a while 24 | led.cycle( longwaitTime ); 25 | 26 | led.blink( dashTime, waitTime, 3 ).start(); 27 | while ( led.cycle().state() ); 28 | led.cycle( longwaitTime ); 29 | 30 | led.blink( dotTime, waitTime, 3 ).start(); 31 | while ( led.cycle().state() ); 32 | led.cycle( longerwaitTime ); 33 | } 34 | -------------------------------------------------------------------------------- /src/Atm_command.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Atm_command : public Machine { 6 | public: 7 | enum { IDLE, READCHAR, SEND }; 8 | enum { EVT_INPUT, EVT_EOL, ELSE }; 9 | 10 | Atm_command( void ) : Machine(){}; 11 | Atm_command& begin( Stream& stream, char buffer[], int size ); 12 | Atm_command& trace( Stream& stream ); 13 | Atm_command& onCommand( atm_cb_push_t callback, int idx = 0 ); 14 | Atm_command& list( const char* cmds ); 15 | 16 | Atm_command& separator( const char sep[] ); 17 | int lookup( int id, const char* cmdlist ); 18 | char* arg( int id ); 19 | 20 | private: 21 | enum { ENT_READCHAR, ENT_SEND }; 22 | atm_connector oncommand; 23 | Stream* stream; 24 | char* buffer; 25 | int bufsize, bufptr; 26 | char eol, lastch; 27 | const char* separatorChar; 28 | const char* commands; 29 | 30 | int event( int id ); 31 | void action( int id ); 32 | }; 33 | -------------------------------------------------------------------------------- /examples/button/button.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Start by creating a bunch of state machines 4 | 5 | Atm_led led1, led2, led3; // Three Automaton led machines 6 | Atm_button btn; // An Automaton button machine 7 | Atm_fan fan; // To split the trigger in 3 8 | 9 | void setup() { 10 | // Initialize the led machines at different rates 11 | led1.begin( 4 ).blink( 100, 100 ); 12 | led2.begin( 5 ).blink( 200, 200 ); 13 | led3.begin( 6 ).blink( 400, 400 ); 14 | 15 | // Send one event to many 16 | fan.begin() 17 | .onInput( led1, led1.EVT_TOGGLE_BLINK ) 18 | .onInput( led2, led2.EVT_TOGGLE_BLINK ) 19 | .onInput( led3, led3.EVT_TOGGLE_BLINK ); 20 | 21 | // Button triggers the fan 22 | btn.begin( 2 ) 23 | .onPress( fan, fan.EVT_INPUT ); 24 | 25 | // Start the blinking 26 | fan.trigger( fan.EVT_INPUT ); 27 | } 28 | 29 | // Run the app from the Arduino loop() 30 | // Press the button to toggle the leds on and off 31 | 32 | void loop() { 33 | automaton.run(); 34 | } 35 | -------------------------------------------------------------------------------- /examples/sos3/sos3.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | Atm_led led[3]; 4 | Atm_timer timer[3]; 5 | 6 | const int pin = 4; 7 | const int dotTime = 100; 8 | const int dashTime = 300; 9 | const int waitTime = 200; 10 | const int longwaitTime = 300; 11 | const int longerwaitTime = 1000; 12 | 13 | void setup() { 14 | 15 | led[0].begin( pin ) 16 | .blink( dotTime, waitTime, 3 ) 17 | .onFinish( timer[0], Atm_timer::EVT_START ); 18 | timer[0].begin( longwaitTime ) 19 | .onTimer( led[1], Atm_led::EVT_BLINK ); 20 | 21 | led[1].begin( pin ) 22 | .blink( dashTime, waitTime, 3 ) 23 | .onFinish( timer[1], Atm_timer::EVT_START ); 24 | timer[1].begin( longwaitTime ) 25 | .onTimer( led[2], Atm_led::EVT_BLINK ); 26 | 27 | led[2].begin( pin ) 28 | .blink( dotTime, waitTime, 3 ) 29 | .onFinish( timer[2], Atm_timer::EVT_START ); 30 | timer[2].begin( longerwaitTime ) 31 | .onTimer( led[0], Atm_led::EVT_BLINK ); 32 | 33 | led[0].start(); 34 | } 35 | 36 | void loop() { 37 | automaton.run(); 38 | } 39 | -------------------------------------------------------------------------------- /examples/sos2/sos2.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | Atm_led dot, dash; 4 | Atm_timer stepTimer; 5 | Atm_step step; 6 | 7 | const int pin = 4; 8 | const int dotTime = 100; 9 | const int dashTime = 300; 10 | const int waitTime = 200; 11 | const int longwaitTime = 300; 12 | const int longerwaitTime = 1000; 13 | 14 | void setup() { 15 | 16 | // Define two leds (patterns) 17 | dot.begin( pin ).blink( dotTime, waitTime, 3 ); 18 | dash.begin( pin ).blink( dashTime, waitTime, 3 ); 19 | 20 | // Define a timer 21 | stepTimer.begin( 1700 ).repeat( ATM_COUNTER_OFF ); 22 | 23 | // Define a step sequencer and link it to the leds we defined earlier 24 | step.begin() 25 | .onStep( 0, dot, Atm_led::EVT_BLINK ) 26 | .onStep( 1, dash, Atm_led::EVT_BLINK ) 27 | .onStep( 2, dot, Atm_led::EVT_BLINK ); 28 | 29 | // Set up the timer to drive the step sequencer 30 | stepTimer.onTimer( step, Atm_step::EVT_STEP ); 31 | 32 | // Start the timer 33 | stepTimer.start(); 34 | } 35 | 36 | void loop() { 37 | automaton.run(); 38 | } 39 | -------------------------------------------------------------------------------- /examples/knight_rider3/knight_rider3.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | Atm_player player; // A player machine 4 | 5 | const int ledPinMin = 4; // Use pins 4..9 6 | const int ledPinMax = 9; 7 | 8 | int pattern[] = { // Bitmapped pattern 9 | 0b00100000, 100, 0, 10 | 0b00010000, 100, 0, 11 | 0b00001000, 100, 0, 12 | 0b00000100, 100, 0, 13 | 0b00000010, 100, 0, 14 | 0b00000001, 100, 0, 15 | 0b00000010, 100, 0, 16 | 0b00000100, 100, 0, 17 | 0b00001000, 100, 0, 18 | 0b00010000, 100, 0, 19 | }; 20 | 21 | void setup() { 22 | player.begin() 23 | .play( pattern, sizeof( pattern ) ) // Set up the pattern 24 | .onNote( true, []( int idx, int v, int up ) { // Called on every note 25 | for ( int i = ledPinMin; i <= ledPinMax; i++ ) { 26 | pinMode( i, OUTPUT ); // LED on/off according to bit 27 | digitalWrite( i, v & ( 1 << ( i - ledPinMin ) ) ? HIGH : LOW ); 28 | } 29 | }) 30 | .repeat( -1 ) // Repeat forever 31 | .start(); // Kickoff! 32 | } 33 | 34 | void loop() { 35 | automaton.run(); 36 | } 37 | 38 | -------------------------------------------------------------------------------- /src/Atm_analog.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Atm_analog : public Machine { 6 | public: 7 | enum { IDLE, SAMPLE, SEND }; // STATES 8 | enum { EVT_TRIGGER, EVT_TIMER, ELSE }; // EVENTS 9 | 10 | Atm_analog( void ) : Machine(){}; 11 | Atm_analog& begin( int attached_pin, int sampleRate = 50 ); 12 | Atm_analog& average( uint16_t* v, uint16_t size ); 13 | Atm_analog& trace( Stream& stream ); 14 | int state( void ); 15 | Atm_analog& range( int toLow, int toHigh ); 16 | Atm_analog& onChange( Machine& machine, int event = 0 ); 17 | Atm_analog& onChange( atm_cb_push_t callback, int idx = 0 ); 18 | Atm_analog& set( int value ); 19 | 20 | private: 21 | enum { ENT_SAMPLE, ENT_SEND }; // ACTIONS 22 | short pin; 23 | atm_timer_millis timer; 24 | int v_sample, v_threshold, v_previous; 25 | atm_connector onchange; 26 | uint16_t* avg_buf; 27 | uint16_t avg_buf_size; 28 | uint16_t avg_buf_head; 29 | uint32_t avg_buf_total; 30 | int toLow, toHigh; 31 | 32 | int avg(); 33 | int sample(); 34 | virtual int read_sample(); 35 | int event( int id ); 36 | void action( int id ); 37 | }; 38 | -------------------------------------------------------------------------------- /src/atm_serial_debug.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Automaton.h - Reactive State Machine Framework for Arduino. 3 | * Published under the MIT License (MIT), Copyright (c) 2015-2016, J.P. van der Landen 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "Arduino.h" 9 | 10 | class atm_serial_debug { // It seems necessary to put this code in .h to keep it from being compiled in unnecessarily 11 | public: 12 | static void trace( Stream* stream, Machine& machine, const char label[], const char current[], const char next[], const char trigger[], uint32_t runtime, 13 | uint32_t cycles ) { 14 | stream->print( millis() ); 15 | stream->print( " Switch " ); 16 | stream->print( label ); 17 | stream->print( "@" ); 18 | stream->print( (long)&machine, HEX ); 19 | stream->print( " from " ); 20 | stream->print( current ); 21 | stream->print( " to " ); 22 | stream->print( next ); 23 | stream->print( " on " ); 24 | stream->print( trigger ); 25 | stream->print( " (" ); 26 | stream->print( cycles ); 27 | stream->print( " cycles in " ); 28 | stream->print( runtime ); 29 | stream->println( " ms)" ); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /examples/extern_LED/Atm_led_mcp.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Atm_led_mcp.cpp 3 | * 4 | * Created on: 09.12.2017 5 | * Author: ian 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | Atm_led_mcp::Atm_led_mcp(Adafruit_MCP23017& _gpio): 12 | Atm_led(), // base class ctor would also be called implicitly, but better style to mention this explicitly :) 13 | gpio(_gpio) { 14 | // nothing to see here 15 | } 16 | 17 | void Atm_led_mcp::initLED() { 18 | gpio.pinMode(pin, OUTPUT); 19 | gpio.digitalWrite(pin, activeLow ? HIGH : LOW); 20 | Serial.printf("LED init on pin %c%x\n", activeLow?'~':' ', pin); 21 | } 22 | 23 | void Atm_led_mcp::switchOn() { 24 | Serial.printf("LED ON on pin %c%x\n", activeLow?'~':' ', pin); 25 | gpio.digitalWrite(pin, !activeLow); 26 | } 27 | 28 | void Atm_led_mcp::switchOff() { 29 | Serial.printf("LED OFF on pin %c%x\n", activeLow?'~':' ', pin); 30 | gpio.digitalWrite(pin, activeLow); 31 | } 32 | 33 | void Atm_led_mcp::setBrightness(int value) { 34 | if (value == toHigh) switchOn(); else if(value==toLow) switchOff(); else 35 | Serial.printf("ERROR: Setting brightness on GPIO expander is not possible (pin: %d)\n", pin); 36 | } 37 | -------------------------------------------------------------------------------- /src/Atm_step.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define ATM_STEP_MAX 10 6 | 7 | class Atm_step : public Machine { 8 | public: 9 | // clang-format off 10 | enum { LINEAR, S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, 11 | SWEEP, X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, XA, XB, XC, XD, XE, XF, XG, XH }; 12 | enum { EVT_STEP, EVT_BACK, EVT_SWEEP, EVT_LINEAR, ELSE }; 13 | // clang-format on 14 | 15 | Atm_step( void ) : Machine(){}; 16 | Atm_step& begin( void ); 17 | int state( void ); 18 | Atm_step& trace( Stream& stream ); 19 | Atm_step& onStep( uint8_t idx ); // fix id -> step??? 20 | Atm_step& onStep( uint8_t id, atm_cb_push_t callback, int idx = 0 ); 21 | Atm_step& onStep( uint8_t id, Machine& machine, int event = 0 ); 22 | Atm_step& onStep( atm_cb_push_t callback, int idx = 0 ); 23 | Atm_step& onStep( Machine& machine, int event = 0 ); 24 | 25 | private: 26 | enum { ENT_S0, ENT_S1, ENT_S2, ENT_S3, ENT_S4, ENT_S5, ENT_S6, ENT_S7, ENT_S8, ENT_S9 }; 27 | atm_connector connector[ATM_STEP_MAX]; 28 | atm_connector onstep; 29 | int event( int id ); 30 | void action( int id ); 31 | }; 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2015-2016, J.P. van der Landen 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | 25 | -------------------------------------------------------------------------------- /src/Atm_encoder.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Atm_encoder : public Machine { 6 | public: 7 | enum { IDLE, UP, DOWN }; // STATES 8 | enum { EVT_UP, EVT_DOWN, ELSE }; // EVENTS 9 | 10 | Atm_encoder( void ) : Machine(){}; 11 | Atm_encoder& begin( int pin1, int pin2, int divider = 1 ); 12 | Atm_encoder& trace( Stream& stream ); 13 | Atm_encoder& onChange( Machine& machine, int event = 0 ); 14 | Atm_encoder& onChange( atm_cb_push_t callback, int idx = 0 ); 15 | Atm_encoder& onChange( bool status, Machine& machine, int event = 0 ); 16 | Atm_encoder& onChange( bool status, atm_cb_push_t callback, int idx = 0 ); 17 | int state( void ); 18 | Atm_encoder& range( int min, int max, bool wrap = false ); 19 | Atm_encoder& set( int value ); 20 | 21 | private: 22 | enum { LP_IDLE, ENT_UP, ENT_DOWN }; // ACTIONS 23 | short pin1, pin2; 24 | const static char enc_states[]; 25 | uint8_t enc_bits; 26 | int8_t enc_counter; 27 | int8_t enc_direction; 28 | int divider; 29 | int value, min, max; 30 | bool wrap, range_invert; 31 | atm_connector onup, ondown; 32 | 33 | bool count( int direction ); 34 | int event( int id ); 35 | void action( int id ); 36 | }; 37 | -------------------------------------------------------------------------------- /examples/knight_rider1/knight_rider1.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | Atm_led led[6]; 4 | Atm_timer timer; 5 | Atm_step step; 6 | Atm_analog pot; 7 | 8 | // Timer drives step sequencer in sweep mode, step sequencer blinks leds. 9 | 10 | short event = Atm_led::EVT_BLINK; 11 | static short pin_list[] = { 4, 5, 6, 7, 8, 9 }; 12 | int blink_time = 70; 13 | int interval_time = 50; 14 | 15 | void setup() { 16 | // Initialize the step sequencer 17 | 18 | step.begin() 19 | .trigger( Atm_step::EVT_SWEEP ); 20 | 21 | // Add the timer 22 | timer.begin( interval_time ) 23 | .onTimer( step, Atm_step::EVT_STEP ) 24 | .repeat( ATM_COUNTER_OFF ); 25 | 26 | // Add the leds and link them to the step sequencer 27 | for ( short i = 0; i <= 5; i++ ) { 28 | led[i].begin( pin_list[i] ) 29 | .blink( blink_time, 1, 1 ); 30 | step.onStep( i, led[i], event ); 31 | } 32 | 33 | pot.begin( A0 ) 34 | .range( 10, 200 ) 35 | .onChange( [] ( int idx, int v, int up ) { 36 | timer.interval( v ); 37 | }); 38 | 39 | // Move last led from step 5 to step 7 to make sweep work properly! 40 | step.onStep( 5 ).onStep( 9, led[5], event ); 41 | timer.start(); 42 | } 43 | 44 | void loop() { 45 | automaton.run(); 46 | } 47 | -------------------------------------------------------------------------------- /src/Atm_digital.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // Digital pin with a minimum duration in ms 6 | // On detection another machine is messaged or a callback is fired 7 | 8 | class Atm_digital : public Machine { 9 | public: 10 | enum { IDLE, WAITH, VHIGH, WAITL, VLOW }; // STATES 11 | enum { EVT_TIMER, EVT_HIGH, EVT_LOW, ELSE }; // EVENTS 12 | 13 | Atm_digital( void ) : Machine(){}; 14 | Atm_digital& begin( int pin, int debounce = 20, bool activeLow = false, bool pullUp = false ); 15 | int state( void ); 16 | Atm_digital& onChange( bool status, atm_cb_push_t callback, int idx = 0 ); 17 | Atm_digital& onChange( bool status, Machine& machine, int event = 0 ); 18 | Atm_digital& onChange( atm_cb_push_t callback, int idx = 0 ); 19 | Atm_digital& onChange( Machine& machine, int event = 0 ); 20 | Atm_digital& led( int led, bool activeLow = false ); 21 | Atm_digital& trace( Stream& stream ); 22 | 23 | private: 24 | enum { ENT_HIGH, ENT_LOW }; // ACTIONS 25 | enum { ON_CHANGE_FALSE, ON_CHANGE_TRUE, _CONN_SIZE_ }; // CONNECTORS 26 | short pin; 27 | atm_timer_millis timer; 28 | bool activeLow; 29 | atm_connector connection[_CONN_SIZE_]; 30 | int8_t indicator; 31 | bool indicatorActiveLow; 32 | 33 | int event( int id ); 34 | void action( int id ); 35 | }; 36 | -------------------------------------------------------------------------------- /examples/blink_modular/Atm_blink.cpp: -------------------------------------------------------------------------------- 1 | #include "Atm_blink.h" 2 | 3 | Atm_blink & Atm_blink::begin( int attached_pin, uint32_t blinkrate ) { 4 | const static state_t state_table[] PROGMEM = { 5 | /* ON_ENTER ON_LOOP ON_EXIT EVT_TIMER EVT_ON EVT_OFF ELSE */ 6 | /* IDLE */ ENT_OFF, -1, -1, -1, LED_ON, -1, -1, 7 | /* LED_ON */ ENT_ON, -1, -1, LED_OFF, -1, IDLE, -1, 8 | /* LED_OFF */ ENT_OFF, -1, -1, LED_ON, -1, IDLE, -1, 9 | }; 10 | Machine::begin( state_table, ELSE ); 11 | pin = attached_pin; 12 | timer.set( blinkrate ); 13 | pinMode( pin, OUTPUT ); 14 | return *this; 15 | } 16 | 17 | int Atm_blink::event( int id ) { 18 | switch ( id ) { 19 | case EVT_TIMER : 20 | return timer.expired( this ); 21 | } 22 | return 0; 23 | } 24 | 25 | void Atm_blink::action( int id ) { 26 | switch ( id ) { 27 | case ENT_ON : 28 | digitalWrite( pin, HIGH ); 29 | return; 30 | case ENT_OFF : 31 | digitalWrite( pin, LOW ); 32 | return; 33 | } 34 | } 35 | 36 | Atm_blink & Atm_blink::trace( Stream & stream ) { 37 | Machine::setTrace( &stream, atm_serial_debug::trace, 38 | "BLINK\0EVT_TIMER\0EVT_ON\0EVT_OFF\0ELSE\0IDLE\0LED_ON\0LED_OFF" ); 39 | return *this; 40 | } 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/Atm_fan.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Atm_fan : public Machine { 6 | public: 7 | enum { IDLE, SEND }; // STATES 8 | enum { EVT_INPUT, ELSE }; // EVENTS 9 | Atm_fan( void ) : Machine(){}; 10 | Atm_fan& begin( void ); 11 | Atm_fan& trace( Stream& stream ); 12 | Atm_fan& trigger( int event ); 13 | int state( void ); 14 | Atm_fan& onInput( Machine& machine, int event = 0 ); 15 | Atm_fan& onInput( atm_cb_push_t callback, int idx = 0 ); 16 | 17 | private: 18 | enum { ENT_SEND }; // ACTIONS 19 | enum { ON_INPUT, CONN_MAX = 4 }; // CONNECTORS 20 | atm_connector connectors[CONN_MAX]; 21 | int event( int id ); 22 | void action( int id ); 23 | }; 24 | 25 | /* 26 | Automaton::ATML::begin - Automaton Markup Language 27 | 28 | 29 | 30 | 31 | 32 | 33 | SEND 34 | 35 | 36 | IDLE 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | Automaton::ATML::end 51 | */ 52 | -------------------------------------------------------------------------------- /src/atm_connector.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Arduino.h" 4 | 5 | typedef void ( *atm_cb_push_t )( int idx, int v, int up ); 6 | typedef bool ( *atm_cb_pull_t )( int idx ); 7 | 8 | class atm_connector { 9 | public: 10 | enum { MODE_NULL, MODE_PUSHCB, MODE_PULLCB, MODE_MACHINE }; // bits 0, 1, 2 - Mode 11 | enum { LOG_AND, LOG_OR, LOG_XOR }; // bits 3, 4 - Logical operator 12 | enum { REL_NULL, REL_EQ, REL_NEQ, REL_LT, REL_GT, REL_LTE, REL_GTE }; // bits 5, 6, 7 - Relational operator 13 | uint8_t mode_flags; 14 | union { 15 | struct { 16 | union { 17 | atm_cb_push_t push_callback; 18 | atm_cb_pull_t pull_callback; 19 | }; 20 | int callback_idx; // +2 = 5 bytes per connector/object 21 | }; 22 | struct { 23 | union { 24 | Machine* machine; 25 | }; 26 | int event; 27 | }; 28 | }; 29 | void set( Machine* m, int evt, int8_t logOp = 0, int8_t relOp = 0 ); 30 | void set( atm_cb_push_t callback, int idx, int8_t logOp = 0, int8_t relOp = 0 ); 31 | void set( atm_cb_pull_t callback, int idx, int8_t logOp = 0, int8_t relOp = 0 ); 32 | bool push( int v = 0, int up = 0, bool overrideCallback = false ); // returns false (only) if callback is set! 33 | int pull( int v = 0, int up = 0, bool def_value = false ); 34 | int8_t logOp( void ); 35 | int8_t relOp( void ); 36 | int8_t mode( void ); 37 | }; 38 | -------------------------------------------------------------------------------- /examples/frere_jacques/frere_jacques.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include "musical_notes.h" 3 | 4 | // Playback 'Frere Jacques' with button trigger and speed control 5 | 6 | Atm_player player; 7 | Atm_button button; 8 | Atm_analog speed; 9 | 10 | int pattern[] = { 11 | _G4, _N04, 0, _A4, _N04, 0, _B4, _N04, 0, _G4, _N04, 0, // Frere Jacques 12 | _G4, _N04, 0, _A4, _N04, 0, _B4, _N04, 0, _G4, _N04, 0, 13 | _B4, _N04, 0, _C5, _N04, 0, _D5, _N04, _N04, // Dormez vous? 14 | _B4, _N04, 0, _C5, _N04, 0, _D5, _N04, _N04, 15 | _D5, _N08, 0, _E5, _N08, 0, _D5, _N08, 0, _C5, _N08, 0, _B4, _N04, 0, _G4, _N04, 0, // Sonnez les matines 16 | _D5, _N08, 0, _E5, _N08, 0, _D5, _N08, 0, _C5, _N08, 0, _B4, _N04, 0, _G4, _N04, 0, 17 | _G4, _N04, 0, _D4, _N04, 0, _G4, _N04, _N04, // Ding dang dong 18 | _G4, _N04, 0, _D4, _N04, 0, _G4, _N04, _N04, 19 | }; 20 | 21 | void setup() { 22 | player.begin( 19 ) // A passive buzzer or speaker on pin 19 23 | .play( pattern, sizeof( pattern ) ) 24 | .repeat( -1 ); 25 | 26 | button.begin( 2 ) // A button on pin 2 toggles playback on and off 27 | .onPress( player, player.EVT_TOGGLE ); 28 | 29 | speed.begin( A0 ) // An analog pot on pin A0 controls playback speed 30 | .range( 50, 300 ) // From 50% to 300% of original speed 31 | .onChange( []( int idx, int v, int up ) { 32 | player.speed( v ); 33 | }); 34 | } 35 | 36 | void loop() { 37 | automaton.run(); 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/Atm_timer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Atm_timer : public Machine { 6 | public: 7 | enum { IDLE, START, WAITD, WAITMS, TRIGGER, FINISH }; 8 | enum { EVT_DAYCNT, EVT_DAYTIMER, EVT_MSTIMER, EVT_REPCNT, EVT_STOP, EVT_START, EVT_TOGGLE, ELSE }; // EVT_PAUSE, EVT_RESUME 9 | 10 | Atm_timer( void ) : Machine(){}; 11 | Atm_timer& begin( uint32_t ms = 0, uint16_t repeats = 1 ); 12 | Atm_timer& trace( Stream& stream ); 13 | Atm_timer& onTimer( atm_cb_push_t callback, int idx = 0 ); 14 | Atm_timer& onTimer( Machine& machine, int event = 0 ); 15 | Atm_timer& onFinish( atm_cb_push_t callback, int idx = 0 ); 16 | Atm_timer& onFinish( Machine& machine, int event = 0 ); 17 | Atm_timer& interval_seconds( uint32_t v ); 18 | Atm_timer& interval_millis( uint32_t v ); 19 | Atm_timer& interval( uint32_t v ); 20 | uint32_t left(); 21 | Atm_timer& repeat( uint16_t v = ATM_COUNTER_OFF ); 22 | Atm_timer& start( void ); 23 | Atm_timer& stop( void ); 24 | Atm_timer& toggle( void ); 25 | // Atm_timer& pause( void ); TODO 26 | // Atm_timer& resume( void ); 27 | 28 | 29 | private: 30 | enum { ENT_START, ENT_TRIGGER, EXT_WAITD, ENT_FINISH }; 31 | atm_timer_millis daytimer, mstimer; 32 | atm_counter daycounter, repcounter; 33 | uint32_t abscounter; 34 | uint16_t days; 35 | uint16_t repeat_cnt; 36 | atm_connector ontimer, onfinish; 37 | 38 | int event( int id ); 39 | void action( int id ); 40 | }; 41 | -------------------------------------------------------------------------------- /src/Atm_bit.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Atm_bit : public Machine { 6 | public: 7 | enum { OFF, ON, REFR_ON, REFR_OFF }; // STATES 8 | enum { EVT_ON, EVT_OFF, EVT_TOGGLE, EVT_INPUT, EVT_REFRESH, ELSE }; // EVENTS 9 | 10 | Atm_bit( void ) : Machine(){}; 11 | Atm_bit& begin( bool initialState = false ); 12 | Atm_bit& onChange( atm_cb_push_t callback, int idx = 0 ); 13 | Atm_bit& onChange( Machine& machine, int event = 0 ); 14 | Atm_bit& onChange( bool status, atm_cb_push_t callback, int idx = 0 ); 15 | Atm_bit& onChange( bool status, Machine& machine, int event = 0 ); 16 | Atm_bit& onInput( bool status, atm_cb_push_t callback, int idx = 0 ); 17 | Atm_bit& onInput( bool status, Machine& machine, int event = 0 ); 18 | Atm_bit& led( int led, bool activeLow = false ); 19 | Atm_bit& on( void ); 20 | Atm_bit& off( void ); 21 | Atm_bit& toggle( void ); 22 | Atm_bit& input( void ); 23 | Atm_bit& refresh( void ); 24 | Atm_bit& trace( Stream& stream ); 25 | 26 | private: 27 | enum { ENT_ON, ENT_OFF, ENT_REFR_ON, ENT_REFR_OFF }; // ACTIONS 28 | enum { ON_CHANGE_FALSE, ON_CHANGE_TRUE, ON_INPUT_FALSE, ON_INPUT_TRUE, _CONN_SIZE_ }; // CONNECTORS 29 | state_t last_state; 30 | atm_connector connector[_CONN_SIZE_]; 31 | int8_t indicator; 32 | bool indicatorActiveLow; 33 | 34 | int event( int id ); 35 | void action( int id ); 36 | }; 37 | -------------------------------------------------------------------------------- /src/Machine.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Automaton.h - Reactive State Machine Framework for Arduino. 3 | Published under the MIT License (MIT), Copyright (c) 2015-2016, J.P. van der Landen 4 | */ 5 | 6 | #pragma once 7 | 8 | class Machine { 9 | public: 10 | virtual int state( void ); 11 | virtual Machine& trigger( int evt = 0 ); 12 | Machine& cycle( uint32_t time = 0 ); 13 | uint32_t state_millis; 14 | uint8_t flags = ATM_SLEEP_FLAG; 15 | state_t next_trigger = -1; 16 | uint8_t sleep( int8_t v = -1 ); 17 | virtual int event( int id ) = 0; // Pure virtual methods -> make this an abstract class 18 | virtual void action( int id ) = 0; 19 | Machine* inventory_next; 20 | 21 | protected: 22 | Machine& state( int state ); 23 | Machine& begin( const state_t tbl[], int width ); 24 | const char* mapSymbol( int id, const char map[] ); 25 | Machine& setTrace( Stream* stream, swcb_sym_t callback, const char symbols[] ); 26 | void onPush( atm_connector connectors[], int id, int sub, int slots, int fill, Machine& machine, int event ); 27 | void onPush( atm_connector connectors[], int id, int sub, int slots, int fill, atm_cb_push_t callback, int idx ); 28 | void push( atm_connector connectors[], int id, int sub, int v, int up ); 29 | 30 | const state_t* state_table; 31 | state_t next; 32 | state_t current = -1; 33 | state_t last_trigger = -1; 34 | const char* symbols; 35 | uint8_t state_width; 36 | swcb_sym_t callback_trace; 37 | Stream* stream_trace; 38 | uint32_t cycles; 39 | }; 40 | -------------------------------------------------------------------------------- /src/Atm_comparator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Atm_comparator : public Machine { 6 | public: 7 | enum { IDLE, SAMPLE, SEND }; // STATES 8 | enum { EVT_TRIGGER, EVT_TIMER, ELSE }; // EVENTS 9 | 10 | Atm_comparator( void ) : Machine(){}; 11 | Atm_comparator& begin( int attached_pin, int sampleRate = 50 ); 12 | Atm_comparator& threshold( uint16_t* v, uint16_t size, bool catchUp = false ); 13 | Atm_comparator& average( uint16_t* v, uint16_t size ); 14 | Atm_comparator& skip(); 15 | Atm_comparator& onChange( atm_cb_push_t callback, int idx = 0 ); 16 | Atm_comparator& onChange( Machine& machine, int event = 0 ); 17 | Atm_comparator& onChange( bool status, atm_cb_push_t callback, int idx = 0 ); 18 | Atm_comparator& onChange( bool status, Machine& machine, int event = 0 ); 19 | int state( void ); 20 | virtual int read_sample(); 21 | Atm_comparator& trace( Stream& stream ); 22 | 23 | private: 24 | enum { ENT_SAMPLE, ENT_SEND }; // ACTIONS 25 | short pin; 26 | atm_timer_millis timer; 27 | int v_sample, v_threshold, v_previous; 28 | uint64_t bitmap_sample, bitmap_previous, bitmap_diff; 29 | uint16_t* p_threshold; // Max 64 values 30 | uint16_t p_threshold_size; 31 | uint16_t* avg_buf; 32 | uint16_t avg_buf_size; 33 | uint16_t avg_buf_head; 34 | uint32_t avg_buf_total; 35 | atm_connector onup, ondown; 36 | int skip_mode; 37 | 38 | int avg(); 39 | Atm_comparator& bitmap( uint16_t v ); 40 | int sample(); 41 | int event( int id ); 42 | void action( int id ); 43 | }; 44 | -------------------------------------------------------------------------------- /examples/frere_jacques/musical_notes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Note durations in milliseconds 4 | 5 | #define _N01 1600 6 | #define _N02 800 7 | #define _N04 400 8 | #define _N08 200 9 | #define _N16 100 10 | 11 | // Tone frequencies 12 | 13 | #define _C3 130 14 | #define _Db3 138 15 | #define _D3 146 16 | #define _Eb3 155 17 | #define _E3 164 18 | #define _F3 174 19 | #define _Gb3 185 20 | #define _G3 196 21 | #define _Ab3 207 22 | #define _A3 220 23 | #define _Bb3 233 24 | #define _B3 246 25 | 26 | #define _C4 261 27 | #define _Db4 277 28 | #define _D4 293 29 | #define _Eb4 311 30 | #define _E4 329 31 | #define _F4 349 32 | #define _Gb4 369 33 | #define _G4 392 34 | #define _Ab4 415 35 | #define _A4 440 36 | #define _Bb4 466 37 | #define _B4 493 38 | 39 | #define _C5 523 40 | #define _Db5 544 41 | #define _D5 587 42 | #define _Eb5 622 43 | #define _E5 659 44 | #define _F5 698 45 | #define _Gb5 739 46 | #define _G5 783 47 | #define _Ab5 830 48 | #define _A5 880 49 | #define _Bb5 932 50 | #define _B5 987 51 | 52 | #define _C6 1046 53 | #define _Db6 1108 54 | #define _D6 1174 55 | #define _Eb6 1244 56 | #define _E6 1318 57 | #define _F6 1396 58 | #define _Gb6 1479 59 | #define _G6 1567 60 | #define _Ab6 1661 61 | #define _A6 1760 62 | #define _Bb6 1864 63 | #define _B6 1975 64 | 65 | #define _C7 2093 66 | #define _Db7 2217 67 | #define _D7 2349 68 | #define _Eb7 2489 69 | #define _E7 2637 70 | #define _F7 2794 71 | #define _Gb7 2960 72 | #define _G7 3136 73 | #define _Ab7 3322 74 | #define _A7 3520 75 | #define _Bb7 3729 76 | #define _B7 3951 77 | 78 | -------------------------------------------------------------------------------- /examples/nuclear_missile_launcher/nuclear_missile_launcher.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Safe controller for a Nuclear Missile Launcher 4 | 5 | Atm_led countdown, ignition; 6 | Atm_button button1, button2; 7 | Atm_bit bit1, bit2; 8 | Atm_timer timer1, timer2; 9 | Atm_controller ctrl; 10 | 11 | const int pinButton1 = 2; 12 | const int pinButton2 = 3; 13 | const int pinCountdownLed = 8; 14 | const int pinIgnitionLed = 9; 15 | const int buttonIntervalMax = 2000; 16 | const int countdownCount = 10; 17 | const int countdownFlashOn = 100; 18 | const int countdownFlashOff = 900; 19 | 20 | void setup() { 21 | // Self resetting button 1 22 | button1.begin( pinButton1 ) 23 | .onPress( bit1, bit1.EVT_ON ); 24 | 25 | timer1.begin( buttonIntervalMax ) 26 | .onTimer( bit1, bit1.EVT_OFF ); 27 | 28 | bit1.begin( false ).led( 4 ) 29 | .onChange( true, timer1, timer1.EVT_START ); 30 | 31 | // Self resetting button 2 32 | button2.begin( pinButton2 ) 33 | .onPress( bit2, bit2.EVT_ON ); 34 | 35 | timer2.begin( buttonIntervalMax ) 36 | .onTimer( bit2, bit2.EVT_OFF ); 37 | 38 | bit2.begin( false ).led( 5 ) 39 | .onChange( true, timer2, timer2.EVT_START ); 40 | 41 | // Controller 42 | ctrl.begin( false ) 43 | .IF( bit1 ).AND( bit2 ) 44 | .onChange( true, countdown, countdown.EVT_BLINK ); 45 | 46 | // Countdown led 47 | countdown.begin( pinCountdownLed ) 48 | .blink( countdownFlashOn, countdownFlashOff, countdownCount ) 49 | .onFinish( ignition, ignition.EVT_ON ); 50 | 51 | // Ignition 52 | ignition.begin( pinIgnitionLed ); 53 | } 54 | 55 | void loop() { 56 | automaton.run(); 57 | } 58 | -------------------------------------------------------------------------------- /src/Atm_fade.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Atm_fade : public Machine { 6 | public: 7 | enum { IDLE, ON, START, STARTU, UP, STARTD, DOWN, REPEAT, DONE, OSTARTU, OUP, OSTARTD, ODOWN }; 8 | enum { EVT_CNT_FADE, EVT_TM_FADE, EVT_TM_ON, EVT_TM_OFF, EVT_CNT_RPT, EVT_ON, EVT_OFF, EVT_BLINK, EVT_TOGGLE, EVT_TOGGLE_BLINK, ELSE }; 9 | enum { EVT_START = EVT_BLINK }; 10 | 11 | Atm_fade( void ) : Machine(){}; 12 | Atm_fade& begin( int attached_pin ); 13 | Atm_fade& trace( Stream& stream ); 14 | Atm_fade& blink( uint32_t duration, uint32_t pause_duration, uint16_t repeat_count = ATM_COUNTER_OFF ); 15 | Atm_fade& blink( uint32_t duration ); 16 | Atm_fade& blink( void ); 17 | Atm_fade& pause( uint32_t duration ); 18 | Atm_fade& fade( int fade ); 19 | Atm_fade& repeat( uint16_t repeat ); 20 | Atm_fade& on( void ); 21 | Atm_fade& off( void ); 22 | Atm_fade& toggle( void ); 23 | Atm_fade& toggleBlink( void ); 24 | Atm_fade& start( void ); 25 | Atm_fade& onFinish( Machine& machine, int event = 0 ); 26 | Atm_fade& onFinish( atm_cb_push_t callback, int idx = 0 ); 27 | 28 | private: 29 | enum { ENT_REPEAT, ENT_OFF, ENT_ON, ENT_UP, ENT_DOWN, ENT_START, ENT_DONE }; 30 | static const uint8_t SLOPE_SIZE = 32; 31 | uint8_t slope[SLOPE_SIZE] = {0, 0, 1, 1, 2, 2, 3, 4, 5, 6, 7, 8, 10, 12, 15, 18, 22, 26, 31, 37, 44, 54, 63, 76, 90, 108, 127, 153, 180, 217, 230, 255}; 32 | short pin; 33 | uint16_t repeat_count; 34 | atm_connector onfinish; 35 | atm_timer_millis timer_fade, timer_on, timer_off; 36 | atm_counter counter_fade, counter_repeat; 37 | int event( int id ); 38 | void action( int id ); 39 | }; 40 | -------------------------------------------------------------------------------- /src/Atm_button.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Atm_button : public Machine { 6 | public: 7 | enum { IDLE, WAIT, PRESSED, REPEAT, RELEASE, LIDLE, LWAIT, LPRESSED, LRELEASE, WRELEASE, AUTO_ST }; 8 | enum { EVT_LMODE, EVT_TIMER, EVT_DELAY, EVT_REPEAT, EVT_PRESS, EVT_RELEASE, EVT_COUNTER, EVT_AUTO, ELSE }; 9 | enum { BTN_PASS4 = -4, BTN_PASS3 = -3, BTN_PASS2 = -2, BTN_PASS1 = -1, BTN_RELEASE = 0, BTN_PRESS1 = 1, BTN_PRESS2 = 2, BTN_PRESS3 = 3, BTN_PRESS4 = 4 }; 10 | 11 | Atm_button( void ) : Machine(){}; 12 | Atm_button& begin( int attached_pin ); 13 | Atm_button& trace( Stream& stream ); 14 | Atm_button& onPress( atm_cb_push_t callback, int idx = 0 ); 15 | Atm_button& onPress( Machine& machine, int event = 0 ); 16 | Atm_button& onPress( int id, atm_cb_push_t callback, int idx = 0 ); 17 | Atm_button& onPress( int id, Machine& machine, int event = 0 ); 18 | Atm_button& onRelease( atm_cb_push_t callback, int idx = 0 ); 19 | Atm_button& onRelease( Machine& machine, int event = 0 ); 20 | Atm_button& debounce( int delay ); 21 | Atm_button& longPress( int max, int delay ); 22 | Atm_button& repeat( int delay = 500, int speed = 50 ); 23 | Atm_button& autoPress( int delay, int press = 1 ); 24 | 25 | protected: 26 | enum { ENT_PRESS, ENT_RELEASE, ENT_LSTART, ENT_LCOUNT, ENT_LRELEASE, EXT_WRELEASE, ENT_AUTO }; 27 | static const int DEBOUNCE = 5; 28 | atm_connector onpress, onrelease; 29 | atm_connector longpress[2]; 30 | short pin; 31 | atm_timer_millis timer_debounce, timer_delay, timer_repeat, timer_auto; 32 | atm_counter counter_longpress; 33 | int longpress_max; 34 | int auto_press = 1; 35 | 36 | int event( int id ); 37 | void action( int id ); 38 | }; 39 | -------------------------------------------------------------------------------- /examples/blink/blink.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class Atm_blink : public Machine { 4 | 5 | public: 6 | Atm_blink( void ) : Machine() {}; 7 | 8 | short pin; 9 | atm_timer_millis timer; 10 | 11 | enum { IDLE, LED_ON, LED_OFF }; // STATES 12 | enum { EVT_TIMER, EVT_ON, EVT_OFF, ELSE }; // EVENTS 13 | enum { ENT_ON, ENT_OFF }; // ACTIONS 14 | 15 | Atm_blink & begin( int attached_pin, uint32_t blinkrate ) { 16 | const static state_t state_table[] PROGMEM = { 17 | /* ON_ENTER ON_LOOP ON_EXIT EVT_TIMER EVT_ON EVT_OFF ELSE */ 18 | /* IDLE */ ENT_OFF, -1, -1, -1, LED_ON, -1, -1, 19 | /* LED_ON */ ENT_ON, -1, -1, LED_OFF, -1, IDLE, -1, 20 | /* LED_OFF */ ENT_OFF, -1, -1, LED_ON, -1, IDLE, -1, 21 | }; 22 | Machine::begin( state_table, ELSE ); 23 | pin = attached_pin; 24 | timer.set( blinkrate ); 25 | pinMode( pin, OUTPUT ); 26 | return *this; 27 | } 28 | 29 | int event( int id ) { 30 | switch ( id ) { 31 | case EVT_TIMER : 32 | return timer.expired( this ); 33 | } 34 | return 0; 35 | } 36 | 37 | void action( int id ) { 38 | switch ( id ) { 39 | case ENT_ON : 40 | digitalWrite( pin, HIGH ); 41 | return; 42 | case ENT_OFF : 43 | digitalWrite( pin, LOW ); 44 | return; 45 | } 46 | } 47 | }; 48 | 49 | Atm_blink led; 50 | 51 | void setup() { 52 | led.begin( 4, 200 ); // Setup a blink machine on pin 4 53 | led.trigger( led.EVT_ON ); // Turn it on 54 | } 55 | 56 | void loop() { 57 | led.cycle(); 58 | } 59 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; http://docs.platformio.org/page/projectconf.html 10 | 11 | [platformio] 12 | 13 | #env_default = esp12e 14 | env_default = d1_mini 15 | 16 | [common_env_data] 17 | 18 | # Automaton + PR #50 (https://github.com/tinkerspy/Automaton/pull/50) 19 | # Automaton-Esp8266 + PR #5 (https://github.com/tinkerspy/Automaton-Esp8266/pull/5) 20 | # 334 = Adafruit MCP23017 Arduino Library 21 | lib_deps_ext = https://github.com/euphi/Automaton.git 22 | https://github.com/euphi/Automaton-Esp8266.git 23 | MFRC522 24 | Adafruit MCP23017 Arduino Library 25 | 26 | lib_deps_int = Hash 27 | 28 | [env:esp12e] 29 | platform = espressif8266 30 | board = d1_mini 31 | framework = arduino 32 | 33 | lib_ldf_mode=chain+ 34 | lib_deps= ${common_env_data.lib_deps_int} 35 | ${common_env_data.lib_deps_ext} 36 | 37 | # Build for ESP-ADC (in-circuit), 512kb Flash only! 38 | build_flags = -Wl,-Tesp8266.flash.512k64.ld 39 | 40 | # ck: DTR connected to GPIO0, RTS connected to RESET 41 | upload_resetmethod = ck 42 | upload_speed = 460800 43 | 44 | 45 | [env:d1_mini] 46 | platform = espressif8266 47 | board = d1_mini 48 | framework = arduino 49 | 50 | lib_ldf_mode=chain+ 51 | lib_deps= ${common_env_data.lib_deps_int} 52 | ${common_env_data.lib_deps_ext} 53 | 54 | upload_speed = 460800 55 | -------------------------------------------------------------------------------- /src/Automaton.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Automaton.cpp - Reactive State Machine Framework for Arduino. 3 | Published under the MIT License (MIT), Copyright (c) 2015-2016, J.P. van der Landen 4 | */ 5 | 6 | #include "Automaton.h" 7 | 8 | Automaton automaton; // The global automaton machine container/scheduler 9 | 10 | /* 11 | * The Automaton class is a simple scheduler for running State Machines 12 | * 13 | ********************************************************************************************* 14 | * 15 | * Automaton::add( machine ) - Adds a State Machine object to the container 16 | * 17 | */ 18 | 19 | Automaton& Automaton::add( Machine& machine, bool force /* = true */ ) { 20 | if ( ( machine.flags & ATM_SLINK_FLAG ) == 0 || force ) { 21 | machine.inventory_next = inventory_root; 22 | inventory_root = &machine; 23 | machine.flags |= ATM_SLINK_FLAG; 24 | } 25 | return *this; 26 | } 27 | 28 | /* 29 | * Automaton::run() - Runs the appliance 30 | * 31 | * executes one cycle of each machine and exits 32 | * 33 | */ 34 | 35 | Automaton& Automaton::run( void ) { 36 | Machine* m; 37 | m = inventory_root; 38 | while ( m ) { 39 | if ( ( m->flags & ( ATM_SLEEP_FLAG | ATM_CYCLE_FLAG ) ) == 0 ) m->cycle(); 40 | // Move to the next machine 41 | m = m->inventory_next; 42 | } 43 | return *this; 44 | } 45 | 46 | Automaton& Automaton::delay( uint32_t time ) { 47 | uint32_t cycle_start = millis(); 48 | do { 49 | run(); 50 | } while ( millis() - cycle_start < time ); 51 | return *this; 52 | } 53 | 54 | Appliance& Appliance::component( Machine& machine ) { 55 | Automaton::add( machine ); 56 | return *this; 57 | } 58 | 59 | Appliance& Appliance::run( uint32_t time /* = 0 */ ) { 60 | Automaton::delay( time ); 61 | return *this; 62 | } 63 | 64 | Factory& Factory::add( Machine& machine ) { 65 | Automaton::add( machine ); 66 | return *this; 67 | } 68 | 69 | Factory& Factory::cycle( uint32_t time /* = 0 */ ) { 70 | Automaton::delay( time ); 71 | return *this; 72 | } 73 | -------------------------------------------------------------------------------- /src/Atm_led.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Atm_led : public Machine { 6 | public: 7 | enum { IDLE, ON, START, BLINK_OFF, LOOP, DONE, OFF, WT_ON, WT_START }; 8 | enum { EVT_ON_TIMER, EVT_OFF_TIMER, EVT_WT_TIMER, EVT_COUNTER, EVT_ON, EVT_OFF, EVT_BLINK, EVT_TOGGLE, EVT_TOGGLE_BLINK, ELSE, EVT_BRUP, EVT_BRDN }; // BRUP/BRDN pseudo 9 | enum { EVT_START = EVT_BLINK }; 10 | 11 | Atm_led( void ) : Machine(){}; 12 | Atm_led& begin( int attached_pin, bool activeLow = false ); 13 | Atm_led& blink( void ); 14 | Atm_led& blink( uint32_t duration ); 15 | Atm_led& blink( uint32_t duration, uint32_t pause_duration, uint16_t repeat_count = ATM_COUNTER_OFF ); 16 | Atm_led& pwm( uint16_t width, float freq = -1 ); 17 | Atm_led& frequency( float freq ); 18 | Atm_led& pause( uint32_t duration ); 19 | Atm_led& fade( int fade ); 20 | Atm_led& lead( uint32_t ms ); 21 | Atm_led& repeat( uint16_t repeat ); 22 | int brightness( int level = -1 ); 23 | Atm_led& on( void ); 24 | Atm_led& off( void ); 25 | Atm_led& toggle( void ); 26 | Atm_led& toggleBlink( void ); 27 | Atm_led& start( void ); 28 | Atm_led& trace( Stream& stream ); 29 | Atm_led& onFinish( Machine& machine, int event = 0 ); 30 | Atm_led& onFinish( atm_cb_push_t callback, int idx = 0 ); 31 | Atm_led& range( int toLow, int toHigh, bool wrap = false ); 32 | Atm_led& levels( unsigned char* map, int mapsize, bool wrap = false ); 33 | int brighten( int v = 1 ); 34 | Atm_led& trigger( int event ); 35 | 36 | private: 37 | enum { ENT_INIT, ENT_ON, ENT_OFF, EXT_CHAIN }; 38 | uint8_t level; 39 | short pin; 40 | bool activeLow; 41 | uint8_t toHigh, toLow; 42 | bool wrap; 43 | uint16_t repeat_count; 44 | uint16_t width; 45 | float freq; 46 | atm_timer_millis on_timer, off_timer, lead_timer; 47 | atm_counter counter; 48 | atm_connector onfinish; 49 | unsigned char* levelMap; 50 | int levelMapSize; 51 | int mapLevel( int level ); 52 | 53 | 54 | int event( int id ); 55 | void action( int id ); 56 | }; 57 | -------------------------------------------------------------------------------- /src/Atm_controller.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define ATM_CONDITION_OPERAND_MAX 4 6 | 7 | class Atm_controller : public Machine { 8 | public: 9 | enum { OFF, ON }; // STATES 10 | enum { EVT_ON, EVT_OFF, EVT_INPUT, ELSE }; // EVENTS 11 | 12 | Atm_controller( void ) : Machine(){}; 13 | Atm_controller& begin( bool initialState = false ); 14 | Atm_controller& onChange( bool status, atm_cb_push_t callback, int idx = 0 ); 15 | Atm_controller& onChange( bool status, Machine& machine, int event = 0 ); 16 | Atm_controller& onChange( atm_cb_push_t callback, int idx = 0 ); 17 | Atm_controller& onChange( Machine& machine, int event = 0 ); 18 | Atm_controller& onInput( bool status, atm_cb_push_t callback, int idx = 0 ); 19 | Atm_controller& onInput( bool status, Machine& machine, int event = 0 ); 20 | Atm_controller& IF( Machine& machine, char relOp = '>', int match = 0 ); 21 | Atm_controller& IF( atm_cb_pull_t callback, int idx = 0 ); 22 | Atm_controller& AND( Machine& machine, char relOp = '>', int match = 0 ); 23 | Atm_controller& AND( atm_cb_pull_t callback, int idx = 0 ); 24 | Atm_controller& OR( Machine& machine, char relOp = '>', int match = 0 ); 25 | Atm_controller& OR( atm_cb_pull_t callback, int idx = 0 ); 26 | Atm_controller& XOR( Machine& machine, char relOp = '>', int match = 0 ); 27 | Atm_controller& XOR( atm_cb_pull_t callback, int idx = 0 ); 28 | Atm_controller& led( int led, bool activeLow = false ); 29 | Atm_controller& trace( Stream& stream ); 30 | 31 | private: 32 | enum { ENT_ON, ENT_OFF }; // ACTIONS 33 | enum { ON_CHANGE_FALSE, ON_CHANGE_TRUE, ON_INPUT_FALSE, ON_INPUT_TRUE, _CONN_SIZE_ }; // CONNECTORS 34 | state_t last_state; 35 | atm_connector connector[_CONN_SIZE_]; 36 | atm_connector operand[ATM_CONDITION_OPERAND_MAX]; 37 | int8_t indicator; 38 | bool indicatorActiveLow; 39 | const static char relOps[]; 40 | 41 | int event( int id ); 42 | void action( int id ); 43 | Atm_controller& OP( char logOp, Machine& machine, char relOp, int match ); 44 | Atm_controller& OP( char logOp, atm_cb_pull_t callback, int idx ); 45 | bool eval_one( atm_connector& connector ); 46 | bool eval_all(); 47 | }; 48 | -------------------------------------------------------------------------------- /src/Atm_fan.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "Atm_fan.hpp" 3 | 4 | /* Add optional parameters for the state machine to begin() 5 | * Add extra initialization code 6 | */ 7 | 8 | Atm_fan& Atm_fan::begin() { 9 | // clang-format off 10 | const static state_t state_table[] PROGMEM = { 11 | /* ON_ENTER ON_LOOP ON_EXIT EVT_INPUT ELSE */ 12 | /* IDLE */ -1, ATM_SLEEP, -1, SEND, -1, 13 | /* SEND */ ENT_SEND, -1, -1, -1, IDLE, 14 | }; 15 | // clang-format on 16 | Machine::begin( state_table, ELSE ); 17 | return *this; 18 | } 19 | 20 | /* Add C++ code for each internally handled event (input) 21 | * The code must return 1 to trigger the event 22 | */ 23 | 24 | int Atm_fan::event( int id ) { 25 | switch ( id ) { 26 | case EVT_INPUT: 27 | return 0; 28 | } 29 | return 0; 30 | } 31 | 32 | /* Add C++ code for each action 33 | * This generates the 'output' for the state machine 34 | */ 35 | 36 | void Atm_fan::action( int id ) { 37 | switch ( id ) { 38 | case ENT_SEND: 39 | push( connectors, ON_INPUT | ATM_BROADCAST, 4, 1, 1 ); 40 | return; 41 | } 42 | } 43 | 44 | /* Optionally override the default trigger() method 45 | * Control how your machine processes triggers 46 | */ 47 | 48 | Atm_fan& Atm_fan::trigger( int event ) { 49 | Machine::trigger( event ); 50 | return *this; 51 | } 52 | 53 | /* Optionally override the default state() method 54 | * Control what the machine returns when another process requests its state 55 | */ 56 | 57 | int Atm_fan::state( void ) { 58 | return Machine::state(); 59 | } 60 | 61 | /* Nothing customizable below this line 62 | ************************************************************************************************ 63 | */ 64 | 65 | /* onInput() push connector variants ( slots 4, autostore 1, broadcast 1 ) 66 | * 67 | * Usage in action() handler: push( connectors, ON_INPUT | ATM_BROADCAST, 4, v, up ); 68 | */ 69 | 70 | Atm_fan& Atm_fan::onInput( Machine& machine, int event ) { 71 | onPush( connectors, ON_INPUT, -1, 4, 0, machine, event ); 72 | return *this; 73 | } 74 | 75 | Atm_fan& Atm_fan::onInput( atm_cb_push_t callback, int idx ) { 76 | onPush( connectors, ON_INPUT, -1, 4, 0, callback, idx ); 77 | return *this; 78 | } 79 | 80 | /* State trace method 81 | * Sets the symbol table and the default logging method for serial monitoring 82 | */ 83 | 84 | Atm_fan& Atm_fan::trace( Stream& stream ) { 85 | Machine::setTrace( &stream, atm_serial_debug::trace, "FAN\0EVT_INPUT\0ELSE\0IDLE\0SEND" ); 86 | return *this; 87 | } 88 | -------------------------------------------------------------------------------- /examples/knight_rider2/Atm_sweep.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Atm_sweep.h" 3 | 4 | Atm_sweep & Atm_sweep::begin( int p0, int p1, int p2, int p3, int p4, int p5 ) { 5 | const static state_t state_table[] PROGMEM = { 6 | /* ON_ENTER ON_LOOP ON_EXIT EVT_TIMER EVT_START EVT_STOP EVT_TOGGLE, ELSE */ 7 | /* IDLE */ ENT_OFF, ATM_SLEEP, -1, -1, U0, IDLE, U0, -1, 8 | /* U0 */ ENT_L0, -1, ENT_OFF, U1, -1, IDLE, IDLE, -1, 9 | /* U1 */ ENT_L1, -1, ENT_OFF, U2, -1, IDLE, IDLE, -1, 10 | /* U2 */ ENT_L2, -1, ENT_OFF, U3, -1, IDLE, IDLE, -1, 11 | /* U3 */ ENT_L3, -1, ENT_OFF, U4, -1, IDLE, IDLE, -1, 12 | /* U4 */ ENT_L4, -1, ENT_OFF, U5, -1, IDLE, IDLE, -1, 13 | /* U5 */ ENT_L5, -1, ENT_OFF, D4, -1, IDLE, IDLE, -1, 14 | /* D4 */ ENT_L4, -1, ENT_OFF, D3, -1, IDLE, IDLE, -1, 15 | /* D3 */ ENT_L3, -1, ENT_OFF, D2, -1, IDLE, IDLE, -1, 16 | /* D2 */ ENT_L2, -1, ENT_OFF, D1, -1, IDLE, IDLE, -1, 17 | /* D1 */ ENT_L1, -1, ENT_OFF, D0, -1, IDLE, IDLE, -1, 18 | /* D0 */ ENT_L0, -1, ENT_OFF, U0, -1, IDLE, IDLE, -1, 19 | }; 20 | Machine::begin( state_table, ELSE ); 21 | pin[0] = p0; pin[1] = p1; pin[2] = p2; 22 | pin[3] = p3; pin[4] = p4; pin[5] = p5; 23 | timer.set( 200 ); 24 | for ( uint8_t i = 0; i <= 5; i++ ) { 25 | pinMode( pin[i], OUTPUT ); 26 | } 27 | return *this; 28 | } 29 | 30 | Atm_sweep & Atm_sweep::speed( uint32_t v ) { 31 | timer.set( v ); 32 | return *this; 33 | } 34 | 35 | int Atm_sweep::event( int id ) { 36 | switch ( id ) { 37 | case EVT_TIMER : 38 | return timer.expired( this ); 39 | } 40 | return 0; 41 | } 42 | 43 | void Atm_sweep::action( int id ) { 44 | switch ( id ) { 45 | case ENT_L0 : return digitalWrite( pin[0], HIGH ); 46 | case ENT_L1 : return digitalWrite( pin[1], HIGH ); 47 | case ENT_L2 : return digitalWrite( pin[2], HIGH ); 48 | case ENT_L3 : return digitalWrite( pin[3], HIGH ); 49 | case ENT_L4 : return digitalWrite( pin[4], HIGH ); 50 | case ENT_L5 : return digitalWrite( pin[5], HIGH ); 51 | case ENT_OFF : 52 | for ( uint8_t i = 0; i <= 5; i++ ) { 53 | digitalWrite( pin[i], LOW ); 54 | } 55 | return; 56 | } 57 | } 58 | 59 | Atm_sweep & Atm_sweep::trace( Stream & stream ) { 60 | Machine::setTrace( &stream, atm_serial_debug::trace, 61 | "SWEEP\0EVT_TIMER\0EVT_START\0EVT_STOP\0ELSE\0" 62 | "IDLE\0U0\0U1\0U2\0U3\0U4\0U5\0D4\0D3\0D2\0D1\0D0" ); 63 | return *this; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /src/Atm_analog.cpp: -------------------------------------------------------------------------------- 1 | #include "Atm_analog.hpp" 2 | 3 | Atm_analog& Atm_analog::begin( int attached_pin, int samplerate /* = 50 */ ) { 4 | // clang-format off 5 | const static state_t state_table[] PROGMEM = { 6 | /* ON_ENTER ON_LOOP ON_EXIT EVT_TRIGGER EVT_TIMER ELSE */ 7 | /* IDLE */ -1, -1, -1, -1, SAMPLE, -1, 8 | /* SAMPLE */ ENT_SAMPLE, -1, -1, SEND, -1, IDLE, 9 | /* SEND */ ENT_SEND, -1, -1, -1, -1, IDLE, 10 | }; 11 | // clang-format on 12 | Machine::begin( state_table, ELSE ); 13 | pin = attached_pin; 14 | timer.set( samplerate ); 15 | return *this; 16 | } 17 | 18 | int Atm_analog::event( int id ) { 19 | switch ( id ) { 20 | case EVT_TIMER: 21 | return timer.expired( this ); 22 | case EVT_TRIGGER: 23 | return v_previous != v_sample; 24 | } 25 | return 0; 26 | } 27 | 28 | void Atm_analog::action( int id ) { 29 | switch ( id ) { 30 | case ENT_SAMPLE: 31 | v_previous = v_sample; 32 | v_sample = sample(); 33 | return; 34 | case ENT_SEND: 35 | v_sample = sample(); 36 | onchange.push( v_sample, v_sample > v_previous ); 37 | return; 38 | } 39 | } 40 | 41 | Atm_analog& Atm_analog::range( int toLow, int toHigh ) { 42 | this->toLow = toLow; 43 | this->toHigh = toHigh; 44 | return *this; 45 | } 46 | 47 | Atm_analog& Atm_analog::set( int value ) { // Dummy method 48 | return *this; 49 | } 50 | 51 | Atm_analog& Atm_analog::onChange( Machine& machine, int event /* = 0 */ ) { 52 | this->onchange.set( &machine, event ); 53 | return *this; 54 | } 55 | 56 | Atm_analog& Atm_analog::onChange( atm_cb_push_t callback, int idx /* = 0 */ ) { 57 | onchange.set( callback, idx ); 58 | return *this; 59 | } 60 | 61 | int Atm_analog::read_sample() { 62 | return analogRead( pin ); 63 | } 64 | 65 | int Atm_analog::avg() { 66 | uint16_t v = read_sample(); 67 | avg_buf_total = avg_buf_total + v - avg_buf[avg_buf_head]; 68 | avg_buf[avg_buf_head] = v; 69 | if ( avg_buf_head + 1 >= avg_buf_size ) { 70 | avg_buf_head = 0; 71 | } else { 72 | avg_buf_head++; 73 | } 74 | return avg_buf_total / avg_buf_size; 75 | } 76 | 77 | int Atm_analog::sample() { 78 | int v = avg_buf_size > 0 ? avg() : read_sample(); 79 | if ( toHigh || toLow ) { 80 | return map( v, 0, 1023, toLow, toHigh ); 81 | } else { 82 | return v; 83 | } 84 | } 85 | 86 | int Atm_analog::state( void ) { 87 | return sample(); 88 | } 89 | 90 | Atm_analog& Atm_analog::average( uint16_t* v, uint16_t size ) { 91 | avg_buf = v; 92 | avg_buf_size = size / sizeof( uint16_t ); 93 | avg_buf_head = 0; 94 | avg_buf_total = 0; 95 | for ( uint16_t i = 0; i < avg_buf_size; i++ ) { 96 | avg_buf[i] = read_sample(); 97 | avg_buf_total += avg_buf[i]; 98 | } 99 | return *this; 100 | } 101 | 102 | Atm_analog& Atm_analog::trace( Stream& stream ) { 103 | setTrace( &stream, atm_serial_debug::trace, 104 | "ANALOG\0EVT_TRIGGER\0EVT_TIMER\0ELSE\0" 105 | "IDLE\0SAMPLE\0SEND" ); 106 | 107 | return *this; 108 | } 109 | -------------------------------------------------------------------------------- /src/Atm_digital.cpp: -------------------------------------------------------------------------------- 1 | #include "Atm_digital.hpp" 2 | 3 | Atm_digital& Atm_digital::begin( int pin, int debounce /* = 20 */, bool activeLow /* = false */, bool pullUp /* = false */ ) { 4 | // clang-format off 5 | const static state_t state_table[] PROGMEM = { 6 | /* ON_ENTER ON_LOOP ON_EXIT EVT_TIMER EVT_HIGH EVT_LOW ELSE */ 7 | /* IDLE */ -1, -1, -1, -1, WAITH, -1, -1, 8 | /* WAITH */ -1, -1, -1, VHIGH, -1, IDLE, -1, 9 | /* VHIGH */ ENT_HIGH, -1, -1, -1, -1, WAITL, -1, 10 | /* WAITL */ -1, -1, -1, VLOW, VHIGH, -1, -1, 11 | /* VLOW */ ENT_LOW, -1, -1, -1, -1, -1, IDLE, 12 | }; 13 | // clang-format on 14 | Machine::begin( state_table, ELSE ); 15 | this->pin = pin; 16 | this->activeLow = activeLow; 17 | timer.set( debounce ); 18 | indicator = -1; 19 | pinMode( pin, pullUp ? INPUT_PULLUP : INPUT ); 20 | return *this; 21 | } 22 | 23 | int Atm_digital::event( int id ) { 24 | switch ( id ) { 25 | case EVT_TIMER: 26 | return timer.expired( this ); 27 | case EVT_HIGH: 28 | return ( !digitalRead( pin ) != !activeLow ); // XOR 29 | case EVT_LOW: 30 | return !( !digitalRead( pin ) != !activeLow ); 31 | } 32 | return 0; 33 | } 34 | 35 | void Atm_digital::action( int id ) { 36 | switch ( id ) { 37 | case ENT_HIGH: 38 | connection[ON_CHANGE_TRUE].push( state() ); 39 | if ( indicator > -1 ) digitalWrite( indicator, !HIGH != !indicatorActiveLow ); 40 | return; 41 | case ENT_LOW: 42 | connection[ON_CHANGE_FALSE].push( state() ); 43 | if ( indicator > -1 ) digitalWrite( indicator, !LOW != !indicatorActiveLow ); 44 | return; 45 | } 46 | } 47 | 48 | int Atm_digital::state( void ) { 49 | return ( current == VHIGH || current == WAITL ); 50 | } 51 | 52 | Atm_digital& Atm_digital::led( int led, bool activeLow /* = false */ ) { 53 | indicator = led; 54 | indicatorActiveLow = activeLow; 55 | pinMode( indicator, OUTPUT ); 56 | return *this; 57 | } 58 | 59 | Atm_digital& Atm_digital::onChange( bool status, atm_cb_push_t callback, int idx /* = 0 */ ) { 60 | connection[status ? ON_CHANGE_TRUE : ON_CHANGE_FALSE].set( callback, idx ); 61 | return *this; 62 | } 63 | 64 | Atm_digital& Atm_digital::onChange( bool status, Machine& machine, int event /* = 0 */ ) { 65 | connection[status ? ON_CHANGE_TRUE : ON_CHANGE_FALSE].set( &machine, event ); 66 | return *this; 67 | } 68 | 69 | Atm_digital& Atm_digital::onChange( atm_cb_push_t callback, int idx /* = 0 */ ) { 70 | connection[ON_CHANGE_FALSE].set( callback, idx ); 71 | connection[ON_CHANGE_TRUE].set( callback, idx ); 72 | return *this; 73 | } 74 | 75 | Atm_digital& Atm_digital::onChange( Machine& machine, int event /* = 0 */ ) { 76 | connection[ON_CHANGE_FALSE].set( &machine, event ); 77 | connection[ON_CHANGE_TRUE].set( &machine, event ); 78 | return *this; 79 | } 80 | 81 | Atm_digital& Atm_digital::trace( Stream& stream ) { 82 | setTrace( &stream, atm_serial_debug::trace, "DIGITAL\0EVT_TIMER\0EVT_HIGH\0EVT_LOW\0ELSE\0IDLE\0WAITH\0VHIGH\0WAITL\0VLOW" ); 83 | return *this; 84 | } 85 | -------------------------------------------------------------------------------- /src/Atm_command.cpp: -------------------------------------------------------------------------------- 1 | #include "Atm_command.hpp" 2 | 3 | Atm_command& Atm_command::begin( Stream& stream, char buffer[], int size ) { 4 | // clang-format off 5 | const static state_t state_table[] PROGMEM = { 6 | /* ON_ENTER ON_LOOP ON_EXIT EVT_INPUT EVT_EOL ELSE */ 7 | /* IDLE */ -1, -1, -1, READCHAR, -1, -1, 8 | /* READCHAR */ ENT_READCHAR, -1, -1, READCHAR, SEND, -1, 9 | /* SEND */ ENT_SEND, -1, -1, -1, -1, IDLE, 10 | }; 11 | // clang-format on 12 | Machine::begin( state_table, ELSE ); 13 | this->stream = &stream; 14 | this->buffer = buffer; 15 | bufsize = size; 16 | bufptr = 0; 17 | separatorChar = " "; 18 | lastch = '\0'; 19 | return *this; 20 | } 21 | 22 | int Atm_command::event( int id ) { 23 | switch ( id ) { 24 | case EVT_INPUT: 25 | return stream->available(); 26 | case EVT_EOL: 27 | return buffer[bufptr - 1] == '\n' || buffer[bufptr - 1] == '\r' || bufptr >= bufsize; 28 | } 29 | return 0; 30 | } 31 | 32 | void Atm_command::action( int id ) { 33 | switch ( id ) { 34 | case ENT_READCHAR: 35 | if ( stream->available() ) { 36 | char ch = stream->read(); 37 | if ( strchr( separatorChar, ch ) == NULL ) { 38 | buffer[bufptr++] = ch; 39 | lastch = ch; 40 | } else { 41 | if ( lastch != '\0' ) buffer[bufptr++] = '\0'; 42 | lastch = '\0'; 43 | } 44 | } 45 | return; 46 | case ENT_SEND: 47 | buffer[--bufptr] = '\0'; 48 | oncommand.push( lookup( 0, commands ) ); 49 | lastch = '\0'; 50 | bufptr = 0; 51 | return; 52 | } 53 | } 54 | 55 | Atm_command& Atm_command::onCommand( atm_cb_push_t callback, int idx /* = 0 */ ) { 56 | oncommand.set( callback, idx ); 57 | return *this; 58 | } 59 | 60 | Atm_command& Atm_command::list( const char* cmds ) { 61 | commands = cmds; 62 | return *this; 63 | } 64 | 65 | Atm_command& Atm_command::separator( const char sep[] ) { 66 | separatorChar = sep; 67 | return *this; 68 | } 69 | 70 | char* Atm_command::arg( int id ) { 71 | int cnt = 0; 72 | int i; 73 | if ( id == 0 ) return buffer; 74 | for ( i = 0; i < bufptr; i++ ) { 75 | if ( buffer[i] == '\0' ) { 76 | if ( ++cnt == id ) { 77 | i++; 78 | break; 79 | } 80 | } 81 | } 82 | return &buffer[i]; 83 | } 84 | 85 | int Atm_command::lookup( int id, const char* cmdlist ) { 86 | int cnt = 0; 87 | char* arg = this->arg( id ); 88 | char* a = arg; 89 | while ( cmdlist[0] != '\0' ) { 90 | while ( cmdlist[0] != '\0' && toupper( cmdlist[0] ) == toupper( a[0] ) ) { 91 | cmdlist++; 92 | a++; 93 | } 94 | if ( a[0] == '\0' && ( cmdlist[0] == ' ' || cmdlist[0] == '\0' ) ) return cnt; 95 | while ( cmdlist[0] != ' ' && cmdlist[0] != '\0' ) cmdlist++; 96 | cmdlist++; 97 | a = arg; 98 | cnt++; 99 | } 100 | return -1; 101 | } 102 | 103 | Atm_command& Atm_command::trace( Stream& stream ) { 104 | setTrace( &stream, atm_serial_debug::trace, "COMMAND\0EVT_INPUT\0EVT_EOL\0ELSE\0IDLE\0READCHAR\0SEND" ); 105 | return *this; 106 | } 107 | -------------------------------------------------------------------------------- /src/Automaton.h: -------------------------------------------------------------------------------- 1 | /* 2 | Automaton.h - Reactive State Machine Framework for Arduino. 3 | Published under the MIT License (MIT), Copyright (c) 2015-2016, J.P. van der Landen 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "Arduino.h" 9 | 10 | typedef int8_t state_t; 11 | 12 | const uint8_t ATM_SLEEP_FLAG = 0b00000001; 13 | const uint8_t ATM_CYCLE_FLAG = 0b00000010; 14 | const uint8_t ATM_SLINK_FLAG = 0b00000100; 15 | const uint8_t ATM_USR1_FLAG = 0b00010000; 16 | const uint8_t ATM_USR2_FLAG = 0b00100000; 17 | const uint8_t ATM_USR3_FLAG = 0b01000000; 18 | const uint8_t ATM_USR4_FLAG = 0b10000000; 19 | const uint8_t ATM_USR_FLAGS = 0b11110000; 20 | const uint8_t ATM_BROADCAST = 0b10000000; 21 | 22 | #define read_state( addr ) ( state_t ) pgm_read_byte_near( addr ) 23 | 24 | #define B2INT( hibyte, lobyte ) ((int)( hibyte << 8 | lobyte )) 25 | #define B4INT( byte4, byte3, byte2, byte1) ((uint32_t)( (uint32_t)byte4 << 24 | (uint32_t)byte3 << 16 | (uint32_t)byte2 << 8 | (uint32_t)byte1 )) 26 | 27 | class Machine; 28 | class Automaton; 29 | 30 | extern Automaton automaton; 31 | 32 | typedef void ( *swcb_sym_t )( Stream* stream, Machine& machine, const char label[], const char current[], const char next[], const char trigger[], 33 | uint32_t runtime, uint32_t cycles ); 34 | 35 | const uint8_t ATM_UP = 1; 36 | const uint8_t ATM_DOWN = 0; 37 | 38 | const state_t ATM_NOP = -1; 39 | const state_t ATM_SLEEP = -2; 40 | const state_t ATM_ON_SWITCH = -3; 41 | const state_t ATM_ON_ENTER = 0; 42 | const state_t ATM_ON_LOOP = 1; 43 | const state_t ATM_ON_EXIT = 2; 44 | 45 | const uint32_t ATM_TIMER_OFF = 0xffffffff; // This timer value never expires 46 | const uint16_t ATM_COUNTER_OFF = 0xffff; // This counter value never expires 47 | 48 | class Automaton { 49 | public: 50 | Automaton& add( Machine& machine, bool force = true ); 51 | Automaton& delay( uint32_t time ); 52 | Automaton& run( void ); 53 | 54 | private: 55 | Machine* inventory_root; 56 | void runTiny( void ); 57 | }; 58 | 59 | // For backward compatibility with 'the old way': Appliance app/app.component()/app.run() 60 | 61 | class Appliance : Automaton { 62 | public: 63 | Appliance& component( Machine& machine ); 64 | Appliance& run( uint32_t time = 0 ); 65 | }; 66 | 67 | class Factory : Automaton { 68 | public: 69 | Factory& add( Machine& machine ); 70 | Factory& cycle( uint32_t time = 0 ); 71 | }; 72 | 73 | // Support classes 74 | 75 | #include 76 | #include 77 | #include 78 | #include 79 | 80 | // Main framework classes 81 | 82 | #include 83 | 84 | // Bundled state machines (components) 85 | 86 | #include 87 | #include 88 | #include 89 | #include 90 | #include 91 | #include 92 | #include 93 | #include 94 | #include 95 | #include 96 | #include 97 | #include 98 | #include 99 | #include 100 | -------------------------------------------------------------------------------- /src/atm_connector.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Automaton.cpp - Reactive State Machine Framework for Arduino. 3 | Published under the MIT License (MIT), Copyright (c) 2015-2016, J.P. van der Landen 4 | */ 5 | 6 | #include "Automaton.h" 7 | 8 | /* 9 | * The atm_connector class facilitates creating push and pull connections between 10 | * State Machines. 11 | * 12 | ********************************************************************************************* 13 | * 14 | * push( v, up, overrideCallback ) - Calls a machine's trigger method or a callback 15 | * 16 | * Will return false if a callback is configured while the overrideCallback arg was specified 17 | */ 18 | 19 | bool atm_connector::push( int v /* = 0 */, int up /* = 0 */, bool overrideCallback /* = false */ ) { 20 | switch ( mode_flags & 0b00000111 ) { 21 | case MODE_PUSHCB: 22 | if ( overrideCallback ) { 23 | return false; 24 | } else { 25 | if ( push_callback ) { 26 | ( *push_callback )( callback_idx, v, up ); 27 | } 28 | } 29 | return true; 30 | case MODE_MACHINE: 31 | if ( machine != 0 ) { 32 | machine->trigger( event ); 33 | } 34 | return true; 35 | } 36 | return true; 37 | } 38 | 39 | /* 40 | * pull( v, up, def_value ) - Calls a machine's state method or a callback 41 | * 42 | */ 43 | 44 | int atm_connector::pull( int v /* = 0 */, int up /* = 0 */, bool def_value /* = false */ ) { 45 | switch ( mode_flags & 0b00000111 ) { 46 | case MODE_PULLCB: 47 | return ( *pull_callback )( callback_idx ); 48 | case MODE_MACHINE: 49 | return machine->state(); 50 | } 51 | return def_value; 52 | } 53 | 54 | /* 55 | * logOp() Returns the logical operator part of the mode_flags byte 56 | * 57 | */ 58 | 59 | int8_t atm_connector::logOp( void ) { 60 | return ( mode_flags & 0b00011000 ) >> 3; 61 | } 62 | 63 | /* 64 | * logOp() Returns the relational operator part of the mode_flags byte 65 | * 66 | */ 67 | 68 | int8_t atm_connector::relOp( void ) { 69 | return ( mode_flags & 0b11100000 ) >> 5; 70 | } 71 | 72 | /* 73 | * set( callback, idx, logOp, relOp ) - Configures a connector object as a push callback 74 | * 75 | */ 76 | 77 | void atm_connector::set( atm_cb_push_t callback, int idx, int8_t logOp /* = 0 */, int8_t relOp /* = 0 */ ) { 78 | mode_flags = MODE_PUSHCB | ( logOp << 3 ) | ( relOp << 5 ); 79 | push_callback = callback; 80 | callback_idx = idx; 81 | } 82 | 83 | /* 84 | * set( callback, idx, logOp, relOp ) - Configures a connector object as a pull callback 85 | * 86 | */ 87 | 88 | void atm_connector::set( atm_cb_pull_t callback, int idx, int8_t logOp /* = 0 */, int8_t relOp /* = 0 */ ) { 89 | mode_flags = MODE_PULLCB | ( logOp << 3 ) | ( relOp << 5 ); 90 | pull_callback = callback; 91 | callback_idx = idx; 92 | } 93 | 94 | /* 95 | * set( callback, idx, logOp, relOp ) - Configures a connector object as a machine connector (calls trigger) 96 | * 97 | */ 98 | 99 | void atm_connector::set( Machine* m, int evt, int8_t logOp /* = 0 */, int8_t relOp /* = 0 */ ) { 100 | mode_flags = MODE_MACHINE | ( logOp << 3 ) | ( relOp << 5 ); 101 | machine = m; 102 | event = evt; 103 | } 104 | 105 | /* 106 | * mode() - Returns the mode part of the mode_flags byte 107 | * 108 | */ 109 | 110 | int8_t atm_connector::mode( void ) { 111 | return mode_flags & 0b00000111; 112 | } 113 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | Atm_controller KEYWORD1 2 | AND KEYWORD2 3 | IF KEYWORD2 4 | OR KEYWORD2 5 | XOR KEYWORD2 6 | begin KEYWORD2 7 | led KEYWORD2 8 | onChange KEYWORD2 9 | onInput KEYWORD2 10 | trace KEYWORD2 11 | Atm_player KEYWORD1 12 | begin KEYWORD2 13 | onFinish KEYWORD2 14 | onNote KEYWORD2 15 | pitch KEYWORD2 16 | play KEYWORD2 17 | repeat KEYWORD2 18 | speed KEYWORD2 19 | start KEYWORD2 20 | state KEYWORD2 21 | stop KEYWORD2 22 | toggle KEYWORD2 23 | trace KEYWORD2 24 | trigger KEYWORD2 25 | atm_counter KEYWORD1 26 | decrement KEYWORD2 27 | expired KEYWORD2 28 | set KEYWORD2 29 | Atm_bit KEYWORD1 30 | begin KEYWORD2 31 | input KEYWORD2 32 | led KEYWORD2 33 | off KEYWORD2 34 | on KEYWORD2 35 | onChange KEYWORD2 36 | onInput KEYWORD2 37 | refresh KEYWORD2 38 | toggle KEYWORD2 39 | trace KEYWORD2 40 | atm_connector::union :: KEYWORD1 41 | atm_connector KEYWORD1 42 | logOp KEYWORD2 43 | mode KEYWORD2 44 | pull KEYWORD2 45 | push KEYWORD2 46 | relOp KEYWORD2 47 | set KEYWORD2 48 | atm_connector::union KEYWORD1 49 | atm_connector::union ::::union KEYWORD1 50 | atm_serial_debug KEYWORD1 51 | trace KEYWORD2 52 | Atm_encoder KEYWORD1 53 | begin KEYWORD2 54 | onChange KEYWORD2 55 | range KEYWORD2 56 | set KEYWORD2 57 | state KEYWORD2 58 | trace KEYWORD2 59 | Atm_timer KEYWORD1 60 | begin KEYWORD2 61 | interval KEYWORD2 62 | interval_millis KEYWORD2 63 | interval_seconds KEYWORD2 64 | left KEYWORD2 65 | onFinish KEYWORD2 66 | onTimer KEYWORD2 67 | repeat KEYWORD2 68 | start KEYWORD2 69 | stop KEYWORD2 70 | toggle KEYWORD2 71 | trace KEYWORD2 72 | Atm_fade KEYWORD1 73 | begin KEYWORD2 74 | blink KEYWORD2 75 | fade KEYWORD2 76 | off KEYWORD2 77 | on KEYWORD2 78 | onFinish KEYWORD2 79 | pause KEYWORD2 80 | repeat KEYWORD2 81 | start KEYWORD2 82 | toggle KEYWORD2 83 | toggleBlink KEYWORD2 84 | trace KEYWORD2 85 | atm_timer_millis KEYWORD1 86 | expired KEYWORD2 87 | set KEYWORD2 88 | Atm_analog KEYWORD1 89 | average KEYWORD2 90 | begin KEYWORD2 91 | onChange KEYWORD2 92 | range KEYWORD2 93 | set KEYWORD2 94 | state KEYWORD2 95 | trace KEYWORD2 96 | Atm_comparator KEYWORD1 97 | average KEYWORD2 98 | begin KEYWORD2 99 | onChange KEYWORD2 100 | read_sample KEYWORD2 101 | skip KEYWORD2 102 | state KEYWORD2 103 | threshold KEYWORD2 104 | trace KEYWORD2 105 | Automaton KEYWORD1 106 | add KEYWORD2 107 | delay KEYWORD2 108 | run KEYWORD2 109 | Appliance KEYWORD1 110 | component KEYWORD2 111 | run KEYWORD2 112 | Factory KEYWORD1 113 | add KEYWORD2 114 | cycle KEYWORD2 115 | Atm_fan KEYWORD1 116 | begin KEYWORD2 117 | onInput KEYWORD2 118 | state KEYWORD2 119 | trace KEYWORD2 120 | trigger KEYWORD2 121 | Atm_button KEYWORD1 122 | autoPress KEYWORD2 123 | begin KEYWORD2 124 | debounce KEYWORD2 125 | longPress KEYWORD2 126 | onPress KEYWORD2 127 | onRelease KEYWORD2 128 | repeat KEYWORD2 129 | trace KEYWORD2 130 | Atm_command KEYWORD1 131 | arg KEYWORD2 132 | begin KEYWORD2 133 | list KEYWORD2 134 | lookup KEYWORD2 135 | onCommand KEYWORD2 136 | separator KEYWORD2 137 | trace KEYWORD2 138 | Atm_digital KEYWORD1 139 | begin KEYWORD2 140 | led KEYWORD2 141 | onChange KEYWORD2 142 | state KEYWORD2 143 | trace KEYWORD2 144 | Machine KEYWORD1 145 | action KEYWORD2 146 | cycle KEYWORD2 147 | event KEYWORD2 148 | sleep KEYWORD2 149 | state KEYWORD2 150 | trigger KEYWORD2 151 | Atm_step KEYWORD1 152 | begin KEYWORD2 153 | onStep KEYWORD2 154 | state KEYWORD2 155 | trace KEYWORD2 156 | Atm_led KEYWORD1 157 | begin KEYWORD2 158 | blink KEYWORD2 159 | brighten KEYWORD2 160 | brightness KEYWORD2 161 | fade KEYWORD2 162 | lead KEYWORD2 163 | levels KEYWORD2 164 | off KEYWORD2 165 | on KEYWORD2 166 | onFinish KEYWORD2 167 | pause KEYWORD2 168 | range KEYWORD2 169 | repeat KEYWORD2 170 | start KEYWORD2 171 | toggle KEYWORD2 172 | toggleBlink KEYWORD2 173 | trace KEYWORD2 174 | trigger KEYWORD2 175 | -------------------------------------------------------------------------------- /src/Atm_player.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #if defined( _VARIANT_ARDUINO_DUE_X ) || defined( ARDUINO_FEATHER52 ) 6 | #define ATM_PLAYER_DISABLE_TONE 7 | #endif 8 | 9 | class Atm_player : public Machine { 10 | public: 11 | enum { IDLE, START, SOUND, QUIET, NEXT, REPEAT, FINISH }; // STATES 12 | enum { EVT_START, EVT_STOP, EVT_TOGGLE, EVT_TIMER, EVT_EOPAT, EVT_REPEAT, ELSE }; // EVENTS 13 | Atm_player( void ) : Machine(){}; 14 | Atm_player& begin( int pin = -1 ); 15 | Atm_player& trace( Stream& stream ); 16 | Atm_player& trigger( int event ); 17 | int state( void ); 18 | Atm_player& play( int* pat, int size ); 19 | Atm_player& play( uint32_t* pat, int size ); 20 | Atm_player& play( int freq, int period, int pause = 0 ); 21 | Atm_player& repeat( uint16_t v = -1 ); 22 | Atm_player& speed( float v ); 23 | Atm_player& pitch( float v ); 24 | Atm_player& start( void ); 25 | Atm_player& stop( void ); 26 | Atm_player& toggle( void ); 27 | Atm_player& onFinish( Machine& machine, int event = 0 ); 28 | Atm_player& onFinish( atm_cb_push_t callback, int idx = 0 ); 29 | Atm_player& onNote( Machine& machine, int event = 0 ); 30 | Atm_player& onNote( atm_cb_push_t callback, int idx = 0 ); 31 | Atm_player& onNote( int sub, Machine& machine, int event = 0 ); 32 | Atm_player& onNote( int sub, atm_cb_push_t callback, int idx = 0 ); 33 | 34 | private: 35 | int pin; 36 | int* pattern16; // Can also be 32 bit on some hardware (teensy 3.x) 37 | uint32_t* pattern32; 38 | uint16_t patternsize; 39 | uint8_t patternwidth; 40 | int step; 41 | uint16_t repeatCount; 42 | float speedFactor, pitchFactor; 43 | int stub[3]; 44 | atm_timer_millis timer; 45 | atm_counter counter_repeat; 46 | enum { ENT_IDLE, ENT_START, ENT_SOUND, ENT_QUIET, ENT_NEXT, ENT_REPEAT, ENT_FINISH }; // ACTIONS 47 | enum { ON_FINISH, ON_NOTE, CONN_MAX = 3 }; // CONNECTORS 48 | atm_connector connectors[CONN_MAX]; 49 | int event( int id ); 50 | void action( int id ); 51 | }; 52 | 53 | /* 54 | Automaton::ATML::begin - Automaton Markup Language 55 | 56 | 57 | 58 | 59 | 60 | 61 | START 62 | START 63 | 64 | 65 | SOUND 66 | 67 | 68 | IDLE 69 | IDLE 70 | QUIET 71 | 72 | 73 | IDLE 74 | IDLE 75 | NEXT 76 | 77 | 78 | IDLE 79 | IDLE 80 | REPEAT 81 | SOUND 82 | 83 | 84 | IDLE 85 | IDLE 86 | FINISH 87 | START 88 | 89 | 90 | IDLE 91 | START 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | Automaton::ATML::end 112 | */ 113 | -------------------------------------------------------------------------------- /src/Atm_bit.cpp: -------------------------------------------------------------------------------- 1 | #include "Atm_bit.hpp" 2 | 3 | Atm_bit& Atm_bit::begin( bool initialState /* = false */ ) { 4 | // clang-format off 5 | const static state_t state_table[] PROGMEM = { 6 | /* ON_ENTER ON_LOOP ON_EXIT EVT_ON EVT_OFF EVT_TOGGLE EVT_INPUT EVT_REFRESH ELSE */ 7 | /* OFF */ ENT_OFF, ATM_SLEEP, -1, ON, -1, ON, OFF, REFR_OFF, -1, 8 | /* ON */ ENT_ON, ATM_SLEEP, -1, -1, OFF, OFF, ON, REFR_ON, -1, 9 | /* REFR_ON */ ENT_REFR_ON, -1, -1, -1, -1, -1, -1, -1, ON, 10 | /* REFR_OFF*/ ENT_REFR_OFF, -1, -1, -1, -1, -1, -1, -1, OFF, 11 | }; 12 | // clang-format on 13 | Machine::begin( state_table, ELSE ); 14 | last_state = -1; 15 | state( initialState ? ON : OFF ); 16 | indicator = -1; 17 | cycle(); 18 | return *this; 19 | } 20 | 21 | int Atm_bit::event( int id ) { 22 | return 0; 23 | } 24 | 25 | void Atm_bit::action( int id ) { 26 | switch ( id ) { 27 | case ENT_OFF: 28 | if ( last_state != -1 ) connector[last_state == current ? ON_INPUT_FALSE : ON_CHANGE_FALSE].push( state() ); 29 | if ( indicator > -1 ) digitalWrite( indicator, !LOW != !indicatorActiveLow ); 30 | last_state = current; 31 | return; 32 | case ENT_ON: 33 | if ( last_state != -1 ) connector[last_state == current ? ON_INPUT_TRUE : ON_CHANGE_TRUE].push( state() ); 34 | if ( indicator > -1 ) digitalWrite( indicator, !HIGH != !indicatorActiveLow ); 35 | last_state = current; 36 | return; 37 | case ENT_REFR_ON: 38 | connector[ON_CHANGE_TRUE].push( ON ); 39 | last_state = -1; 40 | return; 41 | case ENT_REFR_OFF: 42 | connector[ON_CHANGE_FALSE].push( OFF ); 43 | last_state = -1; 44 | return; 45 | } 46 | } 47 | 48 | Atm_bit& Atm_bit::on( void ) { 49 | trigger( EVT_ON ); 50 | return *this; 51 | } 52 | 53 | Atm_bit& Atm_bit::off( void ) { 54 | trigger( EVT_OFF ); 55 | return *this; 56 | } 57 | 58 | Atm_bit& Atm_bit::toggle( void ) { 59 | trigger( EVT_TOGGLE ); 60 | return *this; 61 | } 62 | 63 | Atm_bit& Atm_bit::input( void ) { 64 | trigger( EVT_INPUT ); 65 | return *this; 66 | } 67 | 68 | Atm_bit& Atm_bit::refresh( void ) { 69 | trigger( EVT_REFRESH ); 70 | return *this; 71 | } 72 | 73 | Atm_bit& Atm_bit::led( int led, bool activeLow /* = false */ ) { 74 | indicator = led; 75 | indicatorActiveLow = activeLow; 76 | pinMode( indicator, OUTPUT ); 77 | return *this; 78 | } 79 | 80 | Atm_bit& Atm_bit::onChange( atm_cb_push_t callback, int idx /* = 0 */ ) { 81 | connector[ON_CHANGE_FALSE].set( callback, idx ); 82 | connector[ON_CHANGE_TRUE].set( callback, idx ); 83 | return *this; 84 | } 85 | 86 | Atm_bit& Atm_bit::onChange( Machine& machine, int event /* = 0 */ ) { 87 | connector[ON_CHANGE_FALSE].set( &machine, event ); 88 | connector[ON_CHANGE_TRUE].set( &machine, event ); 89 | return *this; 90 | } 91 | 92 | Atm_bit& Atm_bit::onChange( bool status, atm_cb_push_t callback, int idx /* = 0 */ ) { 93 | connector[status ? ON_CHANGE_TRUE : ON_CHANGE_FALSE].set( callback, idx ); 94 | return *this; 95 | } 96 | 97 | Atm_bit& Atm_bit::onChange( bool status, Machine& machine, int event /* = 0 */ ) { 98 | connector[status ? ON_CHANGE_TRUE : ON_CHANGE_FALSE].set( &machine, event ); 99 | return *this; 100 | } 101 | 102 | Atm_bit& Atm_bit::onInput( bool status, atm_cb_push_t callback, int idx /* = 0 */ ) { 103 | connector[status ? ON_INPUT_TRUE : ON_INPUT_FALSE].set( callback, idx ); 104 | return *this; 105 | } 106 | 107 | Atm_bit& Atm_bit::onInput( bool status, Machine& machine, int event /* = 0 */ ) { 108 | connector[status ? ON_INPUT_TRUE : ON_INPUT_FALSE].set( &machine, event ); 109 | return *this; 110 | } 111 | 112 | Atm_bit& Atm_bit::trace( Stream& stream ) { 113 | Machine::setTrace( &stream, atm_serial_debug::trace, "BIT\0EVT_ON\0EVT_OFF\0EVT_TOGGLE\0EVT_INPUT\0EVT_REFRESH\0ELSE\0OFF\0ON\0REFR_ON\0REFR_OFF" ); 114 | return *this; 115 | } 116 | -------------------------------------------------------------------------------- /src/Atm_encoder.cpp: -------------------------------------------------------------------------------- 1 | #include "Atm_encoder.hpp" 2 | #include 3 | 4 | // Loosely based on https://www.circuitsathome.com/mcu/reading-rotary-encoder-on-arduino (Oleg Mazurov) 5 | 6 | const char Atm_encoder::enc_states[16] = {0, (char)-1, 1, 0, 1, 0, 0, (char)-1, (char)-1, 0, 0, 1, 0, 1, (char)-1, 0}; 7 | 8 | Atm_encoder& Atm_encoder::begin( int pin1, int pin2, int divider /* = 1 */ ) { 9 | // clang-format off 10 | const static state_t state_table[] PROGMEM = { 11 | /* ON_ENTER ON_LOOP ON_EXIT EVT_UP EVT_DOWN ELSE */ 12 | /* IDLE */ -1, LP_IDLE, -1, UP, DOWN, -1, 13 | /* UP */ ENT_UP, -1, -1, -1, -1, IDLE, 14 | /* DOWN */ ENT_DOWN, -1, -1, -1, -1, IDLE, 15 | }; 16 | // clang-format on 17 | Machine::begin( state_table, ELSE ); 18 | this->pin1 = pin1; 19 | this->pin2 = pin2; 20 | this->divider = divider; 21 | pinMode( pin1, INPUT ); 22 | pinMode( pin2, INPUT ); 23 | digitalWrite( pin1, HIGH ); 24 | digitalWrite( pin2, HIGH ); 25 | min = INT_MIN; 26 | max = INT_MAX; 27 | value = 0; 28 | return *this; 29 | } 30 | 31 | int Atm_encoder::event( int id ) { 32 | switch ( id ) { 33 | case EVT_UP: 34 | return enc_direction == +1 && ( enc_counter % divider == 0 ); 35 | case EVT_DOWN: 36 | return enc_direction == -1 && ( enc_counter % divider == 0 ); 37 | } 38 | return 0; 39 | } 40 | 41 | void Atm_encoder::action( int id ) { 42 | switch ( id ) { 43 | case LP_IDLE: 44 | enc_bits = ( ( enc_bits << 2 ) | ( digitalRead( pin1 ) << 1 ) | ( digitalRead( pin2 ) ) ) & 0x0f; 45 | enc_direction = enc_states[enc_bits]; 46 | if ( enc_direction != 0 ) { 47 | enc_counter = enc_counter + enc_direction; 48 | if ( ( enc_counter != 0 ) && ( enc_counter % divider == 0 ) ) { 49 | if ( !count( enc_direction ) ) { 50 | enc_direction = 0; 51 | } 52 | } 53 | } 54 | return; 55 | case ENT_UP: 56 | onup.push( state(), 1 ); 57 | return; 58 | case ENT_DOWN: 59 | ondown.push( state(), 0 ); 60 | return; 61 | } 62 | } 63 | 64 | Atm_encoder& Atm_encoder::range( int min, int max, bool wrap /* = false */ ) { 65 | if ( min > max ) { 66 | range_invert = true; 67 | this->min = max; 68 | this->max = min; 69 | } else { 70 | range_invert = false; 71 | this->min = min; 72 | this->max = max; 73 | } 74 | this->wrap = wrap; 75 | if ( value < min || value > max ) { 76 | value = min; 77 | } 78 | return *this; 79 | } 80 | 81 | Atm_encoder& Atm_encoder::set( int value ) { 82 | this->value = range_invert ? map( value, min, max, max, min ) : value; 83 | return *this; 84 | } 85 | 86 | Atm_encoder& Atm_encoder::onChange( Machine& machine, int event /* = 0 */ ) { 87 | onup.set( &machine, event ); 88 | ondown.set( &machine, event ); 89 | return *this; 90 | } 91 | 92 | Atm_encoder& Atm_encoder::onChange( atm_cb_push_t callback, int idx /* = 0 */ ) { 93 | onup.set( callback, idx ); 94 | ondown.set( callback, idx ); 95 | return *this; 96 | } 97 | 98 | Atm_encoder& Atm_encoder::onChange( bool status, Machine& machine, int event /* = 0 */ ) { 99 | if ( status ) { 100 | onup.set( &machine, event ); 101 | } else { 102 | ondown.set( &machine, event ); 103 | } 104 | return *this; 105 | } 106 | 107 | Atm_encoder& Atm_encoder::onChange( bool status, atm_cb_push_t callback, int idx /* = 0 */ ) { 108 | if ( status ) { 109 | onup.set( callback, idx ); 110 | } else { 111 | ondown.set( callback, idx ); 112 | } 113 | return *this; 114 | } 115 | 116 | int Atm_encoder::state( void ) { 117 | return range_invert ? map( value, min, max, max, min ) : value; 118 | } 119 | 120 | bool Atm_encoder::count( int direction ) { 121 | if ( (long)value + direction > max ) { 122 | if ( wrap ) { 123 | value = min; 124 | } else { 125 | return false; 126 | } 127 | } else if ( (long)value + direction < min ) { 128 | if ( wrap ) { 129 | value = max; 130 | } else { 131 | return false; 132 | } 133 | } else { 134 | value += direction; 135 | } 136 | return true; 137 | } 138 | 139 | Atm_encoder& Atm_encoder::trace( Stream& stream ) { 140 | Machine::setTrace( &stream, atm_serial_debug::trace, "ENCODER\0EVT_UP\0EVT_DOWN\0ELSE\0IDLE\0UP\0DOWN" ); 141 | return *this; 142 | } 143 | -------------------------------------------------------------------------------- /src/Atm_timer.cpp: -------------------------------------------------------------------------------- 1 | #include "Atm_timer.hpp" 2 | 3 | // Timer class that handles intervals from 1 millisecond up to 136 years 4 | // Combined with repeat( 65534 ) that makes a maximum of 8.9 million years 5 | 6 | #define DIVIDER 86400 // Number of seconds in a 24h day 7 | 8 | Atm_timer& Atm_timer::begin( uint32_t ms /* = ATM_TIMER_OFF */, uint16_t repeats /* = 1 */ ) { 9 | // clang-format off 10 | const static state_t state_table[] PROGMEM = { 11 | /* ON_ENTER ON_LOOP ON_EXIT EVT_DAYCNT EVT_DAYTIMER EVT_MSTIMER EVT_REPCNT EVT_STOP EVT_START EVT_TOGGLE ELSE */ 12 | /* IDLE */ -1, ATM_SLEEP, -1, -1, -1, -1, -1, -1, START, START, -1, 13 | /* START */ ENT_START, -1, -1, -1, -1, -1, -1, -1, WAITD, -1, WAITD, 14 | /* WAITD */ -1, -1, EXT_WAITD, WAITMS, WAITD, -1, -1, IDLE, START, IDLE, -1, 15 | /* WAITMS */ -1, -1, -1, -1, -1, TRIGGER, -1, IDLE, START, IDLE, -1, 16 | /* TRIGGER */ ENT_TRIGGER, -1, -1, -1, -1, -1, FINISH, IDLE, START, IDLE, START, 17 | /* FINISH */ ENT_FINISH, -1, -1, -1, -1, -1, -1, -1, START, -1, IDLE, 18 | }; 19 | // clang-format on 20 | Machine::begin( state_table, ELSE ); 21 | daytimer.set( (uint32_t)DIVIDER * 1000 ); // Always set to one day 22 | mstimer.set( ms ); 23 | daycounter.set( days = 0 ); 24 | repeat( repeat_cnt = repeats ); 25 | return *this; 26 | } 27 | 28 | int Atm_timer::event( int id ) { 29 | switch ( id ) { 30 | case EVT_REPCNT: 31 | return repcounter.expired(); 32 | case EVT_DAYCNT: 33 | return daycounter.expired(); 34 | case EVT_MSTIMER: 35 | return mstimer.expired( this ); 36 | case EVT_DAYTIMER: 37 | return daytimer.expired( this ); 38 | } 39 | return 0; 40 | } 41 | 42 | void Atm_timer::action( int id ) { 43 | switch ( id ) { 44 | case ENT_START: 45 | daycounter.set( days ); 46 | return; 47 | case EXT_WAITD: 48 | daycounter.decrement(); 49 | return; 50 | case ENT_TRIGGER: 51 | abscounter++; 52 | repcounter.decrement(); 53 | ontimer.push( repcounter.value, abscounter ); 54 | return; 55 | case ENT_FINISH: 56 | onfinish.push( 0 ); 57 | repcounter.set( repeat_cnt ); 58 | abscounter = 0; 59 | return; 60 | } 61 | } 62 | 63 | Atm_timer& Atm_timer::onTimer( atm_cb_push_t callback, int idx /* = 0 */ ) { 64 | ontimer.set( callback, idx ); 65 | return *this; 66 | } 67 | 68 | Atm_timer& Atm_timer::onTimer( Machine& machine, int event /* = 0 */ ) { 69 | ontimer.set( &machine, event ); 70 | return *this; 71 | } 72 | 73 | Atm_timer& Atm_timer::onFinish( atm_cb_push_t callback, int idx /* = 0 */ ) { 74 | onfinish.set( callback, idx ); 75 | return *this; 76 | } 77 | 78 | Atm_timer& Atm_timer::onFinish( Machine& machine, int event /* = 0 */ ) { 79 | onfinish.set( &machine, event ); 80 | return *this; 81 | } 82 | 83 | Atm_timer& Atm_timer::interval_seconds( uint32_t v ) { 84 | days = v / ( (uint32_t)DIVIDER ); 85 | daycounter.set( days ); // Determine how many days -> days => Set day counter 86 | mstimer.set( ( v - ( (uint32_t)days * DIVIDER ) ) * 1000 ); // And how many milliseconds left 87 | return *this; 88 | } 89 | 90 | Atm_timer& Atm_timer::interval_millis( uint32_t v ) { 91 | days = 0; 92 | daycounter.set( days ); 93 | mstimer.set( v ); 94 | return *this; 95 | } 96 | 97 | Atm_timer& Atm_timer::interval( uint32_t v ) { 98 | return interval_millis( v ); 99 | } 100 | 101 | Atm_timer& Atm_timer::repeat( uint16_t v /* = ATM_COUNTER_OFF */ ) { 102 | repeat_cnt = v; 103 | repcounter.set( v ); 104 | return *this; 105 | } 106 | 107 | uint32_t Atm_timer::left() { 108 | uint32_t r = daycounter.value * DIVIDER; 109 | r += mstimer.value - ( millis() - state_millis ); 110 | return current == WAITD || current == WAITMS ? r : 0; 111 | } 112 | 113 | Atm_timer& Atm_timer::start( void ) { 114 | trigger( EVT_START ); 115 | return *this; 116 | } 117 | 118 | Atm_timer& Atm_timer::stop( void ) { 119 | trigger( EVT_STOP ); 120 | return *this; 121 | } 122 | 123 | Atm_timer& Atm_timer::toggle( void ) { 124 | trigger( EVT_TOGGLE ); 125 | return *this; 126 | } 127 | 128 | Atm_timer& Atm_timer::trace( Stream& stream ) { 129 | setTrace( &stream, atm_serial_debug::trace, 130 | "TIMER\0EVT_DAYCNT\0EVT_DAYTIMER\0EVT_MSTIMER\0EVT_REPCNT\0EVT_OFF\0EVT_START\0EVT_TOGGLE\0" 131 | "ELSE\0IDLE\0START\0WAITD\0WAITMS\0TRIGGER\0FINISH" ); 132 | return *this; 133 | } 134 | -------------------------------------------------------------------------------- /src/Atm_comparator.cpp: -------------------------------------------------------------------------------- 1 | #include "Atm_comparator.hpp" 2 | 3 | Atm_comparator& Atm_comparator::begin( int attached_pin, int samplerate /* = 50 */ ) { 4 | // clang-format off 5 | const static state_t state_table[] PROGMEM = { 6 | /* ON_ENTER ON_LOOP ON_EXIT EVT_TRIGGER EVT_TIMER ELSE */ 7 | /* IDLE */ -1, -1, -1, -1, SAMPLE, -1, 8 | /* SAMPLE */ ENT_SAMPLE, -1, -1, SEND, -1, IDLE, 9 | /* SEND */ ENT_SEND, -1, -1, -1, -1, IDLE, 10 | }; 11 | // clang-format on 12 | Machine::begin( state_table, ELSE ); 13 | pin = attached_pin; 14 | timer.set( samplerate ); 15 | bitmap_sample = 0; 16 | bitmap_previous = 0; 17 | skip_mode = 0; 18 | return *this; 19 | } 20 | 21 | int Atm_comparator::event( int id ) { 22 | switch ( id ) { 23 | case EVT_TRIGGER: 24 | if ( bitmap_diff ) { 25 | return 1; 26 | } 27 | return 0; 28 | case EVT_TIMER: 29 | return timer.expired( this ); 30 | } 31 | return 0; 32 | } 33 | 34 | void Atm_comparator::action( int id ) { 35 | switch ( id ) { 36 | case ENT_SAMPLE: 37 | v_previous = v_sample; 38 | bitmap_previous = bitmap_sample; 39 | v_sample = sample(); 40 | bitmap( v_sample ); 41 | return; 42 | case ENT_SEND: 43 | int final_step = -1; 44 | if ( v_sample >= v_previous ) { 45 | for ( uint16_t i = 0; i < p_threshold_size; i++ ) { 46 | if ( ( bitmap_diff >> i ) & 1 ) { 47 | if ( skip_mode == 0 ) { 48 | onup.push( i, 1 ); 49 | } else { 50 | final_step = i; 51 | } 52 | } 53 | } 54 | } else { 55 | for ( int i = p_threshold_size; i >= 0; i-- ) { 56 | if ( ( bitmap_diff >> i ) & 1 ) { 57 | if ( skip_mode == 0 ) { 58 | ondown.push( i, 0 ); 59 | } else { 60 | final_step = i; 61 | } 62 | } 63 | } 64 | } 65 | if ( final_step > -1 ) { 66 | if ( v_sample >= v_previous ) { 67 | onup.push( final_step, 0 ); 68 | } else { 69 | ondown.push( final_step, 0 ); 70 | } 71 | } 72 | return; 73 | } 74 | } 75 | 76 | Atm_comparator& Atm_comparator::onChange( atm_cb_push_t callback, int idx /* = 0 */ ) { 77 | onup.set( callback, idx ); 78 | ondown.set( callback, idx ); 79 | return *this; 80 | } 81 | 82 | Atm_comparator& Atm_comparator::onChange( Machine& machine, int event /* = 0 */ ) { 83 | onup.set( &machine, event ); 84 | ondown.set( &machine, event ); 85 | return *this; 86 | } 87 | 88 | Atm_comparator& Atm_comparator::onChange( bool status, atm_cb_push_t callback, int idx /* = 0 */ ) { 89 | if ( status ) { 90 | onup.set( callback, idx ); 91 | } else { 92 | ondown.set( callback, idx ); 93 | } 94 | return *this; 95 | } 96 | 97 | Atm_comparator& Atm_comparator::onChange( bool status, Machine& machine, int event /* = 0 */ ) { 98 | if ( status ) { 99 | onup.set( &machine, event ); 100 | } else { 101 | ondown.set( &machine, event ); 102 | } 103 | return *this; 104 | } 105 | 106 | Atm_comparator& Atm_comparator::skip() { 107 | skip_mode = 1; 108 | return *this; 109 | } 110 | 111 | int Atm_comparator::read_sample() { 112 | return analogRead( pin ); 113 | } 114 | 115 | int Atm_comparator::avg() { 116 | uint16_t v = read_sample(); 117 | avg_buf_total = avg_buf_total + v - avg_buf[avg_buf_head]; 118 | avg_buf[avg_buf_head] = v; 119 | if ( avg_buf_head + 1 >= avg_buf_size ) { 120 | avg_buf_head = 0; 121 | } else { 122 | avg_buf_head++; 123 | } 124 | return avg_buf_total / avg_buf_size; 125 | } 126 | 127 | int Atm_comparator::sample() { 128 | return avg_buf_size > 0 ? avg() : read_sample(); 129 | } 130 | 131 | int Atm_comparator::state( void ) { 132 | return v_sample; 133 | } 134 | 135 | Atm_comparator& Atm_comparator::threshold( uint16_t* v, uint16_t size, bool catchUp /* = false */ ) { 136 | p_threshold = v; 137 | p_threshold_size = size / sizeof( uint16_t ); 138 | if ( !catchUp ) { 139 | v_sample = sample(); 140 | bitmap( v_sample ); 141 | v_previous = v_sample; 142 | bitmap_previous = bitmap_sample; 143 | } 144 | return *this; 145 | } 146 | 147 | Atm_comparator& Atm_comparator::average( uint16_t* v, uint16_t size ) { 148 | avg_buf = v; 149 | avg_buf_size = size / sizeof( uint16_t ); 150 | avg_buf_head = 0; 151 | avg_buf_total = 0; 152 | for ( uint16_t i = 0; i < avg_buf_size; i++ ) { 153 | avg_buf[i] = read_sample(); 154 | avg_buf_total += avg_buf[i]; 155 | } 156 | return *this; 157 | } 158 | 159 | Atm_comparator& Atm_comparator::bitmap( uint16_t v ) { 160 | bitmap_sample = 0; 161 | for ( uint8_t i = 0; i < p_threshold_size; i++ ) { 162 | if ( v >= p_threshold[i] ) bitmap_sample |= ( 1 << i ); 163 | } 164 | bitmap_diff = bitmap_sample ^ bitmap_previous; 165 | return *this; 166 | } 167 | 168 | Atm_comparator& Atm_comparator::trace( Stream& stream ) { 169 | setTrace( &stream, atm_serial_debug::trace, 170 | "EVT_TRIGGER\0EVT_TIMER\0ELSE\0" 171 | "IDLE\0SAMPLE\0SEND" ); 172 | return *this; 173 | } 174 | -------------------------------------------------------------------------------- /src/Atm_step.cpp: -------------------------------------------------------------------------------- 1 | #include "Atm_step.hpp" 2 | 3 | Atm_step& Atm_step::begin( void ) { 4 | // clang-format off 5 | const static state_t state_table[] PROGMEM = { 6 | /* ON_ENTER ON_LOOP ON_EXIT EVT_STEP EVT_BACK EVT_SWEEP EVT_LINEAR ELSE */ 7 | /* LINEAR */ -1, -1, -1, S0, R9, SWEEP, LINEAR, -1, 8 | /* S0 */ ENT_S0, -1, -1, S1, R0, SWEEP, LINEAR, -1, // Linear 9 | /* S1 */ ENT_S1, -1, -1, S2, R1, SWEEP, LINEAR, -1, 10 | /* S2 */ ENT_S2, -1, -1, S3, R2, SWEEP, LINEAR, -1, 11 | /* S3 */ ENT_S3, -1, -1, S4, R3, SWEEP, LINEAR, -1, 12 | /* S4 */ ENT_S4, -1, -1, S5, R4, SWEEP, LINEAR, -1, 13 | /* S5 */ ENT_S5, -1, -1, S6, R5, SWEEP, LINEAR, -1, 14 | /* S6 */ ENT_S6, -1, -1, S7, R6, SWEEP, LINEAR, -1, 15 | /* S7 */ ENT_S7, -1, -1, S8, R7, SWEEP, LINEAR, -1, 16 | /* S8 */ ENT_S8, -1, -1, S9, R8, SWEEP, LINEAR, -1, 17 | /* S9 */ ENT_S9, -1, -1, S0, R9, SWEEP, LINEAR, -1, 18 | /* R0 */ ENT_S0, -1, -1, S0, R9, SWEEP, LINEAR, -1, // Linear R 19 | /* R1 */ ENT_S1, -1, -1, S1, R0, SWEEP, LINEAR, -1, 20 | /* R2 */ ENT_S2, -1, -1, S2, R1, SWEEP, LINEAR, -1, 21 | /* R3 */ ENT_S3, -1, -1, S3, R2, SWEEP, LINEAR, -1, 22 | /* R4 */ ENT_S4, -1, -1, S4, R3, SWEEP, LINEAR, -1, 23 | /* R5 */ ENT_S5, -1, -1, S5, R4, SWEEP, LINEAR, -1, 24 | /* R6 */ ENT_S6, -1, -1, S6, R5, SWEEP, LINEAR, -1, 25 | /* R7 */ ENT_S7, -1, -1, S7, R6, SWEEP, LINEAR, -1, 26 | /* R8 */ ENT_S8, -1, -1, S8, R7, SWEEP, LINEAR, -1, 27 | /* R9 */ ENT_S9, -1, -1, S9, R8, SWEEP, LINEAR, -1, 28 | /* SWEEP */ -1, -1, -1, X0, X0, SWEEP, LINEAR, -1, 29 | /* X0 */ ENT_S0, -1, -1, X1, X1, SWEEP, LINEAR, -1, // Sweep 30 | /* X1 */ ENT_S1, -1, -1, X2, X2, SWEEP, LINEAR, -1, 31 | /* X2 */ ENT_S2, -1, -1, X3, X3, SWEEP, LINEAR, -1, 32 | /* X3 */ ENT_S3, -1, -1, X4, X4, SWEEP, LINEAR, -1, 33 | /* X4 */ ENT_S4, -1, -1, X5, X5, SWEEP, LINEAR, -1, 34 | /* X5 */ ENT_S5, -1, -1, X6, X6, SWEEP, LINEAR, -1, 35 | /* X6 */ ENT_S6, -1, -1, X7, X7, SWEEP, LINEAR, -1, 36 | /* X7 */ ENT_S7, -1, -1, X8, X8, SWEEP, LINEAR, -1, 37 | /* X8 */ ENT_S8, -1, -1, X9, X9, SWEEP, LINEAR, -1, 38 | /* X9 */ ENT_S9, -1, -1, XA, XA, SWEEP, LINEAR, -1, 39 | /* XA */ ENT_S8, -1, -1, XB, XB, SWEEP, LINEAR, -1, 40 | /* XB */ ENT_S7, -1, -1, XC, XC, SWEEP, LINEAR, -1, 41 | /* XC */ ENT_S6, -1, -1, XD, XD, SWEEP, LINEAR, -1, 42 | /* XD */ ENT_S5, -1, -1, XE, XE, SWEEP, LINEAR, -1, 43 | /* XE */ ENT_S4, -1, -1, XF, XF, SWEEP, LINEAR, -1, 44 | /* XF */ ENT_S3, -1, -1, XG, XG, SWEEP, LINEAR, -1, 45 | /* XG */ ENT_S2, -1, -1, XH, XH, SWEEP, LINEAR, -1, 46 | /* XH */ ENT_S1, -1, -1, X0, X0, SWEEP, LINEAR, -1, 47 | }; 48 | // clang-format on 49 | Machine::begin( state_table, ELSE ); 50 | return *this; 51 | } 52 | 53 | int Atm_step::event( int id ) { 54 | int on_enter = read_state( state_table + ( current * state_width ) + ATM_ON_ENTER ); 55 | switch ( id ) { 56 | case EVT_STEP: 57 | return ( current < R0 || current > R9 ) && ( on_enter > -1 ) && ( connector[on_enter].mode() == 0 ); 58 | case EVT_BACK: 59 | return ( current >= R0 && current <= R9 ) && ( on_enter > -1 ) && ( connector[on_enter].mode() == 0 ); 60 | } 61 | return 0; 62 | } 63 | 64 | void Atm_step::action( int id ) { 65 | if ( id > -1 ) { 66 | if ( connector[id].mode() ) { 67 | connector[id].push( id ); 68 | onstep.push( id ); 69 | } 70 | } 71 | } 72 | 73 | Atm_step& Atm_step::onStep( uint8_t id ) { 74 | connector[id].mode_flags = atm_connector::MODE_NULL; 75 | return *this; 76 | } 77 | 78 | Atm_step& Atm_step::onStep( uint8_t id, atm_cb_push_t callback, int idx /* = 0 */ ) { 79 | connector[id].set( callback, idx ); 80 | return *this; 81 | } 82 | 83 | Atm_step& Atm_step::onStep( uint8_t id, Machine& machine, int event /* = 0 */ ) { 84 | connector[id].set( &machine, event ); 85 | return *this; 86 | } 87 | 88 | Atm_step& Atm_step::onStep( atm_cb_push_t callback, int idx /* = 0 */ ) { 89 | onstep.set( callback, idx ); 90 | return *this; 91 | } 92 | 93 | Atm_step& Atm_step::onStep( Machine& machine, int event /* = 0 */ ) { 94 | onstep.set( &machine, event ); 95 | return *this; 96 | } 97 | 98 | int Atm_step::state( void ) { 99 | int on_enter = read_state( state_table + ( current * state_width ) + ATM_ON_ENTER ); 100 | return on_enter; 101 | } 102 | 103 | Atm_step& Atm_step::trace( Stream& stream ) { 104 | setTrace( &stream, atm_serial_debug::trace, 105 | "STEP\0EVT_STEP\0EVT_BACK\0EVT_SWEEP\0EVT_LINEAR\0ELSE\0" 106 | "LINEAR\0S0\0S1\0S2\0S3\0S4\0S5\0S6\0S7\0S8\0S9\0R0\0R1\0R2\0R3\0R4\0R5\0R6\0R7\0R8\0R9\0" 107 | "SWEEP\0X0\0X1\0X2\0X3\0X4\0X5\0X6\0X7\0X8\0X9\0XA\0XB\0XC\0XD\0XE\0XF\0XG\0XH" ); 108 | return *this; 109 | } 110 | -------------------------------------------------------------------------------- /src/Atm_button.cpp: -------------------------------------------------------------------------------- 1 | #include "Atm_button.hpp" 2 | 3 | // Add option for button press callback (for reading i2c buttons etc) 4 | 5 | Atm_button& Atm_button::begin( int attached_pin ) { 6 | // clang-format off 7 | const static state_t state_table[] PROGMEM = { 8 | /* Standard Mode: press/repeat */ 9 | /* ON_ENTER ON_LOOP ON_EXIT EVT_LMODE EVT_TIMER EVT_DELAY EVT_REPEAT EVT_PRESS EVT_RELEASE EVT_COUNTER EVT_AUTO ELSE */ 10 | /* IDLE */ -1, -1, -1, LIDLE, -1, -1, -1, WAIT, -1, -1, AUTO_ST, -1, 11 | /* WAIT */ -1, -1, -1, -1, PRESSED, -1, -1, -1, IDLE, -1, -1, -1, 12 | /* PRESSED */ ENT_PRESS, -1, -1, -1, -1, REPEAT, -1, -1, RELEASE, -1, -1, -1, 13 | /* REPEAT */ ENT_PRESS, -1, -1, -1, -1, -1, REPEAT, -1, RELEASE, -1, -1, -1, 14 | /* RELEASE */ ENT_RELEASE, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, IDLE, 15 | /* Long Press Mode: press/long press */ 16 | /* LIDLE */ -1, -1, -1, -1, -1, -1, -1, LWAIT, -1, -1, -1, -1, 17 | /* LWAIT */ ENT_LSTART, -1, -1, -1, LPRESSED, -1, -1, -1, LIDLE, -1, -1, -1, 18 | /* LPRESSED */ ENT_LCOUNT, -1, -1, -1, -1, LPRESSED, -1, -1, LRELEASE, WRELEASE, -1, -1, 19 | /* LRELEASE */ ENT_LRELEASE, -1, EXT_WRELEASE, -1, -1, -1, -1, -1, -1, -1, -1, LIDLE, 20 | /* WRELEASE */ ENT_LRELEASE, -1, EXT_WRELEASE, -1, -1, -1, -1, -1, LIDLE, -1, -1, -1, 21 | /* AUTO_ST */ ENT_AUTO, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, IDLE, 22 | }; 23 | // clang-format on 24 | Machine::begin( state_table, ELSE ); 25 | pin = attached_pin; 26 | counter_longpress.set( 0 ); 27 | timer_debounce.set( DEBOUNCE ); 28 | timer_delay.set( ATM_TIMER_OFF ); 29 | timer_repeat.set( ATM_TIMER_OFF ); 30 | timer_auto.set( ATM_TIMER_OFF ); 31 | pinMode( pin, INPUT_PULLUP ); 32 | return *this; 33 | } 34 | 35 | int Atm_button::event( int id ) { 36 | switch ( id ) { 37 | case EVT_LMODE: 38 | return counter_longpress.value > 0; 39 | case EVT_TIMER: 40 | return timer_debounce.expired( this ); 41 | case EVT_DELAY: 42 | return timer_delay.expired( this ); 43 | case EVT_REPEAT: 44 | return timer_repeat.expired( this ); 45 | case EVT_AUTO: 46 | return timer_auto.expired( this ); 47 | case EVT_PRESS: 48 | return !digitalRead( pin ); 49 | case EVT_RELEASE: 50 | return digitalRead( pin ); 51 | case EVT_COUNTER: 52 | return counter_longpress.expired(); 53 | } 54 | return 0; 55 | } 56 | 57 | void Atm_button::action( int id ) { 58 | int press; 59 | switch ( id ) { 60 | case ENT_PRESS: 61 | onpress.push( auto_press ); 62 | longpress[0].push( 1 ); 63 | return; 64 | case ENT_AUTO: 65 | onpress.push( 1 ); 66 | longpress[0].push( 1 ); 67 | return; 68 | case ENT_RELEASE: 69 | case EXT_WRELEASE: 70 | onrelease.push( 0 ); 71 | return; 72 | case ENT_LSTART: 73 | counter_longpress.set( longpress_max ); 74 | return; 75 | case ENT_LCOUNT: 76 | counter_longpress.decrement(); 77 | press = ( longpress_max - counter_longpress.value ); 78 | if ( onpress.mode() == atm_connector::MODE_PUSHCB ) { 79 | onpress.push( press * -1 ); 80 | } 81 | return; 82 | case ENT_LRELEASE: 83 | press = ( longpress_max - counter_longpress.value ); 84 | onpress.push( press ); 85 | if ( press == 1 || press == 2 ) { 86 | longpress[press-1].push( press ); 87 | } 88 | return; 89 | } 90 | } 91 | 92 | Atm_button& Atm_button::onPress( atm_cb_push_t callback, int idx /* = 0 */ ) { 93 | onpress.set( callback, idx ); 94 | return *this; 95 | } 96 | 97 | Atm_button& Atm_button::onPress( Machine& machine, int event /* = 0 */ ) { 98 | onpress.set( &machine, event ); 99 | return *this; 100 | } 101 | 102 | Atm_button& Atm_button::onPress( int id, atm_cb_push_t callback, int idx /* = 0 */ ) { 103 | if ( id == 1 || id == 2 ) 104 | longpress[id-1].set( callback, idx ); 105 | return *this; 106 | } 107 | 108 | Atm_button& Atm_button::onPress( int id, Machine& machine, int event /* = 0 */ ) { 109 | if ( id == 1 || id == 2 ) 110 | longpress[id-1].set( &machine, event ); 111 | return *this; 112 | } 113 | 114 | Atm_button& Atm_button::onRelease( atm_cb_push_t callback, int idx /* = 0 */ ) { 115 | onrelease.set( callback, idx ); 116 | return *this; 117 | } 118 | 119 | Atm_button& Atm_button::onRelease( Machine& machine, int event /* = 0 */ ) { 120 | onrelease.set( &machine, event ); 121 | return *this; 122 | } 123 | 124 | Atm_button& Atm_button::debounce( int delay ) { 125 | timer_debounce.set( delay ); 126 | return *this; 127 | } 128 | 129 | Atm_button& Atm_button::longPress( int max, int delay ) { 130 | longpress_max = max; 131 | counter_longpress.set( longpress_max ); 132 | timer_delay.set( delay ); 133 | return *this; 134 | } 135 | 136 | Atm_button& Atm_button::repeat( int delay /* = 500 */, int speed /* = 50 */ ) { 137 | timer_delay.set( delay ); 138 | timer_repeat.set( speed ); 139 | return *this; 140 | } 141 | 142 | Atm_button& Atm_button::autoPress( int delay, int press /* = 1 */ ) { 143 | auto_press = press; 144 | timer_auto.set( delay ); 145 | return *this; 146 | } 147 | 148 | Atm_button& Atm_button::trace( Stream& stream ) { 149 | setTrace( &stream, atm_serial_debug::trace, 150 | "BUTTON\0EVT_LMODE\0EVT_TIMER\0EVT_DELAY\0EVT_REPEAT\0EVT_PRESS\0EVT_RELEASE\0EVT_COUNTER\0EVT_" 151 | "AUTO_ST\0ELSE\0IDLE\0WAIT\0PRESSED\0REPEAT\0RELEASE\0LIDLE\0LWAIT\0LPRESSED\0LRELEASE\0WRELEASE\0AUTO" ); 152 | return *this; 153 | } 154 | -------------------------------------------------------------------------------- /src/Atm_controller.cpp: -------------------------------------------------------------------------------- 1 | #include "Atm_controller.hpp" 2 | 3 | const char Atm_controller::relOps[8] = "0=!<>-+"; 4 | 5 | Atm_controller& Atm_controller::begin( bool initialState /* = false */ ) { 6 | // clang-format off 7 | const static state_t state_table[] PROGMEM = { 8 | /* ON_ENTER ON_LOOP ON_EXIT EVT_ON EVT_OFF EVT_INPUT ELSE */ 9 | /* OFF */ ENT_OFF, -1, -1, ON, -1, OFF, -1, 10 | /* ON */ ENT_ON, -1, -1, -1, OFF, ON, -1, 11 | }; 12 | // clang-format on 13 | Machine::begin( state_table, ELSE ); 14 | last_state = -1; 15 | state( initialState ? ON : OFF ); 16 | indicator = -1; 17 | return *this; 18 | } 19 | 20 | int Atm_controller::event( int id ) { 21 | switch ( id ) { 22 | case EVT_ON: 23 | return eval_all(); 24 | case EVT_OFF: 25 | return !eval_all(); 26 | } 27 | return 0; 28 | } 29 | 30 | void Atm_controller::action( int id ) { 31 | switch ( id ) { 32 | case ENT_OFF: 33 | connector[last_state == current ? ON_INPUT_FALSE : ON_CHANGE_FALSE].push( state() ); 34 | if ( indicator > -1 ) digitalWrite( indicator, !LOW != !indicatorActiveLow ); 35 | last_state = current; 36 | return; 37 | case ENT_ON: 38 | if ( last_state != -1 ) connector[( last_state == current ) ? ON_INPUT_TRUE : ON_CHANGE_TRUE].push( state() ); 39 | if ( indicator > -1 ) digitalWrite( indicator, !HIGH != !indicatorActiveLow ); 40 | last_state = current; 41 | return; 42 | } 43 | } 44 | 45 | bool Atm_controller::eval_one( atm_connector& connector ) { 46 | switch ( connector.relOp() ) { 47 | case atm_connector::REL_EQ: 48 | return connector.pull() == connector.event; 49 | case atm_connector::REL_NEQ: 50 | return connector.pull() != connector.event; 51 | case atm_connector::REL_LT: 52 | return connector.pull() < connector.event; 53 | case atm_connector::REL_GT: 54 | return connector.pull() > connector.event; 55 | case atm_connector::REL_LTE: 56 | return connector.pull() <= connector.event; 57 | case atm_connector::REL_GTE: 58 | return connector.pull() >= connector.event; 59 | } 60 | return connector.pull(); 61 | } 62 | 63 | bool Atm_controller::eval_all() { 64 | bool r = eval_one( operand[0] ); 65 | for ( uint8_t i = 1; i < ATM_CONDITION_OPERAND_MAX; i++ ) { 66 | if ( operand[i].mode() ) { 67 | switch ( operand[i].logOp() ) { 68 | case atm_connector::LOG_AND: 69 | r = r && eval_one( operand[i] ); 70 | break; 71 | case atm_connector::LOG_OR: 72 | r = r || eval_one( operand[i] ); 73 | break; 74 | case atm_connector::LOG_XOR: 75 | r = !r != !eval_one( operand[i] ); 76 | break; 77 | } 78 | } 79 | } 80 | return r; 81 | } 82 | 83 | Atm_controller& Atm_controller::led( int led, bool activeLow /* = false */ ) { 84 | indicator = led; 85 | indicatorActiveLow = activeLow; 86 | pinMode( indicator, OUTPUT ); 87 | return *this; 88 | } 89 | 90 | Atm_controller& Atm_controller::onChange( bool status, atm_cb_push_t callback, int idx /* = 0 */ ) { 91 | connector[status ? ON_CHANGE_TRUE : ON_CHANGE_FALSE].set( callback, idx ); 92 | return *this; 93 | } 94 | 95 | Atm_controller& Atm_controller::onChange( bool status, Machine& machine, int event /* = 0 */ ) { 96 | connector[status ? ON_CHANGE_TRUE : ON_CHANGE_FALSE].set( &machine, event ); 97 | return *this; 98 | } 99 | 100 | Atm_controller& Atm_controller::onChange( atm_cb_push_t callback, int idx /* = 0 */ ) { 101 | connector[ON_CHANGE_TRUE].set( callback, idx ); 102 | connector[ON_CHANGE_FALSE].set( callback, idx ); 103 | return *this; 104 | } 105 | 106 | Atm_controller& Atm_controller::onChange( Machine& machine, int event /* = 0 */ ) { 107 | connector[ON_CHANGE_TRUE].set( &machine, event ); 108 | connector[ON_CHANGE_FALSE].set( &machine, event ); 109 | return *this; 110 | } 111 | 112 | Atm_controller& Atm_controller::onInput( bool status, atm_cb_push_t callback, int idx /* = 0 */ ) { 113 | connector[status ? ON_INPUT_TRUE : ON_INPUT_FALSE].set( callback, idx ); 114 | return *this; 115 | } 116 | 117 | Atm_controller& Atm_controller::onInput( bool status, Machine& machine, int event /* = 0 */ ) { 118 | connector[status ? ON_INPUT_TRUE : ON_INPUT_FALSE].set( &machine, event ); 119 | return *this; 120 | } 121 | 122 | Atm_controller& Atm_controller::IF( Machine& machine, char relOp /* = '>' */, int match /* = 0 */ ) { 123 | return OP( atm_connector::LOG_AND, machine, relOp, match ); 124 | } 125 | 126 | Atm_controller& Atm_controller::IF( atm_cb_pull_t callback, int idx /* = 0 */ ) { 127 | return OP( atm_connector::LOG_AND, callback, idx ); 128 | } 129 | 130 | Atm_controller& Atm_controller::AND( Machine& machine, char relOp /* = '>' */, int match /* = 0 */ ) { 131 | return OP( atm_connector::LOG_AND, machine, relOp, match ); 132 | } 133 | 134 | Atm_controller& Atm_controller::AND( atm_cb_pull_t callback, int idx /* = 0 */ ) { 135 | return OP( atm_connector::LOG_AND, callback, idx ); 136 | } 137 | 138 | Atm_controller& Atm_controller::OR( Machine& machine, char relOp /* = '>' */, int match /* = 0 */ ) { 139 | return OP( atm_connector::LOG_OR, machine, relOp, match ); 140 | } 141 | 142 | Atm_controller& Atm_controller::OR( atm_cb_pull_t callback, int idx /* = 0 */ ) { 143 | return OP( atm_connector::LOG_OR, callback, idx ); 144 | } 145 | 146 | Atm_controller& Atm_controller::XOR( Machine& machine, char relOp /* = '>' */, int match /* = 0 */ ) { 147 | return OP( atm_connector::LOG_XOR, machine, relOp, match ); 148 | } 149 | 150 | Atm_controller& Atm_controller::XOR( atm_cb_pull_t callback, int idx /* = 0 */ ) { 151 | return OP( atm_connector::LOG_XOR, callback, idx ); 152 | } 153 | 154 | Atm_controller& Atm_controller::OP( char logOp, Machine& machine, char relOp, int match ) { 155 | for ( uint8_t i = 0; i < ATM_CONDITION_OPERAND_MAX; i++ ) { 156 | if ( operand[i].mode() == atm_connector::MODE_NULL ) { // Pick the first free slot 157 | operand[i].set( &machine, match, logOp, (int)( strchr( relOps, relOp ) - relOps ) ); 158 | break; 159 | } 160 | } 161 | return *this; 162 | } 163 | 164 | Atm_controller& Atm_controller::OP( char logOp, atm_cb_pull_t callback, int idx ) { 165 | for ( uint8_t i = 0; i < ATM_CONDITION_OPERAND_MAX; i++ ) { 166 | if ( operand[i].mode() == atm_connector::MODE_NULL ) { // Pick the first free slot 167 | operand[i].set( callback, idx ); 168 | break; 169 | } 170 | } 171 | return *this; 172 | } 173 | 174 | Atm_controller& Atm_controller::trace( Stream& stream ) { 175 | Machine::setTrace( &stream, atm_serial_debug::trace, "CONTROLLER\0EVT_ON\0EVT_OFF\0EVT_INPUT\0ELSE\0OFF\0ON" ); 176 | return *this; 177 | } 178 | -------------------------------------------------------------------------------- /src/Atm_fade.cpp: -------------------------------------------------------------------------------- 1 | #include "Atm_fade.hpp" 2 | 3 | Atm_fade& Atm_fade::begin( int attached_pin ) { 4 | // clang-format off 5 | const static state_t state_table[] PROGMEM = { 6 | /* ON_ENTER ON_LOOP ON_EXIT EVT_CNT_FADE EVT_TM_FADE EVT_TM_ON EVT_TM_OFF EVT_CNT_RPT EVT_ON EVT_OFF EVT_BLINK EVT_TOGGLE EVT_TOGGLE_BLINK ELSE */ 7 | /* IDLE */ ENT_OFF, ATM_SLEEP, -1, -1, -1, -1, -1, -1,OSTARTU, IDLE, START, OSTARTU, START, -1, // LED off 8 | /* ON */ ENT_ON, ATM_SLEEP, -1, -1, -1, -1, -1, -1, -1,OSTARTD, START, OSTARTD, OSTARTD, -1, // LED on 9 | /* START */ ENT_OFF, -1, -1, -1, -1, -1, -1, -1,OSTARTU, IDLE, START, IDLE, IDLE, STARTU, // Start fading 10 | /* STARTU */ ENT_START, -1, -1, -1, -1, -1, UP, -1,OSTARTU, IDLE, START, IDLE, IDLE, -1, 11 | /* UP */ ENT_UP, -1, -1, STARTD, UP, -1, -1, -1,OSTARTU, IDLE, START, IDLE, IDLE, -1, 12 | /* STARTD */ ENT_START, -1, -1, -1, -1, DOWN, -1, -1,OSTARTU, IDLE, START, IDLE, IDLE, -1, 13 | /* DOWN */ ENT_DOWN, -1, -1, REPEAT, DOWN, -1, -1, -1,OSTARTU, IDLE, START, IDLE, IDLE, -1, 14 | /* REPEAT */ ENT_REPEAT, -1, -1, -1, -1, -1, -1, DONE,OSTARTU, IDLE, START, IDLE, IDLE, STARTU, 15 | /* DONE */ ENT_DONE, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, IDLE, 16 | /* OSTARTU*/ ENT_START, -1, -1, -1, -1, -1, -1, -1, -1, IDLE, START, IDLE, IDLE, OUP, 17 | /* OUP */ ENT_UP, -1, -1, ON, OUP, -1, -1, -1, -1, IDLE, START, IDLE, IDLE, -1, 18 | /* OSTARTD*/ ENT_START, -1, -1, -1, -1, -1, -1, -1,OSTARTU, IDLE, START, IDLE, IDLE, ODOWN, 19 | /* ODOWN */ ENT_DOWN, -1, -1, IDLE, ODOWN, -1, -1, -1,OSTARTU, IDLE, START, IDLE, IDLE, -1, 20 | 21 | }; 22 | // clang-format on 23 | Machine::begin( state_table, ELSE ); 24 | pin = attached_pin; 25 | pinMode( pin, OUTPUT ); 26 | timer_fade.set( 0 ); // Number of ms per slope step (slope duration: rate * 32 ms) 27 | timer_on.set( 500 ); // Plateau between slopes (in which led is fully on) 28 | timer_off.set( 500 ); // Pause between slopes (in which led is fully off) 29 | counter_fade.set( SLOPE_SIZE ); 30 | counter_repeat.set( ATM_COUNTER_OFF ); 31 | repeat_count = ATM_COUNTER_OFF; 32 | return *this; 33 | } 34 | 35 | Atm_fade& Atm_fade::blink( uint32_t duration, uint32_t pause_duration, uint16_t repeat_count /* = ATM_COUNTER_OFF */ ) { 36 | blink( duration ); // Time in which led is fully on 37 | pause( pause_duration ); 38 | repeat( repeat_count ); 39 | return *this; 40 | } 41 | 42 | Atm_fade& Atm_fade::blink( uint32_t duration ) { 43 | timer_on.set( duration ); // Plateau between slopes (in which led is fully on) 44 | return *this; 45 | } 46 | 47 | Atm_fade& Atm_fade::blink( void ) { 48 | trigger( EVT_BLINK ); 49 | return *this; 50 | } 51 | 52 | Atm_fade& Atm_fade::pause( uint32_t duration ) { // Pause between slopes (in which led is fully off) 53 | timer_off.set( duration ? duration : 1 ); // Make sure off_timer is never 0 (work around) 54 | return *this; 55 | } 56 | 57 | Atm_fade& Atm_fade::fade( int fade ) { 58 | timer_fade.set( fade >= 0 ? fade : ATM_TIMER_OFF ); // Number of ms per slope step (slope duration: rate * 32 ms) 59 | return *this; 60 | } 61 | 62 | Atm_fade& Atm_fade::repeat( uint16_t repeat ) { 63 | counter_repeat.set( repeat_count = repeat ); 64 | return *this; 65 | } 66 | 67 | int Atm_fade::event( int id ) { 68 | switch ( id ) { 69 | case EVT_TM_FADE: 70 | return timer_fade.expired( this ); 71 | case EVT_TM_ON: 72 | return timer_on.expired( this ); 73 | case EVT_TM_OFF: 74 | return timer_off.expired( this ); 75 | case EVT_CNT_FADE: 76 | return counter_fade.expired(); 77 | case EVT_CNT_RPT: 78 | return counter_repeat.expired(); 79 | } 80 | return 0; 81 | } 82 | 83 | void Atm_fade::action( int id ) { 84 | switch ( id ) { 85 | case ENT_ON: 86 | analogWrite( pin, 255 ); 87 | return; 88 | case ENT_REPEAT: 89 | counter_repeat.decrement(); 90 | return; 91 | case ENT_OFF: 92 | counter_repeat.set( repeat_count ); 93 | analogWrite( pin, 0 ); 94 | return; 95 | case ENT_START: 96 | counter_fade.set( SLOPE_SIZE ); 97 | return; 98 | case ENT_UP: 99 | analogWrite( pin, slope[SLOPE_SIZE - counter_fade.value] ); 100 | counter_fade.decrement(); 101 | return; 102 | case ENT_DOWN: 103 | analogWrite( pin, slope[counter_fade.value - 1] ); 104 | counter_fade.decrement(); 105 | return; 106 | case ENT_DONE: 107 | onfinish.push( 0 ); 108 | return; 109 | } 110 | } 111 | 112 | Atm_fade& Atm_fade::on( void ) { 113 | trigger( EVT_ON ); 114 | return *this; 115 | } 116 | 117 | Atm_fade& Atm_fade::off( void ) { 118 | trigger( EVT_OFF ); 119 | return *this; 120 | } 121 | 122 | Atm_fade& Atm_fade::toggle( void ) { 123 | trigger( EVT_TOGGLE ); 124 | return *this; 125 | } 126 | 127 | Atm_fade& Atm_fade::toggleBlink( void ) { 128 | trigger( EVT_TOGGLE_BLINK ); 129 | return *this; 130 | } 131 | 132 | Atm_fade& Atm_fade::start( void ) { 133 | trigger( EVT_BLINK ); 134 | return *this; 135 | } 136 | 137 | Atm_fade& Atm_fade::onFinish( Machine& machine, int event /* = 0 */ ) { 138 | onfinish.set( &machine, event ); 139 | return *this; 140 | } 141 | 142 | Atm_fade& Atm_fade::onFinish( atm_cb_push_t callback, int idx /* = 0 */ ) { 143 | onfinish.set( callback, idx ); 144 | return *this; 145 | } 146 | 147 | Atm_fade& Atm_fade::trace( Stream& stream ) { 148 | setTrace( &stream, atm_serial_debug::trace, 149 | "FADE\0EVT_CNT_FADE\0EVT_TM_FADE\0EVT_TM_ON\0EVT_TM_OFF\0EVT_CNT_RPT\0EVT_ON\0EVT_OFF\0EVT_BLINK\0EVT_TOGGLE\0EVT_TOGGLE_BLINK\0ELSE\0" 150 | "IDLE\0ON\0START\0STARTU\0UP\0STARTD\0DOWN\0REPEAT\0DONE\0OSTARTU\0OUP\0OSTARTD\0ODOWN" ); 151 | return *this; 152 | } 153 | -------------------------------------------------------------------------------- /src/Atm_player.cpp: -------------------------------------------------------------------------------- 1 | #include "Atm_player.hpp" 2 | 3 | /* Add optional parameters for the state machine to begin() 4 | * Add extra initialization code 5 | */ 6 | 7 | Atm_player& Atm_player::begin( int pin /* = - 1 */ ) { 8 | // clang-format off 9 | const static state_t state_table[] PROGMEM = { 10 | /* ON_ENTER ON_LOOP ON_EXIT EVT_START EVT_STOP EVT_TOGGLE EVT_TIMER EVT_EOPAT EVT_REPEAT ELSE */ 11 | /* IDLE */ ENT_IDLE, ATM_SLEEP, -1, START, -1, START, -1, -1, -1, -1, 12 | /* START */ ENT_START, -1, -1, -1, -1, -1, -1, -1, -1, SOUND, 13 | /* SOUND */ ENT_SOUND, -1, -1, -1, IDLE, IDLE, QUIET, -1, -1, -1, 14 | /* QUIET */ ENT_QUIET, -1, -1, -1, IDLE, IDLE, NEXT, -1, -1, -1, 15 | /* NEXT */ ENT_NEXT, -1, -1, -1, IDLE, IDLE, -1, REPEAT, -1, SOUND, 16 | /* REPEAT */ ENT_REPEAT, -1, -1, -1, IDLE, IDLE, -1, -1, FINISH, START, 17 | /* FINISH */ ENT_FINISH, -1, -1, -1, IDLE, -1, -1, -1, IDLE, START, 18 | }; 19 | // clang-format on 20 | Machine::begin( state_table, ELSE ); 21 | Atm_player::pin = pin; 22 | speed( 100 ); 23 | pitch( 100 ); 24 | repeat( 1 ); 25 | play( 880, 50 ); 26 | return *this; 27 | } 28 | 29 | /* Add C++ code for each event (input) 30 | * The code must return 1 if the event should be triggered 31 | */ 32 | 33 | int Atm_player::event( int id ) { 34 | switch ( id ) { 35 | case EVT_START: 36 | return 0; 37 | case EVT_TIMER: 38 | return timer.expired( this ); 39 | case EVT_EOPAT: 40 | if ( patternwidth == 32 ) { 41 | return ( step * 3 * sizeof( uint32_t ) ) >= patternsize; 42 | } else { 43 | return ( step * 3 * sizeof( int ) ) >= patternsize; 44 | } 45 | case EVT_REPEAT: 46 | return counter_repeat.expired(); 47 | } 48 | return 0; 49 | } 50 | 51 | /* Add C++ code for each action 52 | * This generates the 'output' for the state machine 53 | */ 54 | 55 | void Atm_player::action( int id ) { 56 | switch ( id ) { 57 | case ENT_FINISH: 58 | push( connectors, ON_FINISH, 0, 0, 0 ); 59 | return; 60 | case ENT_IDLE: 61 | #ifndef ATM_PLAYER_DISABLE_TONE 62 | if ( pin >= 0 ) noTone( pin ); // Tone takes up 7 bytes extra SRAM 63 | #endif 64 | counter_repeat.set( repeatCount ); 65 | return; 66 | case ENT_START: 67 | step = 0; 68 | counter_repeat.decrement(); 69 | return; 70 | case ENT_SOUND: 71 | if ( patternwidth == 32 ) { 72 | uint32_t v = pattern32[step * 3] * (uint32_t)pitchFactor; 73 | push( connectors, ON_NOTE, true, v & 0xFFFF, v >> 16 & 0xFFFF ); 74 | #ifndef ATM_PLAYER_DISABLE_TONE 75 | if ( pin >= 0 ) tone( pin, pattern32[step * 3] * pitchFactor ); 76 | #endif 77 | timer.set( pattern32[step * 3 + 1] * speedFactor ); 78 | } else { 79 | push( connectors, ON_NOTE, true, pattern16[step * 3] * pitchFactor, 1 ); 80 | #ifndef ATM_PLAYER_DISABLE_TONE 81 | if ( pin >= 0 ) tone( pin, pattern16[step * 3] * pitchFactor ); 82 | #endif 83 | timer.set( pattern16[step * 3 + 1] * speedFactor ); 84 | } 85 | return; 86 | case ENT_QUIET: 87 | if ( patternwidth == 32 ) { 88 | uint32_t v = pattern32[step * 3] * (uint32_t)pitchFactor; 89 | push( connectors, ON_NOTE, false, v & 0xFFFF, v >> 16 & 0xFFFF ); 90 | #ifndef ATM_PLAYER_DISABLE_TONE 91 | if ( pin >= 0 ) noTone( pin ); 92 | #endif 93 | timer.set( pattern32[step * 3 + 2] * speedFactor ); 94 | } else { 95 | push( connectors, ON_NOTE, false, pattern16[step * 3] * pitchFactor, 0 ); 96 | #ifndef ATM_PLAYER_DISABLE_TONE 97 | if ( pin >= 0 ) noTone( pin ); 98 | #endif 99 | timer.set( pattern16[step * 3 + 2] * speedFactor ); 100 | } 101 | return; 102 | case ENT_NEXT: 103 | step++; 104 | return; 105 | case ENT_REPEAT: 106 | return; 107 | } 108 | } 109 | 110 | /* How many times to repeat the pattern 111 | * 112 | */ 113 | 114 | Atm_player& Atm_player::repeat( uint16_t v /* = -1 */) { 115 | counter_repeat.set( repeatCount = v ); 116 | return *this; 117 | } 118 | 119 | Atm_player& Atm_player::speed( float v ) { 120 | speedFactor = 100 / v; 121 | return *this; 122 | } 123 | 124 | Atm_player& Atm_player::pitch( float v ) { 125 | pitchFactor = v / 100; 126 | return *this; 127 | } 128 | 129 | Atm_player& Atm_player::start( void ) { 130 | trigger( EVT_START ); 131 | return *this; 132 | } 133 | 134 | Atm_player& Atm_player::stop( void ) { 135 | trigger( EVT_STOP ); 136 | return *this; 137 | } 138 | 139 | Atm_player& Atm_player::toggle( void ) { 140 | trigger( EVT_TOGGLE ); 141 | return *this; 142 | } 143 | 144 | /* Sets the pattern and pattern length (in bytes) 145 | * 146 | */ 147 | 148 | Atm_player& Atm_player::play( int* pat, int patsize ) { 149 | patternwidth = 16; 150 | pattern16 = pat; 151 | patternsize = patsize; 152 | counter_repeat.set( repeatCount ); 153 | step = 0; 154 | return *this; 155 | } 156 | 157 | Atm_player& Atm_player::play( uint32_t* pat, int patsize ) { 158 | patternwidth = 32; 159 | pattern32 = pat; 160 | patternsize = patsize; 161 | counter_repeat.set( repeatCount ); 162 | step = 0; 163 | return *this; 164 | } 165 | 166 | Atm_player& Atm_player::play( int freq, int period, int pause /* = 0 */ ) { 167 | patternwidth = 16; 168 | stub[0] = freq; 169 | stub[1] = period; 170 | stub[2] = pause; 171 | pattern16 = stub; 172 | patternsize = 3 * sizeof( int ); 173 | step = 0; 174 | return *this; 175 | } 176 | 177 | /* Optionally override the default trigger() method 178 | * Control what triggers your machine can and cannot process 179 | */ 180 | 181 | Atm_player& Atm_player::trigger( int event ) { 182 | Machine::trigger( event ); 183 | return *this; 184 | } 185 | 186 | /* Optionally override the default state() method 187 | * Control what the machine returns when another process requests its state() 188 | */ 189 | 190 | int Atm_player::state( void ) { 191 | return Machine::state(); 192 | } 193 | 194 | /* Nothing customizable below this line 195 | ************************************************************************************************ 196 | */ 197 | 198 | /* onFinish() push connector variants ( slots 1, autostore 0, broadcast 0 ) 199 | * 200 | * Usage in action() handler: push( connectors, ON_FINISH, 0, v, up) 201 | */ 202 | 203 | Atm_player& Atm_player::onFinish( Machine& machine, int event ) { 204 | onPush( connectors, ON_FINISH, 0, 1, 1, machine, event ); 205 | return *this; 206 | } 207 | Atm_player& Atm_player::onFinish( atm_cb_push_t callback, int idx ) { 208 | onPush( connectors, ON_FINISH, 0, 1, 1, callback, idx ); 209 | return *this; 210 | } 211 | 212 | /* onNote() push connector variants ( slots 2, autostore 0, broadcast 0 ) 213 | * 214 | * Usage in action() handler: push( connectors, ON_NOTE, sub, v, up) 215 | */ 216 | 217 | Atm_player& Atm_player::onNote( Machine& machine, int event ) { 218 | onPush( connectors, ON_NOTE, 0, 2, 1, machine, event ); 219 | return *this; 220 | } 221 | Atm_player& Atm_player::onNote( atm_cb_push_t callback, int idx ) { 222 | onPush( connectors, ON_NOTE, 0, 2, 1, callback, idx ); 223 | return *this; 224 | } 225 | Atm_player& Atm_player::onNote( int sub, Machine& machine, int event ) { 226 | onPush( connectors, ON_NOTE, sub, 2, 0, machine, event ); 227 | return *this; 228 | } 229 | Atm_player& Atm_player::onNote( int sub, atm_cb_push_t callback, int idx ) { 230 | onPush( connectors, ON_NOTE, sub, 2, 0, callback, idx ); 231 | return *this; 232 | } 233 | 234 | /* State trace method 235 | * Sets the symbol table and the default logging method for serial monitoring 236 | */ 237 | 238 | Atm_player& Atm_player::trace( Stream& stream ) { 239 | Machine::setTrace( &stream, atm_serial_debug::trace, 240 | "PLAYER\0EVT_START\0EVT_STOP\0EVT_TOGGLE\0EVT_TIMER\0EVT_EOPAT\0EVT_REPEAT\0ELSE\0IDLE\0START\0SOUND\0QUIET\0NEXT\0REPEAT\0FINISH" ); 241 | return *this; 242 | } 243 | -------------------------------------------------------------------------------- /src/Atm_led.cpp: -------------------------------------------------------------------------------- 1 | #include "Atm_led.hpp" 2 | 3 | Atm_led& Atm_led::begin( int attached_pin, bool activeLow ) { 4 | // clang-format off 5 | static const state_t state_table[] PROGMEM = { 6 | /* ON_ENTER ON_LOOP ON_EXIT EVT_ON_TIMER EVT_OFF_TIMER EVT_WT_TIMER EVT_COUNTER EVT_ON EVT_OFF EVT_BLINK EVT_TOGGLE EVT_TOGGLE_BLINK ELSE */ 7 | /* IDLE */ ENT_INIT, ATM_SLEEP, -1, -1, -1, -1, -1, WT_ON, -1, WT_START, ON, WT_START, -1, // LED off 8 | /* ON */ ENT_ON, ATM_SLEEP, -1, -1, -1, -1, -1, -1, OFF, WT_START, OFF, OFF, -1, // LED on 9 | /* START */ ENT_ON, -1, -1, BLINK_OFF, -1, -1, -1, WT_ON, OFF, -1, OFF, OFF, -1, // Start blinking 10 | /* BLINK_OFF */ ENT_OFF, -1, -1, -1, LOOP, -1, -1, WT_ON, OFF, -1, OFF, OFF, -1, 11 | /* LOOP */ -1, -1, -1, -1, -1, -1, DONE, WT_ON, OFF, -1, OFF, OFF, START, 12 | /* DONE */ -1, -1, EXT_CHAIN, -1, OFF, -1, -1, WT_ON, OFF, WT_START, OFF, OFF, -1, // Wait after last blink 13 | /* OFF */ ENT_OFF, -1, -1, -1, -1, -1, -1, WT_ON, OFF, WT_START, -1, -1, IDLE, // All off -> IDLE 14 | /* WT_ON */ -1, -1, -1, -1, -1, ON, -1, WT_ON, OFF, WT_START, -1, -1, -1, // LEAD for ON 15 | /* WT_START */ -1, -1, -1, -1, -1, START, -1, WT_ON, OFF, WT_START, -1, -1, -1, // LEAD for BLINK 16 | }; 17 | // clang-format on 18 | Machine::begin( state_table, ELSE ); 19 | pin = attached_pin; 20 | this->activeLow = activeLow; 21 | level = 255; 22 | toLow = 0; 23 | toHigh = 255; 24 | wrap = false; 25 | pinMode( pin, OUTPUT ); 26 | digitalWrite( pin, activeLow ? HIGH : LOW ); 27 | on_timer.set( 500 ); 28 | off_timer.set( 500 ); 29 | pwm( 512, 1 ); 30 | lead_timer.set( 0 ); 31 | repeat_count = ATM_COUNTER_OFF; 32 | counter.set( repeat_count ); 33 | while ( state() != 0 ) cycle(); 34 | return *this; 35 | } 36 | 37 | Atm_led& Atm_led::pwm( uint16_t width, float freq ) { 38 | 39 | if ( freq > -1 ) { 40 | this->freq = freq; 41 | } else { 42 | freq = this->freq; 43 | } 44 | this->width = width; 45 | float cycle_width = 1000 / freq; 46 | on_timer.set( cycle_width / 1024 * this->width ); 47 | off_timer.set( cycle_width / 1024 * ( 1024 - this->width ) ); 48 | return *this; 49 | } 50 | 51 | Atm_led& Atm_led::frequency( float freq ) { 52 | 53 | this->freq = freq; 54 | float cycle_width = 1000 / freq; 55 | on_timer.set( cycle_width / 1024 * this->width ); 56 | off_timer.set( cycle_width / 1024 * ( 1024 - this->width ) ); 57 | return *this; 58 | } 59 | 60 | int Atm_led::event( int id ) { 61 | switch ( id ) { 62 | case EVT_ON_TIMER: 63 | return on_timer.expired( this ); 64 | case EVT_OFF_TIMER: 65 | return off_timer.expired( this ); 66 | case EVT_WT_TIMER: 67 | return lead_timer.expired( this ); 68 | case EVT_COUNTER: 69 | return counter.expired(); 70 | } 71 | return 0; 72 | } 73 | 74 | void Atm_led::action( int id ) { 75 | switch ( id ) { 76 | case ENT_INIT: 77 | counter.set( repeat_count ); 78 | return; 79 | case ENT_ON: 80 | if ( on_timer.value > 0 ) { // Never turn if on_timer is zero (duty cycle 0 must be dark) 81 | if ( activeLow ) { 82 | digitalWrite( pin, LOW ); 83 | } else { 84 | if ( level == toHigh ) { 85 | digitalWrite( pin, HIGH ); 86 | } else { 87 | analogWrite( pin, mapLevel( level ) ); 88 | } 89 | } 90 | } 91 | return; 92 | case ENT_OFF: 93 | counter.decrement(); 94 | if ( !activeLow ) { 95 | digitalWrite( pin, LOW ); 96 | } else { 97 | if ( level == toHigh ) { 98 | digitalWrite( pin, HIGH ); 99 | } else { 100 | analogWrite( pin, mapLevel( level ) ); 101 | } 102 | } 103 | return; 104 | case EXT_CHAIN: 105 | onfinish.push( 0 ); 106 | return; 107 | } 108 | } 109 | 110 | int Atm_led::mapLevel( int level ) { 111 | if ( levelMapSize ) { 112 | return levelMap[level]; 113 | } else { 114 | return map( level, toLow, toHigh, 0, 255 ); 115 | } 116 | } 117 | 118 | Atm_led& Atm_led::on( void ) { 119 | trigger( EVT_ON ); 120 | return *this; 121 | } 122 | 123 | Atm_led& Atm_led::off( void ) { 124 | trigger( EVT_OFF ); 125 | return *this; 126 | } 127 | 128 | Atm_led& Atm_led::toggle( void ) { 129 | trigger( EVT_TOGGLE ); 130 | return *this; 131 | } 132 | 133 | Atm_led& Atm_led::toggleBlink( void ) { 134 | trigger( EVT_TOGGLE_BLINK ); 135 | return *this; 136 | } 137 | 138 | Atm_led& Atm_led::start( void ) { 139 | trigger( EVT_BLINK ); 140 | return *this; 141 | } 142 | 143 | Atm_led& Atm_led::onFinish( Machine& machine, int event /* = 0 */ ) { 144 | onfinish.set( &machine, event ); 145 | return *this; 146 | } 147 | 148 | Atm_led& Atm_led::onFinish( atm_cb_push_t callback, int idx /* = 0 */ ) { 149 | onfinish.set( callback, idx ); 150 | return *this; 151 | } 152 | 153 | Atm_led& Atm_led::blink( uint32_t duration, uint32_t pause_duration, uint16_t repeat_count /* = ATM_COUNTER_OFF */ ) { 154 | blink( duration ); // Time in which led is fully on 155 | pause( pause_duration ); 156 | repeat( repeat_count ); 157 | return *this; 158 | } 159 | 160 | Atm_led& Atm_led::blink( uint32_t duration ) { 161 | on_timer.set( duration ); // Time in which led is fully on 162 | return *this; 163 | } 164 | 165 | Atm_led& Atm_led::blink( void ) { 166 | trigger( EVT_BLINK ); 167 | return *this; 168 | } 169 | 170 | Atm_led& Atm_led::range( int toLow, int toHigh, bool wrap /* = false */ ) { 171 | this->toLow = toLow; 172 | this->toHigh = toHigh; 173 | this->wrap = wrap; 174 | level = toHigh; 175 | return *this; 176 | } 177 | 178 | Atm_led& Atm_led::levels( unsigned char* map, int mapsize, bool wrap /* = false */ ) { 179 | this->levelMap = map; 180 | levelMapSize = mapsize; 181 | range( 0, mapsize - 1, wrap ); 182 | return *this; 183 | } 184 | 185 | Atm_led& Atm_led::pause( uint32_t duration ) { // Time in which led is fully off 186 | off_timer.set( duration ? duration : 1 ); // Make sure off_timer is never 0 (work around) 187 | return *this; 188 | } 189 | 190 | Atm_led& Atm_led::fade( int fade ) { 191 | return *this; 192 | } // Dummy for method compatibility with Atm_fade 193 | 194 | Atm_led& Atm_led::lead( uint32_t ms ) { 195 | lead_timer.set( ms ); 196 | return *this; 197 | } 198 | 199 | Atm_led& Atm_led::repeat( uint16_t repeat ) { 200 | counter.set( repeat_count = repeat ); 201 | return *this; 202 | } 203 | 204 | int Atm_led::brightness( int level /* = -1 */ ) { 205 | if ( level > -1 ) { 206 | this->level = level; 207 | if ( current == ON || current == START ) { 208 | analogWrite( pin, mapLevel( level ) ); 209 | } 210 | } 211 | return this->level; 212 | } 213 | 214 | int Atm_led::brighten( int v ) { 215 | if ( abs( v ) == 1 ) { 216 | int br = (int)this->level + v; 217 | if ( br > toHigh ) 218 | br = wrap ? toLow : toHigh; 219 | if ( br < toLow ) 220 | br = wrap ? toHigh : toLow; 221 | brightness( br ); 222 | } 223 | return this->level; 224 | } 225 | 226 | Atm_led& Atm_led::trigger( int event ) { 227 | if ( event > ELSE ) { 228 | brighten( event == EVT_BRUP ? 1 : -1 ); 229 | } else { 230 | Machine::trigger( event ); 231 | } 232 | return *this; 233 | } 234 | 235 | Atm_led& Atm_led::trace( Stream& stream ) { 236 | setTrace( &stream, atm_serial_debug::trace, 237 | "LED\0EVT_ON_TIMER\0EVT_OFF_TIMER\0EVT_WT_TIMER\0EVT_COUNTER\0EVT_ON\0EVT_OFF\0EVT_" 238 | "BLINK\0EVT_TOGGLE\0EVT_TOGGLE_BLINK\0ELSE\0" 239 | "IDLE\0ON\0START\0BLINK_OFF\0LOOP\0DONE\0OFF\0WT_ON\0WT_START" ); 240 | return *this; 241 | } 242 | -------------------------------------------------------------------------------- /src/Machine.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Automaton.cpp - Reactive State Machine Framework for Arduino. 3 | Published under the MIT License (MIT), Copyright (c) 2015-2016, J.P. van der Landen 4 | */ 5 | 6 | #include "Automaton.h" 7 | 8 | /* The Machine class is a base class for creating and running State Machines 9 | * 10 | ********************************************************************************************* 11 | * 12 | * Machine::state( void ) - Retrieves the current state for the machine 13 | * 14 | * (may be overridden by a subclass in which case it may return something else, like a value ) 15 | */ 16 | 17 | int Machine::state() { 18 | return current; 19 | } 20 | 21 | /* 22 | * Machine::state( state ) - Sets the next state for the machine 23 | * 24 | */ 25 | 26 | Machine& Machine::state( int state ) { 27 | next = state; 28 | last_trigger = -1; 29 | flags &= ~ATM_SLEEP_FLAG; 30 | return *this; 31 | } 32 | 33 | /* 34 | * Machine::trigger( evt ) - Triggers an event for the machine 35 | * 36 | * The machine is cycled for maximum of 8 times until it is actively listening for the event 37 | * Then the event is triggered followed by two more cycles to process the event and the 38 | * following state change. 39 | * 40 | */ 41 | 42 | Machine& Machine::trigger( int evt /* = 0 */ ) { 43 | int new_state; 44 | int max_cycle = 8; 45 | do { 46 | flags &= ~ATM_SLEEP_FLAG; 47 | cycle(); 48 | new_state = read_state( state_table + ( current * state_width ) + evt + ATM_ON_EXIT + 1 ); 49 | } while ( --max_cycle && ( new_state == -1 || next_trigger != -1 ) ); 50 | if ( new_state > -1 ) { 51 | next_trigger = evt; 52 | flags &= ~ATM_SLEEP_FLAG; 53 | cycle(); // Pick up the trigger 54 | flags &= ~ATM_SLEEP_FLAG; 55 | cycle(); // Process the state change 56 | } 57 | return *this; 58 | } 59 | 60 | /* 61 | * Machine::setTrace( stream, callback, symbols ) - Sets up state tracing for the machine 62 | * 63 | * Connects a stream object, a callback (atm_serial_debug) and a symbol table (string) to the object 64 | * 65 | */ 66 | 67 | Machine& Machine::setTrace( Stream* stream, swcb_sym_t callback, const char symbols[] ) { 68 | callback_trace = callback; 69 | stream_trace = stream; 70 | this->symbols = symbols; 71 | return *this; 72 | } 73 | 74 | /* 75 | * Machine::sleep( v ) - Sets or returns the current sleep flag setting 76 | * 77 | */ 78 | 79 | uint8_t Machine::sleep( int8_t v /* = 1 */ ) { 80 | if ( v > -1 ) flags = v ? flags | ATM_SLEEP_FLAG : flags & ~ATM_SLEEP_FLAG; 81 | return ( flags & ATM_SLEEP_FLAG ) > 0; 82 | } 83 | 84 | /* 85 | * Machine::begin( state_table, width ) - Initializes the state table and sets the sleep flag 86 | * 87 | */ 88 | 89 | Machine& Machine::begin( const state_t* tbl, int width ) { 90 | state_table = tbl; 91 | state_width = ATM_ON_EXIT + width + 2; 92 | flags &= ~ATM_SLEEP_FLAG; 93 | automaton.add( *this, false ); 94 | current = -1; 95 | next = 0; 96 | next_trigger = -1; 97 | return *this; 98 | } 99 | 100 | /* 101 | * Machine::onPush( connectors, id, sub, slots, multi, dest, arg ) - Registers a connector destination 102 | * 103 | * connectors Connector table 104 | * id Connector id 105 | * sub Connector sub id (for multi-slot connectors) 106 | * slots Number of slots reserved for this connector 107 | * multi Register multiple (all) slots in one call 108 | * dest Destination: Machine object or callback 109 | * arg Argument for machine (event) or callback (idx) 110 | * 111 | */ 112 | 113 | void Machine::onPush( atm_connector connectors[], int id, int sub, int slots, int fill, Machine& machine, int event ) { 114 | if ( sub == -1 ) { // auto store 115 | sub = 0; 116 | for ( int i = 0; i < slots; i++ ) { 117 | if ( connectors[id + i].mode() == 0 ) { // Find a free slot 118 | sub = i; 119 | } 120 | } 121 | } 122 | if ( slots > 1 && fill ) { 123 | for ( int i = 0; i < slots; i++ ) { 124 | connectors[id + i].set( &machine, event ); 125 | } 126 | } else { 127 | connectors[id + sub].set( &machine, event ); 128 | } 129 | } 130 | 131 | void Machine::onPush( atm_connector connectors[], int id, int sub, int slots, int fill, atm_cb_push_t callback, int idx ) { 132 | if ( sub == -1 ) { // auto store 133 | sub = 0; 134 | for ( int i = 0; i < slots; i++ ) { 135 | if ( connectors[id + i].mode() == 0 ) { // Find a free slot 136 | sub = i; 137 | } 138 | } 139 | } 140 | if ( slots > 1 && fill ) { 141 | for ( int i = 0; i < slots; i++ ) { 142 | connectors[id + i].set( callback, idx ); 143 | } 144 | } else { 145 | connectors[id + sub].set( callback, idx ); 146 | } 147 | } 148 | 149 | /* 150 | * Machine::push( connectors, id, sub, v, up ) - Pushes an action through the specified connector 151 | * 152 | * connectors Connector table 153 | * id Connector id 154 | * sub Connector sub id (for multi-slot connectors) 155 | * v Value to pass to a callback as 'v' 156 | * up Value to pass to a callback as 'up' 157 | * 158 | */ 159 | 160 | void Machine::push( atm_connector connectors[], int id, int sub, int v, int up ) { 161 | if ( ( id & ATM_BROADCAST ) > 0 ) { 162 | id = id & ~ATM_BROADCAST; 163 | for ( int i = id; i < sub; i++ ) { 164 | connectors[id + i].push( v, up ); 165 | } 166 | } else { 167 | connectors[id + sub].push( v, up ); 168 | } 169 | } 170 | 171 | /* 172 | * Machine::mapSymbol( id, map ) - Maps a number ( event/state ) to a symbol 173 | * 174 | * 0 Machine class name (e.g. LED) 175 | * 1..ELSE Event name (e.g. EVT_TIMER) 176 | * ELSE.. State name (e.g. IDLE) 177 | * 178 | */ 179 | 180 | const char* Machine::mapSymbol( int id, const char map[] ) { 181 | int cnt = 0; 182 | int i = 0; 183 | if ( id == -1 ) return "*NONE*"; 184 | if ( id == 0 ) return map; 185 | while ( 1 ) { 186 | if ( map[i] == '\0' && ++cnt == id ) { 187 | i++; 188 | break; 189 | } 190 | i++; 191 | } 192 | return &map[i]; 193 | } 194 | 195 | /* 196 | * Machine::cycle( time ) - Executes one cycle of a State Machine 197 | * 198 | * For every state change: 199 | * - Calls the ON_SWITCH action 200 | * - Calls the state trace function (if connected) 201 | * - Calls the previous state's ON_EXIT action 202 | * - Changes the active state (current) to the new 203 | * - Calls the new state's ON_ENTER action 204 | * 205 | * For every 'normal' cycle: 206 | * - Executes the ON_LOOP action 207 | * - Scans the event columns in the current table and calls active events 208 | * 209 | * If the 'time' argument is given, loops until that time has passed 210 | * otherwise executes only one cycle of the machine 211 | */ 212 | 213 | Machine& Machine::cycle( uint32_t time /* = 0 */ ) { 214 | uint32_t cycle_start = millis(); 215 | do { 216 | if ( ( flags & ( ATM_SLEEP_FLAG | ATM_CYCLE_FLAG ) ) == 0 ) { 217 | cycles++; 218 | flags |= ATM_CYCLE_FLAG; 219 | if ( next != -1 ) { 220 | action( ATM_ON_SWITCH ); 221 | if ( callback_trace ) { 222 | callback_trace( stream_trace, *this, symbols, mapSymbol( current == -1 ? current : current + state_width - ATM_ON_EXIT, symbols ), 223 | mapSymbol( next == -1 ? next : next + state_width - ATM_ON_EXIT, symbols ), 224 | mapSymbol( last_trigger == -1 ? -1 : last_trigger + 1, symbols ), millis() - state_millis, cycles ); 225 | } 226 | if ( current > -1 ) action( read_state( state_table + ( current * state_width ) + ATM_ON_EXIT ) ); 227 | current = next; 228 | next = -1; 229 | state_millis = millis(); 230 | action( read_state( state_table + ( current * state_width ) + ATM_ON_ENTER ) ); 231 | if ( read_state( state_table + ( current * state_width ) + ATM_ON_LOOP ) == ATM_SLEEP ) { 232 | flags |= ATM_SLEEP_FLAG; 233 | } else { 234 | flags &= ~ATM_SLEEP_FLAG; 235 | } 236 | cycles = 0; 237 | } 238 | state_t i = read_state( state_table + ( current * state_width ) + ATM_ON_LOOP ); 239 | if ( i != -1 ) { 240 | action( i ); 241 | } 242 | for ( i = ATM_ON_EXIT + 1; i < state_width; i++ ) { 243 | state_t next_state = read_state( state_table + ( current * state_width ) + i ); 244 | if ( ( next_state != -1 ) && ( i == state_width - 1 || event( i - ATM_ON_EXIT - 1 ) || next_trigger == i - ATM_ON_EXIT - 1 ) ) { 245 | state( next_state ); 246 | last_trigger = i - ATM_ON_EXIT - 1; 247 | next_trigger = -1; 248 | break; 249 | } 250 | } 251 | flags &= ~ATM_CYCLE_FLAG; 252 | } 253 | } while ( millis() - cycle_start < time ); 254 | return *this; 255 | } 256 | --------------------------------------------------------------------------------