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.