Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 41 additions & 32 deletions lib/mcp23009e/examples/dpad_piano.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,21 @@
MENU exits the piano mode.
"""

from time import sleep_ms, sleep_us, ticks_diff, ticks_ms
from time import sleep_ms

from machine import I2C, Pin
from mcp23009e import MCP23009E
from mcp23009e.const import *
from pyb import Timer

# Board pins
SPEAKER = Pin("SPEAKER", Pin.OUT)
MENU_BUTTON = Pin("MENU_BUTTON", Pin.IN, Pin.PULL_UP)

# Hardware PWM on SPEAKER pin (PA11 = TIM1_CH4)
buzzer_tim = Timer(1, freq=1000)
buzzer_ch = buzzer_tim.channel(4, Timer.PWM, pin=Pin("SPEAKER"))
buzzer_ch.pulse_width_percent(0)

# I2C and expander setup
i2c = I2C(1)
reset_expander = Pin("RST_EXPANDER", Pin.OUT)
Expand Down Expand Up @@ -50,21 +55,18 @@
}


def tone(pin, freq, duration_ms):
"""Generate a square wave on the buzzer pin."""
if freq == 0:
sleep_ms(duration_ms)
def tone(freq):
"""Start a tone using hardware PWM."""
if freq <= 0:
no_tone()
return
buzzer_tim.freq(freq)
buzzer_ch.pulse_width_percent(50)

period_us = int(1_000_000 / freq)
half_period_us = period_us // 2
end_time = ticks_ms() + duration_ms

while ticks_diff(end_time, ticks_ms()) > 0:
pin.on()
sleep_us(half_period_us)
pin.off()
sleep_us(half_period_us)
def no_tone():
"""Silence the buzzer."""
buzzer_ch.pulse_width_percent(0)


def setup_buttons():
Expand Down Expand Up @@ -121,40 +123,47 @@ def dpad_piano():

last_note = None
last_octave = None
last_freq = 0

try:
while True:
# MENU button is a direct MCU pin, not an MCP23009E pin
if MENU_BUTTON.value() == 0:
print("Menu button pressed, exiting piano.")
wait_menu_release()
break

pressed = get_pressed_buttons()
frequency, note_name, octave = select_frequency(pressed)

if pressed:
frequency, note_name, octave = select_frequency(pressed)

if note_name != last_note or octave != last_octave:
print(
"Playing:",
note_name,
"-",
frequency,
"Hz",
"(" + octave + " octave)",
)
last_note = note_name
last_octave = octave

tone(SPEAKER, frequency, 60)
else:
if frequency == 0:
if last_freq != 0:
no_tone()
last_note = None
last_octave = None
last_freq = 0
sleep_ms(20)
Comment on lines 124 to 144
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

last_freq is initialized to None but later compared to 0, which causes an extra no_tone() call on the first loop iteration even before any note is played. Consider initializing last_freq to 0 (or removing last_freq entirely and calling no_tone() unconditionally when frequency == 0, since it is idempotent).

Copilot uses AI. Check for mistakes.
continue

if note_name != last_note or octave != last_octave:
print(
"Playing:",
note_name,
"-",
frequency,
"Hz",
"(" + octave + " octave)",
)
tone(frequency)
last_note = note_name
last_octave = octave
last_freq = frequency

sleep_ms(20)

except KeyboardInterrupt:
print("\nPiano stopped.")
finally:
no_tone()


dpad_piano()
Loading