Skip to content

Instantly share code, notes, and snippets.

@FevenKitsune
Last active December 1, 2022 20:35
Show Gist options
  • Select an option

  • Save FevenKitsune/5b0a939920fc556e3006af376108b9a9 to your computer and use it in GitHub Desktop.

Select an option

Save FevenKitsune/5b0a939920fc556e3006af376108b9a9 to your computer and use it in GitHub Desktop.
CircuitPython example of a finite state machine.
"""
Finite State Machine Example
A state machine is a programming architecture commonly used to easily produce predictable and reliable code.
https://en.wikipedia.org/wiki/Finite-state_machine
The most important part to producing an FSM is to break your task into distinct states,
with triggers that switch the state your machine is currently in.
This can be modeled with a flowchart.
Below is the code for the following flowchart:
== MOTION SENSING LATCH ==
U/S = Ultrasonic Sensor
STATES: TRIGGERS:
0: Checking sensor - U/S < Target
1: Double-check sensor - U/S > Target
Check LED value - LED Off
2: Turn LED off - LED On
3: Turn LED on
4: Wait for sensor reset
5: Double-check reset
┌[S0]────┐ ┌[S1]────────┐
│Checking├──U/S < TARGET─►│Double-check│
│Sensor │ │Sensor + ├────────┐
│ │◄─U/S > TARGET──┤Check LED │ │
└────────┘ │value ├──────┐ │
▲ └────────────┘ │ │
┌─U/S > TARGET─┘ │ │
│ ┌[S3]────┐ │ │
│ │Turn LED│◄─LED Off─┘ │
┌[S5]───┐ ┌[S4]────┐ ┌─┤on │ │
│Double-├──U/S < TARGET─►│Wait for│◄┘ └────────┘ │
│check │ │sensor │ │
│reset │◄─U/S > TARGET──┤reset │◄┐ ┌[S2]────┐ │
└───────┘ └────────┘ └─┤Turn LED│◄─LED On────┘
│off │
└────────┘
"""
# imports
import time
import board
import digitalio
import adafruit_hcsr04
# variables declared here are only defined once at the start of the program.
sonar = adafruit_hcsr04.HCSR04(trigger_pin=board.D10, echo_pin=board.D9)
led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT
# state tracker. the core of a state machine architecture. the value of this variable determines the subroutine that is executed.
state = 0
# if you intend to use a specific number multiple times in your program, it's a good idea to declare it as a variable so if you wish to change it,
# you only need to change a single value here.
TRIGGER_DISTANCE = 8.0
while True:
# It's a good idea to sample your variable once at the beginning to ensure the value you evaluate stays the same throughout the cycle.
# Sampling the ultrasonic sensor too fast can additionally lead to errors.
sensor_distance = sonar.distance
if state == 0:
"""STATE 0
Initialized, figure out what's happening with the sensor.
"""
if sensor_distance < TRIGGER_DISTANCE:
# Proximity sensor has been triggered! Switch to next state
state = 1
# Using elif is a good idea to ensure the sensor_distance is updated before another state check is run.
# elif (else if) means if the first if statement is true and state is set to 1, the program will cycle
# back to the beginning of the while True before jumping to state == 1.
elif state == 1:
"""STATE 1
Something has been detected! Lets check if it's a false positive by checking the value again.
Lets also figure out if the LED is on or off and switch to the correct state that toggles that light.
"""
if sensor_distance < TRIGGER_DISTANCE:
# Proximity sensor has been triggered! Check LED state and switch to appropriate state.
if led.value == True:
# LED is on, turn it off.
state = 2
elif led.value == False:
# LED is off, turn it on.
state = 3
else:
# False positive! Switch back to state 1...
state = 1
elif state == 2:
"""STATE 2
LED is currently on, request to turn it off.
"""
led.value = False
# Goto state 4, wait for proximity to reset.
state = 4
elif state == 3:
"""STATE 3
LED is currently off, request to turn it on.
"""
led.value = True
# Goto state 4, wait for proximity to reset.
state = 4
elif state == 4:
"""STATE 4
Wait for proximity sensor to reset before arming again.
"""
if sensor_distance > TRIGGER_DISTANCE:
# Proximity sensor has been reset! Switch to state 5 to validate.
state = 5
elif state == 5:
"""STATE 5
Check for false positive before rearming sensor!
"""
if sensor_distance > TRIGGER_DISTANCE:
# Priximity sensor is still reset! Switch back to original state.
state = 1
else:
# Likely a false positive! Reset back to state 4.
state = 4
print((sensor_distance,))
time.sleep(0.05)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment