#171Débutant

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

0.0(0)
Piano à boutons avec Raspberry Pi Pico (5 boutons + buzzer + enregistreur)
Un petit piano à cinq boutons : chacun joue do, ré, mi, fa ou sol sur le buzzer, et la note dure tant que tu gardes le doigt dessus. Garde la touche do enfoncée une seconde et l'enregistrement commence, la LED de la carte clignote ; joue ta mélodie, garde do enfoncé une seconde de plus, et la Raspberry Pi Pico te la rejoue avec le même rythme. Le moyen le plus simple d'enregistrer ta propre mélodie et de la réécouter.

Vidéo

Schéma du circuit

Piano à boutons avec Raspberry Pi Pico (5 boutons + buzzer + enregistreur) - Circuit

Code source

MicroPython
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)
261

FAQ

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.

Piano à boutons avec Raspberry Pi Pico et enregistreur | BlueGrays