Last active
September 1, 2020 12:28
-
-
Save fcostin/851c1b4d1e3cb75ba972408151f185ba to your computer and use it in GitHub Desktop.
hypothetical refactor of self-opening trash can state machine
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 2020/09/01 - this code is placed in the public domain. | |
// | |
// This is a hypothetical refactor of state machine code from | |
// ivanilves's fun self-opening trash can project. | |
// | |
// Ref: https://github.com/ivanilves/arduino-sketches/blob/d0e965ebae1b98fa23b833fed2e2e28b21ef8863/basurito/basurito.ino | |
// | |
// The code below is incomplete, has not been tested and will not work! it's a sketch of an idea. | |
// | |
// Rough principles of this refactor: | |
// * define a finite set of explicit enum values for states, instead of using many bools | |
// * push all the state machine transition logic into pure function | |
// | |
// Details lost in translation: | |
// * controlling the LED | |
// There are 5 possible states. | |
// Note: there are two kinds of opening state, | |
// depending on if we are opening in response | |
// to button press (OPENING_MANUAL) or if we | |
// are opening because we decided to based on | |
// distance sensor (OPENING_AUTO). | |
typedef enum { | |
CLOSED, | |
OPENING_AUTO, | |
OPENING_MANUAL, | |
CLOSING, | |
OPEN | |
} state_t; | |
// inputs_t contains all inputs required to compute our successor state | |
typedef struct { | |
state_t state; | |
int distance; | |
int srv_pos; | |
bool button_pressed; | |
} inputs_t; | |
// outputs_t will hold the successor state we decided & any actions requested | |
typedef struct { | |
state_t state; | |
int srv_pos; | |
int delay; | |
int lowpower_delay; | |
} outputs_t; | |
// advance_state computes the successor state & any requested actions. | |
// Structured as a pure function. | |
outputs_t advance_state(inputs_t x) { | |
outputs_t succ; | |
succ.state = x.state; | |
succ.srv_pos = x.srv_pos; | |
succ.delay = 0; | |
succ.lowpower_delay = 0; | |
if ((x.state == CLOSED) && (x.distance >= MIN_DIST) && (x.distance <= MAX_DIST)) { | |
succ.state = OPENING_AUTO; | |
} else if ((x.state == CLOSED) && x.button_pressed) { | |
succ.state = OPENING_MANUAL; | |
} else if ((x.state == OPEN) && x.button_pressed) { | |
succ.state = CLOSING; | |
} else if (((x.state == OPENING_AUTO) || (x.state == OPENING_MANUAL)) && (x.srv_pos < SRV_OPEN)) { | |
succ.srv_pos = x.srv_pos + 1; | |
succ.delay = OPENING_DELAY; | |
} else if ((x.state == OPENING_MANUAL) && (x.srv_pos >= SRV_OPEN)) { | |
succ.state = OPEN; | |
succ.srv_pos = SRV_OPEN; | |
succ.delay = OPENING_DELAY; | |
} else if ((x.state == OPENING_AUTO) && (x.srv_pos >= SRV_OPEN)) { | |
succ.state = CLOSING; | |
succ.srv_pos = SRV_OPEN; | |
succ.delay = OPENING_DELAY; | |
succ.lowpower_delay = SLEEP_4S; | |
} else if ((x.state == CLOSING) && (x.srv_pos > SRV_CLOSED)) { | |
succ.srv_pos = x.srv_pos - 1; | |
succ.delay = CLOSING_DELAY; | |
} else if ((x.state == CLOSING) && (x.srv_pos <= SRV_CLOSED)) { | |
succ.state = CLOSED; | |
succ.srv_pos = SRV_CLOSED; | |
succ.delay = CLOSING_DELAY; | |
} else if ((x.state == CLOSED) || (x.state == OPEN)) { | |
succ.state = state; | |
succ.lowpower_delay = SLEEP_500MS; | |
} | |
return succ; | |
}; | |
// Globals used to maintain state between ticks. | |
state_t current_state; | |
int srv_pos; | |
void setup() { | |
// ... put setup here ... | |
} | |
void loop() { | |
// Gather all inputs | |
inputs_t x; | |
x.state = current_state; | |
x.distance = calculateDistance(); // fixme: sensing every tick may be wasteful | |
x.srv_pos = srv_pos; | |
x.button_pressed = buttonPressed(); | |
// Compute successor state | |
outputs_t succ; | |
succ = advance_state(x); | |
// Execute requested actions | |
if (succ.srv_pos != x.srv_pos) { | |
srv.write(succ.srv_pos); | |
srv_pos = succ.srv_pos; | |
} | |
current_state = succ.state; | |
if (succ.delay > 0) { | |
delay(succ.delay); | |
} | |
if (succ.lowpower_delay > 0) { | |
LowPower.powerDown(succ.lowpower_delay, ADC_OFF, BOD_OFF); | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment