Implementing this project, we have a large conditional structure in the main while True
loop with an if
clause for each state. Each time through the loop, only the clause corresponding to the current state is executed.
There's some oddity around the switch. Specifically the PAUSED state is handled separately: if the switch is pressed (and therefore its value fell) we return to the state which was paused, as well as resuming any audio that was playing and servo motion.
If it hasn't been just pressed we see if its still being held down and has been for a second. In that case we transition to the RESET state.
Since the pause operation applies to all but the WAITING state, it's handled outside any state: state, audio, and servo motion are saved (and paused/stopped) and we transition to the PAUSED state. This happens when the switch is pressed, in all but the waiting state (or the paused state as it was already considered previously).
The rest of the states are handled more conventionally. There is an if clause for each state that determines what happens while in that state, as well as when to move to another state and what happens as we do.
For example, in the WAITING state, nothing happens until the switch is pressed or the time reaches 10 seconds to midnight on Dec. 31. When either of those happen the countdown clip starts playing, the servo starts turning, the rainbow effect is initialized, and the time to stop the drop is set. This last bit is required since the switch can be used to start the drop at any time. Finally, the DROPPING state is transitioned to.
The remaining states differ in the details, but the general structure is similar.
while True: now = time.monotonic() t = rtc.datetime switch.update() if state == PAUSED_STATE: log("Paused") if switch.fell: if audio.paused: audio.resume() servo.throttle = paused_servo paused_servo = 0.0 state = paused_state elif not switch.value: if now - switch_pressed_at > 1.0: state = RESET_STATE continue if switch.fell and state != WAITING_STATE: switch_pressed_at = now paused_state = state if audio.playing: audio.pause() paused_servo = servo.throttle servo.throttle = 0.0 state = PAUSED_STATE continue if state == WAITING_STATE: log("Waiting") if switch.fell or (t.tm_mday == 31 and t.tm_mon == 12 and t.tm_hour == 23 and t.tm_min == 59 and t.tm_sec == 50): start_playing('./countdown.wav') servo.throttle = DROP_THROTTLE rainbow = rainbow_lamp(range(0, 256, 2)) log("10 seconds to midnight") rainbow_time = now + 0.1 drop_finish_time = now + DROP_DURATION state = DROPPING_STATE elif state == DROPPING_STATE: log("Dropping") if now >= drop_finish_time: log("***Midnight") servo.throttle = 0.0 stop_playing() start_playing('./Auld_Lang_Syne.wav') reset_fireworks(now) firework_stop_time = now + FIREWORKS_DURATION state = BURST_STATE continue if now >= rainbow_time: next(rainbow) rainbow_time = now + 0.1 elif state == BURST_STATE: log("Burst") if burst(now): state = SHOWER_STATE shower_count = 0 elif state == SHOWER_STATE: log("Shower") if shower(now): if now >= firework_stop_time: state = IDLE_STATE else: state = BURST_STATE reset_fireworks(now) elif state == IDLE_STATE: log("Idle") elif state == RESET_STATE: log("Reset") strip.fill(0) strip.brightness = 1.0 strip.show() if audio.playing: audio.stop() servo.throttle = RAISE_THROTTLE state = RAISING_STATE elif state == RAISING_STATE: log("Raise") if switch.rose: servo.throttle = 0.0 state = WAITING_STATE
Notice that at first it looks like there's no way out of the IDLE state, but remember that near the top of the loop is the way to get into the PAUSED state by pressing the switch. This is the way out of IDLE: press and hold the switch to reset the system and raise the ball. That will result in the machine getting into the WAITING state.
Above is just the main loop that implements the core of the machine. The rest of the code can be found in the zip that you can download on the Code page.
Page last edited March 08, 2024
Text editor powered by tinymce.