pimoroni-pico/micropython/examples/breakout_encoder_wheel/stop_watch.py

125 wiersze
4.8 KiB
Python

import math
import time
from pimoroni_i2c import PimoroniI2C
from pimoroni import BREAKOUT_GARDEN_I2C_PINS # or PICO_EXPLORER_I2C_PINS or HEADER_I2C_PINS
from breakout_encoder_wheel import BreakoutEncoderWheel, CENTRE, NUM_LEDS
"""
Display a circular stop-watch on the Encoder Wheel's LED ring.
Press the centre button to start the stopwatch, then again to pause and resume.
Press Ctrl+C to stop the program.
"""
# Constants
BRIGHTNESS = 1.0 # The brightness of the LEDs when the stopwatch is running
UPDATES = 50 # How many times the LEDs will be updated per second
MINUTE_UPDATES = UPDATES * 60 # How many times the LEDs will be updated per minute
HOUR_UPDATES = MINUTE_UPDATES * 60 # How many times the LEDs will be updated per hour
UPDATE_RATE_US = 1000000 // UPDATES
IDLE_PULSE_MIN = 0.2 # The brightness (between 0.0 and 1.0) that the idle pulse will go down to
IDLE_PULSE_MAX = 0.5 # The brightness (between 0.0 and 1.0) that the idle pulse will go up to
IDLE_PULSE_TIME = 2 # The time (in seconds) to perform a complete idle pulse
UPDATES_PER_PULSE = IDLE_PULSE_TIME * UPDATES
IDLE, COUNTING, PAUSED = range(3) # The state constants used for program flow
# Create a new BreakoutEncoderWheel
i2c = PimoroniI2C(**BREAKOUT_GARDEN_I2C_PINS)
wheel = BreakoutEncoderWheel(i2c)
# Variables
state = IDLE
idle_update = 0
second_update = 0
minute_update = 0
hour_update = 0
last_centre_pressed = False
# Simple function to clamp a value between a minimum and maximum
def clamp(n, smallest, largest):
return max(smallest, min(n, largest))
# Sleep until a specific time in the future. Use this instead of time.sleep() to correct for
# inconsistent timings when dealing with complex operations or external communication
def sleep_until(end_time):
time_to_sleep = time.ticks_diff(end_time, time.ticks_us())
if time_to_sleep > 0:
time.sleep_us(time_to_sleep)
# Record the current time
current_time = time.ticks_us()
# Run the update loop forever
while True:
# Read whether or not the wheen centre has been pressed
centre_pressed = wheel.pressed(CENTRE)
if centre_pressed and centre_pressed != last_centre_pressed:
if state == IDLE: # If we're currently idle, switch to counting
second_update = 0
minute_update = 0
hour_update = 0
state = COUNTING
elif state == COUNTING: # If we're counting, switch to paused
state = PAUSED
elif state == PAUSED: # If we're paused, switch back to counting
state = COUNTING
last_centre_pressed = centre_pressed
# If we're idle, perform a pulsing animation to show the stopwatch is ready to go
if state == IDLE:
percent_along = min(idle_update / UPDATES_PER_PULSE, 1.0)
brightness = ((math.cos(percent_along * math.pi * 2) + 1.0) / 2.0) * ((IDLE_PULSE_MAX - IDLE_PULSE_MIN)) + IDLE_PULSE_MIN
# Update all the LEDs
for i in range(NUM_LEDS):
wheel.set_hsv(i, 0.0, 0.0, brightness)
wheel.show()
# Advance to the next update, wrapping around to zero if at the end
idle_update += 1
if idle_update >= UPDATES_PER_PULSE:
idle_update = 0
# If we're counting, perform the stopwatch animation
elif state == COUNTING:
# Calculate how many LED channels should light, as a proportion of a second, minute, and hour
r_to_light = min(second_update / UPDATES, 1.0) * 24
g_to_light = min(minute_update / MINUTE_UPDATES, 1.0) * 24
b_to_light = min(hour_update / HOUR_UPDATES, 1.0) * 24
# Set each LED, such that ones below the current time are fully lit, ones after
# are off, and the one at the transition is at a percentage of the brightness
for i in range(NUM_LEDS):
r = int(clamp(r_to_light - i, 0.0, 1.0) * BRIGHTNESS * 255)
g = int(clamp(g_to_light - i, 0.0, 1.0) * BRIGHTNESS * 255)
b = int(clamp(b_to_light - i, 0.0, 1.0) * BRIGHTNESS * 255)
wheel.set_rgb(i, r, g, b)
wheel.show()
# Advance the second updates count, wrapping around to zero if at the end
second_update += 1
if second_update >= UPDATES:
second_update = 0
# Advance the minute updates count, wrapping around to zero if at the end
minute_update += 1
if minute_update >= MINUTE_UPDATES:
minute_update = 0
# Advance the hour updates count, wrapping around to zero if at the end
hour_update += 1
if hour_update >= HOUR_UPDATES:
hour_update = 0
# Sleep until the next update, accounting for how long the above operations took to perform
current_time = time.ticks_add(current_time, UPDATE_RATE_US)
sleep_until(current_time)