Skip to content

Commit ca5c2e3

Browse files
committed
fix(lis2mdl): Use hardware PWM for metal detector buzzer tone.
1 parent ce081c5 commit ca5c2e3

1 file changed

Lines changed: 27 additions & 20 deletions

File tree

lib/lis2mdl/examples/metal_detector.py

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
"""
22
Detect nearby metal objects by monitoring field magnitude changes.
3-
Measure a baseline, then loop and print an alert (with intensity bar) when magnitude deviates significantly.
4-
Louder buzzer beep for stronger fields — like a real metal detector.
3+
Measure a baseline, then loop and print an alert (with intensity bar)
4+
when magnitude deviates significantly. Higher-pitched buzzer beep for
5+
stronger fields — like a real metal detector.
6+
7+
The buzzer uses hardware PWM (Timer 1 channel 4 on PA11/SPEAKER) so the
8+
tone is generated in the background while the CPU continues reading the
9+
magnetometer.
510
"""
611

7-
from time import sleep_ms, ticks_ms
12+
from time import sleep_ms
813

914
from lis2mdl import LIS2MDL
10-
from machine import I2C, Pin
11-
12-
SPEAKER = Pin("SPEAKER", Pin.OUT)
15+
from machine import I2C, Pin, Timer
1316

1417
BASELINE_SAMPLES = 30
1518
BASELINE_DELAY_MS = 100
@@ -18,23 +21,27 @@
1821
MAX_ALERT_DELTA_UT = 60.0
1922
BAR_WIDTH = 20
2023

24+
# Hardware PWM on SPEAKER pin (PA11 = TIM1_CH4)
25+
buzzer_tim = Timer(1, freq=1000)
26+
buzzer_ch = buzzer_tim.channel(4, Timer.PWM, pin=Pin("SPEAKER"))
27+
buzzer_ch.pulse_width_percent(0)
28+
2129

22-
def tone(pin, freq, duration_ms):
23-
"""Generate a square wave on the buzzer pin using millisecond timing only."""
30+
def tone(freq, duration_ms):
31+
"""Play a tone at the given frequency for duration_ms using hardware PWM."""
2432
if freq <= 0:
33+
buzzer_ch.pulse_width_percent(0)
2534
sleep_ms(duration_ms)
2635
return
36+
buzzer_tim.freq(freq)
37+
buzzer_ch.pulse_width_percent(50)
38+
sleep_ms(duration_ms)
39+
buzzer_ch.pulse_width_percent(0)
2740

28-
# With sleep_ms(), half-period cannot be smaller than 1 ms.
29-
# That limits the practical max frequency to about 500 Hz.
30-
half_period_ms = max(1, int(500 / freq))
31-
end_time = ticks_ms() + duration_ms
3241

33-
while ticks_ms() < end_time:
34-
pin.on()
35-
sleep_ms(half_period_ms)
36-
pin.off()
37-
sleep_ms(half_period_ms)
42+
def no_tone():
43+
"""Silence the buzzer."""
44+
buzzer_ch.pulse_width_percent(0)
3845

3946

4047
def make_bar(value, max_value, width=20):
@@ -45,11 +52,11 @@ def make_bar(value, max_value, width=20):
4552

4653

4754
def alert_tone(delta_ut):
48-
"""Play a stronger tone when the magnetic disturbance is larger."""
55+
"""Play a higher-pitched tone when the magnetic disturbance is larger."""
4956
normalized = min(delta_ut, MAX_ALERT_DELTA_UT) / MAX_ALERT_DELTA_UT
50-
freq = int(150 + normalized * 350) # 150 -> 500 Hz
57+
freq = int(800 + normalized * 2200) # 800 -> 3000 Hz
5158
duration_ms = int(40 + normalized * 80)
52-
tone(SPEAKER, freq, duration_ms)
59+
tone(freq, duration_ms)
5360

5461

5562
i2c = I2C(1)

0 commit comments

Comments
 (0)