Skip to content

Commit 4bbbc80

Browse files
Kaan OzenKaan Ozen
authored andcommitted
feat(apds9960): Add pentatonic light theremin example.
1 parent a9dd802 commit 4bbbc80

1 file changed

Lines changed: 81 additions & 0 deletions

File tree

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
"""Light theremin example using APDS9960 and the board buzzer.
2+
3+
Changes the buzzer pitch based on ambient light level.
4+
Uses a pentatonic scale for a musical, "auto-tuned" sound.
5+
Updates hardware only when the note changes to prevent jitter.
6+
"""
7+
8+
from time import sleep_ms
9+
10+
from apds9960 import uAPDS9960 as APDS9960
11+
from machine import I2C, Pin
12+
from pyb import Timer
13+
14+
# Calibration Constants
15+
# You may need to adjust MAX_LIGHT depending on your room's ambient lighting
16+
MIN_LIGHT = 45
17+
MAX_LIGHT = 570
18+
19+
# Musical Frequency Range (Hz) - Auto-Tuned Pentatonic Scale
20+
PENTATONIC_NOTES = [
21+
131, 147, 165, 196, 220, # Octave 3
22+
262, 294, 330, 392, 440, # Octave 4
23+
523, 587, 659, 784, 880 # Octave 5
24+
]
25+
TOTAL_NOTES = len(PENTATONIC_NOTES)
26+
27+
# Hardware Initialization
28+
i2c = I2C(1)
29+
apds = APDS9960(i2c)
30+
apds.enable_light_sensor()
31+
32+
# Hardware PWM on SPEAKER pin
33+
buzzer_tim = Timer(1, freq=1000)
34+
buzzer_ch = buzzer_tim.channel(4, Timer.PWM, pin=Pin("SPEAKER"))
35+
buzzer_ch.pulse_width_percent(0)
36+
37+
print("=======================")
38+
print(" Light Theremin ")
39+
print("=======================")
40+
print("Move your hand over the sensor.")
41+
print("Cover it completely to mute.")
42+
print("Press Ctrl+C to exit.")
43+
44+
# STATE CACHE: Track the last played frequency
45+
last_freq = 0
46+
47+
try:
48+
while True:
49+
light_level = apds.ambient_light()
50+
51+
if light_level < MIN_LIGHT:
52+
# Only update hardware/console if it wasn't already muted
53+
if last_freq != 0:
54+
buzzer_ch.pulse_width_percent(0)
55+
print("Muted")
56+
last_freq = 0
57+
else:
58+
# Clamp the light reading to avoid exceeding the max frequency
59+
clamped_light = min(light_level, MAX_LIGHT)
60+
61+
# Map the light range to an index in our note array (0 to 14)
62+
note_index = (clamped_light - MIN_LIGHT) * (TOTAL_NOTES - 1) // (MAX_LIGHT - MIN_LIGHT)
63+
print("Light Level: {}, Note Index: {}".format(light_level, note_index))
64+
65+
# Fetch the perfect harmonic frequency
66+
freq = PENTATONIC_NOTES[note_index]
67+
68+
# Update the buzzer tone
69+
if freq != last_freq:
70+
buzzer_tim.freq(freq)
71+
buzzer_ch.pulse_width_percent(50)
72+
print("Playing: {} Hz".format(freq))
73+
last_freq = freq
74+
75+
76+
sleep_ms(20)
77+
78+
except KeyboardInterrupt:
79+
print("\nTheremin stopped.")
80+
finally:
81+
buzzer_ch.pulse_width_percent(0)

0 commit comments

Comments
 (0)