Last active
December 1, 2022 20:35
-
-
Save FevenKitsune/5b0a939920fc556e3006af376108b9a9 to your computer and use it in GitHub Desktop.
CircuitPython example of a finite 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
| """ | |
| 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