11"""
22Detect 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
914from lis2mdl import LIS2MDL
10- from machine import I2C , Pin
11-
12- SPEAKER = Pin ("SPEAKER" , Pin .OUT )
15+ from machine import I2C , Pin , Timer
1316
1417BASELINE_SAMPLES = 30
1518BASELINE_DELAY_MS = 100
1821MAX_ALERT_DELTA_UT = 60.0
1922BAR_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
4047def make_bar (value , max_value , width = 20 ):
@@ -45,11 +52,11 @@ def make_bar(value, max_value, width=20):
4552
4653
4754def 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
5562i2c = I2C (1 )
0 commit comments