#171Principiante

Piano de botones con Raspberry Pi Pico (5 botones + zumbador + grabadora)

¡Un minipiano de cinco teclas que recuerda lo que tocas! La **Raspberry Pi Pico** toca DO, RE, MI, FA y SOL en un **zumbador pasivo** con **cinco botones**, y la nota dura lo que mantengas la tecla. **Mantén DO un segundo**, toca tu melodía y vuelve a mantener DO: la Pico la reproduce y **acorta a 1,5 segundos los silencios entre notas que superan esa duración**. El indicador de grabación es el LED de la propia placa, sin piezas extra. Notas con PWM, antirrebote y temporización sin bloqueo en un

0.0(0)
Piano de botones con Raspberry Pi Pico (5 botones + zumbador + grabadora)
Un pequeño piano de cinco botones: cada uno toca do, re, mi, fa o sol en el zumbador, y la nota dura mientras lo mantienes pulsado. Si mantienes la tecla do un segundo empieza la grabación y el LED de la placa parpadea; toca tu melodía, vuelve a mantener do un segundo y la Raspberry Pi Pico te la devuelve con el mismo ritmo. La forma más fácil de grabar tu propia melodía y escucharla.

Video

Diagrama del circuito

Piano de botones con Raspberry Pi Pico (5 botones + zumbador + grabadora) - Circuit

Código fuente

MicroPython
1# ─────────────────────────────────────────
2#   BlueGrays · bluegrays.com
3#   Piano de botones con Raspberry Pi Pico (5 botones + zumbador + grabadora)
4# ─────────────────────────────────────────
5# Mini piano de cinco botones: DO, RE, MI, FA, SOL. Una nota suena
6# mientras se mantiene su tecla. Mantén DO 1 s: empieza la grabación y las
7# notas se guardan con su tiempo. Mantén DO 1 s otra vez: la grabación se
8# detiene y la melodía se reproduce con su ritmo.
9#
10# Pines:
11#   GP7  -> Botón DO (otra patilla a GND)
12#   GP8  -> Botón RE (otra patilla a GND)
13#   GP9  -> Botón MI (otra patilla a GND)
14#   GP10 -> Botón FA (otra patilla a GND)
15#   GP11 -> Botón SOL (otra patilla a GND)
16#   GP17 -> Zumbador (+), otra patilla a GND
17#   LED  -> LED integrado: grabar / reproducir
18
19from machine import Pin, PWM
20import time
21
22# ============ AJUSTES ============
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         # Ciclo de trabajo PWM, 32768 = 50%
27
28REC_KEY = 0            # Tecla de grabación: DO
29LONG_PRESS_MS = 1000   # Tiempo de mantenimiento del récord
30DEBOUNCE_MS = 20       # Antirrebote del botón
31MAX_NOTES = 64         # Máximo de notas en una grabación
32MAX_GAP_MS = 1500      # Silencio más largo en la reproducción
33PLAY_DELAY_MS = 500    # Pausa antes de la reproducción
34REC_BLINK_MS = 500     # Parpadeo del LED durante la grabación
35LOOP_MS = 2            # Paso del bucle principal
36
37FREE, REC, PLAY = 0, 1, 2   # Modos
38
39# ============ HARDWARE ============
40buzzer = PWM(Pin(17))
41buzzer.duty_u16(0)
42try:
43    led = Pin("LED", Pin.OUT)      # LED integrado
44except (TypeError, ValueError):
45    led = Pin(25, Pin.OUT)         # Firmware antiguo
46led.value(0)
47
48
49class Key:
50    # Botón con antirrebote; update() devuelve +1 al pulsar, -1 al soltar
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      # Consumido por un comando
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# ============ SONIDO ============
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    # Sonido de señal corto: pares (Hz, ms); 0 Hz = silencio
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# ============ ESTADO ============
102mode = FREE
103held = []          # Notas mantenidas, la más reciente al final
104rec_notes = []     # Grabación: [tecla, inicio ms, duración ms]
105rec_t0 = 0
106play_list = []     # Reproducción: (tiempo ms, tecla, duración ms)
107play_index = 0
108play_start = 0
109play_end = 0
110play_sounding = False
111
112
113def update_sound():
114    # El zumbador toca una nota: gana la última tecla pulsada
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))       # Señal ascendente
127    print("Kayit basladi / Recording started (en fazla / max %d nota / notes)" % MAX_NOTES)
128
129
130def build_play_list():
131    # Ordenar las notas por inicio, desplazar la primera a 0 y acortar los silencios largos
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))       # Señal descendente
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    # Reproducción sin bloqueo: iniciar las notas cuando toca y silenciarlas cuando terminan
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)                  # El LED se enciende con la nota
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        # Cualquier tecla detiene la reproducción
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    # Pulsación larga en DO: iniciar o detener la grabación. La nota DO mantenida no se graba.
221    keys[k].used = True
222    if k in held:
223        held.remove(k)
224    update_sound()
225    if mode == FREE:
226        start_recording(now)
227    elif mode == REC:
228        stop_recording(now)
229
230
231# ============ BUCLE PRINCIPAL ============
232print("== BlueGrays · bluegrays.com ==")
233print("Pico Buton Piyano / Button Piano")
234print("Tuslar / Keys: DO RE MI FA SOL")
235print("DO 1 sn basili = kayit baslat / bitir + geri cal")
236print("Hold DO 1 s = start / stop recording + play back")
237chirp((NOTE_HZ[0], 90), (NOTE_HZ[2], 90), (NOTE_HZ[4], 90), (1047, 180))   # Arranque
238
239while True:
240    now = time.ticks_ms()
241    for i, key in enumerate(keys):
242        event = key.update(now)
243        if event == 1:
244            on_press(i, now)
245        elif event == -1:
246            on_release(i, now)
247
248    rec_key = keys[REC_KEY]
249    if (mode != PLAY and not rec_key.used
250            and rec_key.held_ms(now) >= LONG_PRESS_MS):
251        on_long_press(REC_KEY, now)
252
253    if mode == PLAY:
254        update_playback(now)
255    elif mode == REC:
256        # El LED parpadea durante la grabación
257        led.value(1 if (time.ticks_diff(now, rec_t0) // REC_BLINK_MS) % 2 == 0 else 0)
258
259    time.sleep_ms(LOOP_MS)
260

Preguntas frecuentes

¿Cómo grabo una melodía y la reproduzco?▾

Mantén DO un segundo: dos pitidos ascendentes indican que empieza la grabación. Toca tu melodía y vuelve a mantener DO un segundo. Tras dos pitidos descendentes, la melodía se reproduce; los silencios entre notas de más de 1,5 segundos se acortan a 1,5 segundos. Pulsar cualquier tecla durante la reproducción la detiene.

¿Necesito un LED aparte para el indicador de grabación?▾

No. El código usa el LED de la placa mediante `Pin("LED")`, que en la Pico es GP25 y también funciona en la Pico W. Parpadea cada medio segundo durante la grabación y se enciende con cada nota durante la reproducción.

¿Cómo cambio las notas o la octava?▾

Las frecuencias están en `NOTE_HZ`: 523, 587, 659, 698 y 784 Hz. Divídelas todas entre dos para bajar una octava o multiplícalas por dos para subirla. Para una sexta tecla, añade el pin, el nombre y la frecuencia (880 para LA) a `NOTE_PINS`, `NOTE_NAMES` y `NOTE_HZ`.

¿Cómo toco el piano en Wokwi?▾

Inicia la simulación y haz clic una vez en el circuito. Las teclas 1 a 5 del teclado tocan de DO a SOL, y cuanto más mantienes la tecla, más dura la nota. Para grabar, mantén el 1 un segundo; las notas y la melodía grabada aparecen en el monitor serie.

Piano de botones con Raspberry Pi Pico y grabadora de melodías | BlueGrays