Piano à boutons avec Raspberry Pi Pico (5 boutons + buzzer + enregistreur)
Un mini-piano à cinq touches qui se souvient de ce que tu joues ! Le **Raspberry Pi Pico** joue DO, RE, MI, FA et SOL sur un **buzzer passif** avec **cinq boutons**, et la note dure tant que tu gardes la touche enfoncée. **Maintiens DO une seconde**, joue ta mélodie puis maintiens DO à nouveau : le Pico la rejoue et **ramène à 1,5 seconde les silences entre les notes qui dépassent cette durée**. Le témoin d'enregistrement est la LED de la carte, sans pièce en plus. Notes en PWM, anti-rebond et t

Vidéo
Schéma du circuit

Code source
1# ─────────────────────────────────────────
2# BlueGrays · bluegrays.com
3# Piano à boutons avec Raspberry Pi Pico (5 boutons + buzzer + enregistreur)
4# ─────────────────────────────────────────
5# Mini-piano à cinq boutons : DO, RÉ, MI, FA, SOL. Une note joue tant que
6# sa touche est maintenue. Maintiens DO 1 s : l'enregistrement démarre et
7# les notes sont stockées avec leur timing. Maintiens DO 1 s encore :
8# l'enregistrement s'arrête et la mélodie est rejouée en rythme.
9#
10# Broches :
11# GP7 -> Bouton DO (autre patte sur GND)
12# GP8 -> Bouton RÉ (autre patte sur GND)
13# GP9 -> Bouton MI (autre patte sur GND)
14# GP10 -> Bouton FA (autre patte sur GND)
15# GP11 -> Bouton SOL (autre patte sur GND)
16# GP17 -> Buzzer (+), autre patte sur GND
17# LED -> LED intégrée : enregistrement / lecture
18
19from machine import Pin, PWM
20import time
21
22# ============ RÉGLAGES ============
23NOTE_PINS = (7, 8, 9, 10, 11)
24NOTE_NAMES = ("DO", "RE", "MI", "FA", "SOL")
25NOTE_HZ = (523, 587, 659, 698, 784) # C5 D5 E5 F5 G5 (Hz)
26VOLUME = 32768 # Rapport cyclique PWM, 32768 = 50 %
27
28REC_KEY = 0 # Touche d'enregistrement : DO
29LONG_PRESS_MS = 1000 # Durée de maintien pour le record
30DEBOUNCE_MS = 20 # Anti-rebond du bouton
31MAX_NOTES = 64 # Nombre maximal de notes dans un enregistrement
32MAX_GAP_MS = 1500 # Plus long silence à la lecture
33PLAY_DELAY_MS = 500 # Pause avant la lecture
34REC_BLINK_MS = 500 # Clignotement de la LED pendant l'enregistrement
35LOOP_MS = 2 # Pas de la boucle principale
36
37FREE, REC, PLAY = 0, 1, 2 # Modes
38
39# ============ MATÉRIEL ============
40buzzer = PWM(Pin(17))
41buzzer.duty_u16(0)
42try:
43 led = Pin("LED", Pin.OUT) # LED intégrée
44except (TypeError, ValueError):
45 led = Pin(25, Pin.OUT) # Ancien firmware
46led.value(0)
47
48
49class Key:
50 # Bouton avec anti-rebond ; update() renvoie +1 à l'appui, -1 au relâchement
51 def __init__(self, gpio):
52 self.pin = Pin(gpio, Pin.IN, Pin.PULL_UP)
53 self.raw = 1
54 self.stable = 1
55 self.changed = time.ticks_ms()
56 self.down_at = 0
57 self.used = False # Consommé par une commande
58
59 def update(self, now):
60 raw = self.pin.value()
61 if raw != self.raw:
62 self.raw = raw
63 self.changed = now
64 if raw != self.stable and time.ticks_diff(now, self.changed) >= DEBOUNCE_MS:
65 self.stable = raw
66 if raw == 0:
67 self.down_at = now
68 self.used = False
69 return 1
70 return -1
71 return 0
72
73 def held_ms(self, now):
74 return time.ticks_diff(now, self.down_at) if self.stable == 0 else 0
75
76
77keys = [Key(p) for p in NOTE_PINS]
78
79
80# ============ SON ============
81def tone(hz):
82 buzzer.freq(hz)
83 buzzer.duty_u16(VOLUME)
84
85
86def silence():
87 buzzer.duty_u16(0)
88
89
90def chirp(*steps):
91 # Court signal sonore : paires (Hz, ms) ; 0 Hz = silence
92 for hz, ms in steps:
93 if hz:
94 tone(hz)
95 else:
96 silence()
97 time.sleep_ms(ms)
98 silence()
99
100
101# ============ ÉTAT ============
102mode = FREE
103held = [] # Notes maintenues, la plus récente en dernier
104rec_notes = [] # Enregistrement : [touche, début ms, durée ms]
105rec_t0 = 0
106play_list = [] # Lecture : (temps ms, touche, durée ms)
107play_index = 0
108play_start = 0
109play_end = 0
110play_sounding = False
111
112
113def update_sound():
114 # Le buzzer joue une note : la dernière touche appuyée l'emporte
115 if held:
116 tone(NOTE_HZ[held[-1]])
117 else:
118 silence()
119
120
121def start_recording(now):
122 global mode, rec_notes, rec_t0
123 mode = REC
124 rec_notes = []
125 rec_t0 = now
126 chirp((880, 70), (0, 40), (1319, 110)) # Signal montant
127 print("Kayit basladi / Recording started (en fazla / max %d nota / notes)" % MAX_NOTES)
128
129
130def build_play_list():
131 # Trier les notes par début, décaler la première à 0, raccourcir les longs silences
132 notes = sorted(rec_notes, key=lambda n: n[1])
133 result = []
134 t = 0
135 for i, (k, start, length) in enumerate(notes):
136 if i > 0:
137 prev_k, prev_start, prev_len = notes[i - 1]
138 t += min(start - prev_start, prev_len + MAX_GAP_MS)
139 result.append((t, k, length))
140 return result
141
142
143def stop_recording(now):
144 global mode, play_list, play_index, play_start, play_end, play_sounding
145 led.value(0)
146 chirp((1319, 70), (0, 40), (880, 110)) # Signal descendant
147 if not rec_notes:
148 mode = FREE
149 print("Kayit bos / Recording is empty")
150 chirp((220, 300))
151 return
152 play_list = build_play_list()
153 names = " ".join(NOTE_NAMES[n[1]] for n in play_list)
154 print("Kayit bitti / Recording stopped: %d nota / notes" % len(play_list))
155 print("Melodi / Melody:", names)
156 mode = PLAY
157 play_index = 0
158 play_end = 0
159 play_sounding = False
160 play_start = time.ticks_add(time.ticks_ms(), PLAY_DELAY_MS)
161 print("Geri calma / Playback...")
162
163
164def finish_playback(message):
165 global mode
166 mode = FREE
167 silence()
168 led.value(0)
169 print(message)
170
171
172def update_playback(now):
173 # Lecture non bloquante : démarrer les notes à l'heure, les couper quand elles finissent
174 global play_index, play_end, play_sounding
175 elapsed = time.ticks_diff(now, play_start)
176 if elapsed < 0:
177 return
178 while play_index < len(play_list) and elapsed >= play_list[play_index][0]:
179 t, k, length = play_list[play_index]
180 tone(NOTE_HZ[k])
181 led.value(1) # La LED s'allume avec la note
182 play_end = t + length
183 play_sounding = True
184 play_index += 1
185 if play_sounding and elapsed >= play_end:
186 silence()
187 led.value(0)
188 play_sounding = False
189 if not play_sounding and play_index >= len(play_list):
190 finish_playback("Bitti / Done. DO 1 sn = yeni kayit / DO 1 s = new recording")
191
192
193def on_press(k, now):
194 if mode == PLAY:
195 # N'importe quelle touche arrête la lecture
196 keys[k].used = True
197 finish_playback("Durduruldu / Stopped")
198 return
199 held.append(k)
200 update_sound()
201 tag = "[REC] " if mode == REC else ""
202 print("%sNota / Note: %s (%d Hz)" % (tag, NOTE_NAMES[k], NOTE_HZ[k]))
203
204
205def on_release(k, now):
206 if k in held:
207 held.remove(k)
208 update_sound()
209 if keys[k].used or mode != REC:
210 return
211 start = time.ticks_diff(keys[k].down_at, rec_t0)
212 length = time.ticks_diff(now, keys[k].down_at)
213 rec_notes.append([k, start, length])
214 if len(rec_notes) >= MAX_NOTES:
215 print("Kayit dolu / Recording full")
216 stop_recording(now)
217
218
219def on_long_press(k, now):
220 # Appui long sur DO : démarrer ou arrêter l'enregistrement. La note DO maintenue n'est pas
221 # enregistrée.
222 keys[k].used = True
223 if k in held:
224 held.remove(k)
225 update_sound()
226 if mode == FREE:
227 start_recording(now)
228 elif mode == REC:
229 stop_recording(now)
230
231
232# ============ BOUCLE PRINCIPALE ============
233print("== BlueGrays · bluegrays.com ==")
234print("Pico Buton Piyano / Button Piano")
235print("Tuslar / Keys: DO RE MI FA SOL")
236print("DO 1 sn basili = kayit baslat / bitir + geri cal")
237print("Hold DO 1 s = start / stop recording + play back")
238chirp((NOTE_HZ[0], 90), (NOTE_HZ[2], 90), (NOTE_HZ[4], 90), (1047, 180)) # Démarrage
239
240while True:
241 now = time.ticks_ms()
242 for i, key in enumerate(keys):
243 event = key.update(now)
244 if event == 1:
245 on_press(i, now)
246 elif event == -1:
247 on_release(i, now)
248
249 rec_key = keys[REC_KEY]
250 if (mode != PLAY and not rec_key.used
251 and rec_key.held_ms(now) >= LONG_PRESS_MS):
252 on_long_press(REC_KEY, now)
253
254 if mode == PLAY:
255 update_playback(now)
256 elif mode == REC:
257 # La LED clignote pendant l'enregistrement
258 led.value(1 if (time.ticks_diff(now, rec_t0) // REC_BLINK_MS) % 2 == 0 else 0)
259
260 time.sleep_ms(LOOP_MS)
261FAQ
Comment enregistrer une mélodie et la rejouer ?▾
Maintiens DO une seconde : deux bips montants indiquent que l'enregistrement commence. Joue ta mélodie, puis maintiens DO une seconde. Après deux bips descendants, la mélodie est rejouée ; les silences entre les notes qui dépassent 1,5 seconde sont ramenés à 1,5 seconde. Appuyer sur une touche pendant la lecture l'arrête.
Faut-il ajouter une LED pour le témoin d'enregistrement ?▾
Non. Le code utilise la LED de la carte via `Pin("LED")`, c'est-à-dire GP25 sur le Pico, et cela fonctionne aussi sur le Pico W. Elle clignote toutes les demi-secondes pendant l'enregistrement et s'allume avec chaque note pendant la lecture.
Comment changer les notes ou l'octave ?▾
Les fréquences sont dans `NOTE_HZ` : 523, 587, 659, 698 et 784 Hz. Divise-les toutes par deux pour descendre d'une octave, multiplie-les par deux pour monter. Pour une sixième touche, ajoute la broche, le nom et la fréquence (880 pour LA) dans `NOTE_PINS`, `NOTE_NAMES` et `NOTE_HZ`.
Comment jouer du piano sur Wokwi ?▾
Lance la simulation et clique une fois sur le circuit. Les touches 1 à 5 du clavier jouent de DO à SOL, et plus tu maintiens une touche, plus la note dure. Pour enregistrer, maintiens 1 environ une seconde ; les notes et la mélodie enregistrée s'affichent dans le moniteur série.
