Skip to content

Commit

Permalink
updating content
Browse files Browse the repository at this point in the history
  • Loading branch information
dmccreary committed Jan 9, 2025
1 parent 13d4e07 commit 1dc8286
Show file tree
Hide file tree
Showing 12 changed files with 610 additions and 1 deletion.
Binary file added docs/kits/led-strips/led-strip-layout.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
169 changes: 169 additions & 0 deletions docs/kits/stopwatch/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# Stopwatch

## Overview

Here is a two-button stopwatch that uses the OLED display.
The first button is called the **Start/Stop** button. When you first press start it starts to add up the time since it was pressed. Pressing it again will stop collecting time but still remember the total accumulated time while the stopwatch was running.

The second button is the **Reset** button. When you press Reset the
total time accumulated will be reset back to 0.

Let me break down this stopwatch program in a way that's easy to understand!

Let's think of this like building a digital stopwatch step by step:

## STEP 1 - The Parts ("Hardware Setup")
- We're using a small computer called a Raspberry Pi Pico
- It has two buttons connected to it: one for Start/Stop and one for Reset
- There's a small screen (called an OLED display) that shows us the time
- The display connects to the Pico using special pins, like plugging in a cable

## STEP 2 - Setting Up Our Variables ("Global Variables")
- Think of these like creating storage boxes for important information
- We have boxes for:
* Whether the stopwatch is running or stopped
* When we started timing
* How much time has passed in total
* When we last pressed a button (to avoid accidental double-presses)

## STEP 3 - The Start/Stop Button Function
- When you press the Start/Stop button:
* If the stopwatch was stopped → it starts running and remembers the start time
* If it was running → it stops and saves how much time has passed
- It's like pressing the start/stop on a real stopwatch!

## STEP 4 - The Reset Button Function
- When you press Reset:
* It stops the stopwatch
* Sets all the saved time back to zero
* Like clearing your stopwatch to start fresh

## STEP 5 - Making Time Look Nice ("format_time")
- This part takes the raw time (which is in milliseconds) and makes it look readable
- Instead of showing "12500 milliseconds", it shows "00:12.500" (12.5 seconds)
- It's like converting 60 minutes into 1 hour - just more precise!

## STEP 6 - Updating the Display
- This happens many times per second
- If the stopwatch is running:
* It takes the current time and subtracts the start time
* Adds any time saved from previous runs
* Shows this on the screen
- If the stopwatch is stopped:
* It just shows the saved time

## STEP 7 - The Main Program Loop
- This is like the heartbeat of the program
- Every 1/10th of a second:
* It checks if any buttons were pressed
* Updates the time on the display
* Keeps everything running smoothly

Think of it like this: Imagine you're timing a race with a regular stopwatch. You:
1. Press start when the race begins (Start button)
2. Press stop when someone finishes (Stop button)
3. Can pause and restart for multiple racers (Start/Stop button again)
4. Clear it to zero for a new race (Reset button)

This program does all that, but digitally! The main difference is that it's much more precise - it can measure down to milliseconds (thousandths of a second), which would be impossible with a regular handheld stopwatch.

## Sample Code

```python
from mp_button import Button
from machine import Pin, SPI
import ssd1306
from utime import sleep, ticks_ms, ticks_diff

# lower right corner - button closes path to GND
# HARDWARE PIN CONFIGURATION
# LEDs
PICO_ONBOARD_LED_PIN = 25
# Button pins
STOPWATCH_START_STOP_PIN = 14
STOPWATCH_RESET_PIN = 15
# Display pins
DISPLAY_SCL_PIN = 2
DISPLAY_SDA_PIN = 3
DISPLAY_RES_PIN = 4
DISPLAY_DC_PIN = 5
DISPLAY_CS_PIN = 6

# configure the pins
start_stop_pin = Pin(STOPWATCH_START_STOP_PIN, Pin.IN, Pin.PULL_UP)
reset_pin = Pin(STOPWATCH_RESET_PIN, Pin.IN, Pin.PULL_UP)

led = machine.Pin(PICO_ONBOARD_LED_PIN, machine.Pin.OUT)

SCL=Pin(DISPLAY_SCL_PIN)
SDA=Pin(DISPLAY_SDA_PIN)
spi=SPI(0, sck=SCL, mosi=SDA)
RES = Pin(DISPLAY_RES_PIN)
DC = Pin(DISPLAY_DC_PIN)
CS = machine.Pin(DISPLAY_CS_PIN)
oled = ssd1306.SSD1306_SPI(128, 64, spi, DC, RES, CS)

# Global variables
STOPPED = const(0)
RUNNING = const(1)
stopwatch_state = STOPPED
stopwatch_starttime = 0
stopwatch_elapsed_time = 0 # Renamed from stopwatch_resume_time for clarity
last_start_stop_press = 0
last_reset_press = 0
DEBOUNCE_MS = 250

def start_stop_irq(pin):
global last_start_stop_press, stopwatch_state, stopwatch_starttime, stopwatch_elapsed_time
current_time = ticks_ms()
if ticks_diff(current_time, last_start_stop_press) > DEBOUNCE_MS:
last_start_stop_press = current_time

if stopwatch_state == STOPPED:
stopwatch_state = RUNNING
stopwatch_starttime = ticks_ms()
else:
# Calculate the time elapsed since last start
stopwatch_elapsed_time += ticks_diff(ticks_ms(), stopwatch_starttime)
stopwatch_state = STOPPED

def reset_irq(pin):
global last_reset_press, stopwatch_state, stopwatch_elapsed_time
current_time = ticks_ms()
if ticks_diff(current_time, last_reset_press) > DEBOUNCE_MS:
last_reset_press = current_time
stopwatch_state = STOPPED
stopwatch_elapsed_time = 0

# Here are the Interupt handlers
start_stop_pin.irq(trigger=Pin.IRQ_RISING, handler=start_stop_irq)
reset_pin.irq(trigger=Pin.IRQ_FALLING, handler=reset_irq)


def format_time(milliseconds):
"""Convert milliseconds to formatted time string (MM:SS.mmm)"""
seconds = milliseconds // 1000
ms = milliseconds % 1000
minutes = seconds // 60
seconds = seconds % 60
return f"{minutes:02d}:{seconds:02d}.{ms:03d}"

def update_screen(state, elapsed_time):
global stopwatch_starttime
oled.fill(0)
oled.text("stopwatch lab", 0, 0, 1)
state_text = "RUNNING" if state == RUNNING else "STOPPED"
oled.text(state_text, 0, 20, 1)

if state == RUNNING:
current_time = elapsed_time + ticks_diff(ticks_ms(), stopwatch_starttime)
oled.text(format_time(current_time), 0, 40, 1)
else:
oled.text(format_time(elapsed_time), 0, 40, 1)
oled.show()

# Main loop
while True:
update_screen(stopwatch_state, stopwatch_elapsed_time)
sleep(0.1)
```
4 changes: 3 additions & 1 deletion docs/references.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,6 @@ way to try out different designs. This web app allows you to change the format

### Battery Management

[HT4928S - T6845C Data sheet][https://components101.com/sites/default/files/component_datasheet/T6845C-Datasheet.pdf] 8 pin battery management Chip
[HT4928S - T6845C Data sheet][https://components101.com/sites/default/files/component_datasheet/T6845C-Datasheet.pdf] 8 pin battery management Chip

https://www.youtube.com/watch?v=np4NRMKOG6U
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ nav:
- 7-Segment: kits/oled-large/10-draw-seven-segments.md
- Segment Thickness: kits/oled-large/11-draw-thickness.md
- Digital Clock: kits/oled-large/15-digital-clock.md
- Stopwatch: kits/stopwatch/index.md
- Wireless OLED: kits/oled-wireless/index.md
- SH1106 OLED: kits/sh1106/index.md
- ILI9341:
Expand Down
2 changes: 2 additions & 0 deletions src/kits/stopwatch/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
START_BUTTON_PIN = 14
RESET_BUTTON_PIN = 15
38 changes: 38 additions & 0 deletions src/kits/stopwatch/lib/01-one-button-test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from mp_button import Button
from machine import Pin

# lower right corner - button closes path to GND
BUTTON_PIN = 14

# we create two counters to cound presses down and released
counter_pressed = 0
counter_released = 0

# the following function will be invoked
# when the button changes state
# the Button module expects a callback to handle
# - pin number
# - event (Button.PRESSED | Button.RELEASED)
# the event contains a string 'pressed' or 'released'
# which can be used in your code to act upon
def button_change(button, event):
global counter_pressed, counter_released
print(f'button {button} has been {event}')
if event == Button.PRESSED:
counter_pressed += 1
if event == Button.RELEASED:
counter_released += 1
print(f'pressed {counter_pressed} times')
print(f'released {counter_released} times')


# we define a variable which holds a Button
# this Button object will be created using:
# - a pin number (GPIOx)
# - the state at rest (value() is False by default)
# - a callback to invoke when the button changes state (see above)
button_one = Button(14, False, button_change, internal_pullup = True)

# during our loop we keep checking the button(s)
while(True):
button_one.update()
41 changes: 41 additions & 0 deletions src/kits/stopwatch/lib/02-two-button-test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from mp_button import Button
from machine import Pin

# lower right corner - button closes path to GND
STOPWATCH_START_STOP_PIN = 14
STOPWATCH_RESET_PIN = 15

# we create two counters to cound presses down and released
counter_pressed = 0
counter_released = 0

# the following function will be invoked
# when the button changes state
# the Button module expects a callback to handle
# - pin number
# - event (Button.PRESSED | Button.RELEASED)
# the event contains a string 'pressed' or 'released'
# which can be used in your code to act upon
def button_change(button, event):
global counter_pressed, counter_released
print(f'button {button} has been {event}')
if event == Button.PRESSED:
counter_pressed += 1
if event == Button.RELEASED:
counter_released += 1
print(f'pressed {counter_pressed} times')
print(f'released {counter_released} times')


# we define a variable which holds a Button
# this Button object will be created using:
# - a pin number (GPIOx)
# - the state at rest (value() is False by default)
# - a callback to invoke when the button changes state (see above)
button_one = Button(STOPWATCH_START_STOP_PIN, False, button_change, internal_pullup = True)
button_two = Button(STOPWATCH_RESET_PIN, False, button_change, internal_pullup = True)

# during our loop we keep checking the button(s)
while(True):
button_one.update()
button_two.update()
60 changes: 60 additions & 0 deletions src/kits/stopwatch/lib/03-two-button-irq-test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from mp_button import Button
from machine import Pin
from utime import sleep, ticks_ms, ticks_diff

# lower right corner - button closes path to GND
STOPWATCH_START_STOP_PIN = 14
STOPWATCH_RESET_PIN = 15

# configure the pins
start_stop_pin = Pin(STOPWATCH_START_STOP_PIN, Pin.IN, Pin.PULL_UP)
reset_pin = Pin(STOPWATCH_RESET_PIN, Pin.IN, Pin.PULL_UP)

# we create two counters to cound presses down and released
# global variables
STOPPED = const(0)
RUNNING = const(1)
# set the initial state to be stopped
stopwatch_state = STOPPED
stopwatch_starttime = 0
stopwatch_resume_time = 0

# Debounce state
last_start_stop_press = 0
last_reset_press = 0
DEBOUNCE_MS = 250

def start_stop_irq(pin):
global last_start_stop_press, stopwatch_state, stopwatch_starttime, stopwatch_resume_time
current_time = ticks_ms()
if ticks_diff(current_time, last_start_stop_press) > DEBOUNCE_MS:
last_start_stop_press = current_time
# toggle the running state
if stopwatch_state == STOPPED:
stopwatch_state = RUNNING
stopwatch_starttime = ticks_ms()
else:
# collect the time since last
stopwatch_resume_time += (ticks_ms() - stopwatch_starttime) + stopwatch_resume_time
stopwatch_state = STOPPED
# print('start/stop pressed')

def reset_irq(pin):
global last_reset_press, stopwatch_state, stopwatch_milliseconds, stopwatch_resume_time
current_time = ticks_ms()
if ticks_diff(current_time, last_reset_press) > DEBOUNCE_MS:
last_reset_press = current_time
stopwatch_state = STOPPED
stopwatch_resume_time = 0
# print('reset butto pressed')

# Here are the Interupt handlers
start_stop_pin.irq(trigger=Pin.IRQ_RISING, handler=start_stop_irq)
reset_pin.irq(trigger=Pin.IRQ_FALLING, handler=reset_irq)

# during our loop we keep checking the button(s)
counter = 0
while(True):
counter += 1
print("state:", stopwatch_state, stopwatch_resume_time//1000, ":", stopwatch_resume_time%1000)
sleep(.5)
42 changes: 42 additions & 0 deletions src/kits/stopwatch/lib/05-oled-test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from machine import Pin, SPI
import utime
import ssd1306
from mp_button import Button
from utime import sleep, localtime

# HARDWARE PIN CONFIGURATION
# LEDs
PICO_ONBOARD_LED_PIN = 25
# Button pins
STOPWATCH_START_STOP_PIN = 14
STOPWATCH_RESET_PIN = 15
# Display pins
DISPLAY_SCL_PIN = 2
DISPLAY_SDA_PIN = 3
DISPLAY_RES_PIN = 4
DISPLAY_DC_PIN = 5
DISPLAY_CS_PIN = 6

led = machine.Pin(PICO_ONBOARD_LED_PIN, machine.Pin.OUT)

SCL=Pin(DISPLAY_SCL_PIN)
SDA=Pin(DISPLAY_SDA_PIN)
spi=SPI(0, sck=SCL, mosi=SDA)
RES = Pin(DISPLAY_RES_PIN)
DC = Pin(DISPLAY_DC_PIN)
CS = machine.Pin(DISPLAY_CS_PIN)
oled = ssd1306.SSD1306_SPI(128, 64, spi, DC, RES, CS)

def update_screen(seconds):
oled.fill(0)
oled.text("stopwatch lab", 0, 0, 1)
oled.text(str(counter), 0, 10, 1)
oled.show()

counter = 0
while True:
update_screen(counter)
print(counter)
led.toggle()
sleep(.5)
counter += 1
Loading

0 comments on commit 1dc8286

Please sign in to comment.