circuitpython_brute-force.jpg
You can open up a safe with explosives but it's easier to open if you have the combination

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.

Discussion

This method has pros and cons. It's simple for small, simple machines but as the machine's size and complexity increases so does the code length and complexity. Even for this machine, while not being overly large or complex, the code is getting a bit obtuse.

This guide was first published on Dec 29, 2018. It was last updated on Dec 29, 2018.

This page (Brute Force) was last updated on Oct 15, 2021.

Text editor powered by tinymce.