#171Anfänger

Raspberry Pi Pico Tasten-Klavier (5 Taster + Summer + Melodie-Rekorder)

Ein Mini-Klavier mit fünf Tasten, das sich merkt, was du spielst! Der **Raspberry Pi Pico** spielt mit **fünf Tastern** DO, RE, MI, FA und SOL auf einem **passiven Summer**; der Ton klingt, solange du die Taste hältst. **Halte DO eine Sekunde**, spiel deine Melodie und halte DO erneut: Der Pico spielt sie ab und **verkürzt Pausen zwischen den Tönen, die länger als 1,5 Sekunden sind, auf 1,5 Sekunden**. Als Aufnahmeanzeige dient die LED auf der Platine. Töne per PWM, Entprellung und nicht blockie

0.0(0)
Raspberry Pi Pico Tasten-Klavier (5 Taster + Summer + Melodie-Rekorder)
Ein kleines Klavier mit fünf Knöpfen: Jeder spielt Do, Re, Mi, Fa oder Sol auf dem Summer, und der Ton klingt, solange du den Knopf hältst. Hältst du die Do-Taste eine Sekunde lang, startet die Aufnahme und die LED auf der Platine blinkt; spiel deine Melodie, halte Do noch einmal eine Sekunde, und der Raspberry Pi Pico spielt sie dir im selben Rhythmus vor. Der einfachste Weg, eine eigene Melodie aufzunehmen und anzuhören.

Video

Schaltplan

Raspberry Pi Pico Tasten-Klavier (5 Taster + Summer + Melodie-Rekorder) - Circuit

Quellcode

MicroPython
1# ─────────────────────────────────────────
2#   BlueGrays · bluegrays.com
3#   Raspberry Pi Pico Tasten-Klavier (5 Taster + Summer + Melodie-Rekorder)
4# ─────────────────────────────────────────
5# Ein Mini-Klavier mit fünf Tasten: DO, RE, MI, FA, SOL. Eine Note
6# erklingt, solange ihre Taste gehalten wird. Halte DO 1 s lang: Die
7# Aufnahme startet und die Noten werden mit ihrem Timing gespeichert.
8# Halte DO noch einmal 1 s lang: Die Aufnahme stoppt und die Melodie wird
9# im Rhythmus abgespielt.
10#
11# Pins:
12#   GP7  -> DO-Taste (anderes Beinchen an GND)
13#   GP8  -> RE-Taste (anderes Beinchen an GND)
14#   GP9  -> MI-Taste (anderes Beinchen an GND)
15#   GP10 -> FA-Taste (anderes Beinchen an GND)
16#   GP11 -> SOL-Taste (anderes Beinchen an GND)
17#   GP17 -> Buzzer (+), anderes Beinchen an GND
18#   LED  -> Onboard-LED: Aufnahme / Wiedergabe
19
20from machine import Pin, PWM
21import time
22
23# ============ EINSTELLUNGEN ============
24NOTE_PINS = (7, 8, 9, 10, 11)
25NOTE_NAMES = ("DO", "RE", "MI", "FA", "SOL")
26NOTE_HZ = (523, 587, 659, 698, 784)   # C5 D5 E5 F5 G5 (Hz)
27VOLUME = 32768         # PWM-Tastverhältnis, 32768 = 50%
28
29REC_KEY = 0            # Aufnahmetaste: DO
30LONG_PRESS_MS = 1000   # Haltezeit für Rekord
31DEBOUNCE_MS = 20       # Tastenentprellung
32MAX_NOTES = 64         # Maximale Notenzahl in einer Aufnahme
33MAX_GAP_MS = 1500      # Längste Stille bei der Wiedergabe
34PLAY_DELAY_MS = 500    # Pause vor der Wiedergabe
35REC_BLINK_MS = 500     # LED blinkt während der Aufnahme
36LOOP_MS = 2            # Schritt der Hauptschleife
37
38FREE, REC, PLAY = 0, 1, 2   # Modi
39
40# ============ HARDWARE ============
41buzzer = PWM(Pin(17))
42buzzer.duty_u16(0)
43try:
44    led = Pin("LED", Pin.OUT)      # Onboard-LED
45except (TypeError, ValueError):
46    led = Pin(25, Pin.OUT)         # Ältere Firmware
47led.value(0)
48
49
50class Key:
51    # Entprellte Taste; update() gibt beim Drücken +1 und beim Loslassen -1 zurück
52    def __init__(self, gpio):
53        self.pin = Pin(gpio, Pin.IN, Pin.PULL_UP)
54        self.raw = 1
55        self.stable = 1
56        self.changed = time.ticks_ms()
57        self.down_at = 0
58        self.used = False      # Von einem Befehl verbraucht
59
60    def update(self, now):
61        raw = self.pin.value()
62        if raw != self.raw:
63            self.raw = raw
64            self.changed = now
65        if raw != self.stable and time.ticks_diff(now, self.changed) >= DEBOUNCE_MS:
66            self.stable = raw
67            if raw == 0:
68                self.down_at = now
69                self.used = False
70                return 1
71            return -1
72        return 0
73
74    def held_ms(self, now):
75        return time.ticks_diff(now, self.down_at) if self.stable == 0 else 0
76
77
78keys = [Key(p) for p in NOTE_PINS]
79
80
81# ============ SOUND ============
82def tone(hz):
83    buzzer.freq(hz)
84    buzzer.duty_u16(VOLUME)
85
86
87def silence():
88    buzzer.duty_u16(0)
89
90
91def chirp(*steps):
92    # Kurzer Signalton: (Hz, ms)-Paare; 0 Hz = Stille
93    for hz, ms in steps:
94        if hz:
95            tone(hz)
96        else:
97            silence()
98        time.sleep_ms(ms)
99    silence()
100
101
102# ============ ZUSTAND ============
103mode = FREE
104held = []          # Gehaltene Noten, die neueste zuletzt
105rec_notes = []     # Aufnahme: [Taste, Start ms, Länge ms]
106rec_t0 = 0
107play_list = []     # Wiedergabe: (Zeit ms, Taste, Länge ms)
108play_index = 0
109play_start = 0
110play_end = 0
111play_sounding = False
112
113
114def update_sound():
115    # Der Buzzer spielt eine Note: Die zuletzt gedrückte Taste gewinnt
116    if held:
117        tone(NOTE_HZ[held[-1]])
118    else:
119        silence()
120
121
122def start_recording(now):
123    global mode, rec_notes, rec_t0
124    mode = REC
125    rec_notes = []
126    rec_t0 = now
127    chirp((880, 70), (0, 40), (1319, 110))       # Steigendes Signal
128    print("Kayit basladi / Recording started (en fazla / max %d nota / notes)" % MAX_NOTES)
129
130
131def build_play_list():
132    # Noten nach Start sortieren, die erste auf 0 verschieben, lange Pausen kürzen
133    notes = sorted(rec_notes, key=lambda n: n[1])
134    result = []
135    t = 0
136    for i, (k, start, length) in enumerate(notes):
137        if i > 0:
138            prev_k, prev_start, prev_len = notes[i - 1]
139            t += min(start - prev_start, prev_len + MAX_GAP_MS)
140        result.append((t, k, length))
141    return result
142
143
144def stop_recording(now):
145    global mode, play_list, play_index, play_start, play_end, play_sounding
146    led.value(0)
147    chirp((1319, 70), (0, 40), (880, 110))       # Fallendes Signal
148    if not rec_notes:
149        mode = FREE
150        print("Kayit bos / Recording is empty")
151        chirp((220, 300))
152        return
153    play_list = build_play_list()
154    names = " ".join(NOTE_NAMES[n[1]] for n in play_list)
155    print("Kayit bitti / Recording stopped: %d nota / notes" % len(play_list))
156    print("Melodi / Melody:", names)
157    mode = PLAY
158    play_index = 0
159    play_end = 0
160    play_sounding = False
161    play_start = time.ticks_add(time.ticks_ms(), PLAY_DELAY_MS)
162    print("Geri calma / Playback...")
163
164
165def finish_playback(message):
166    global mode
167    mode = FREE
168    silence()
169    led.value(0)
170    print(message)
171
172
173def update_playback(now):
174    # Nicht blockierende Wiedergabe: Noten bei Fälligkeit starten, am Ende stummschalten
175    global play_index, play_end, play_sounding
176    elapsed = time.ticks_diff(now, play_start)
177    if elapsed < 0:
178        return
179    while play_index < len(play_list) and elapsed >= play_list[play_index][0]:
180        t, k, length = play_list[play_index]
181        tone(NOTE_HZ[k])
182        led.value(1)                  # LED leuchtet mit der Note
183        play_end = t + length
184        play_sounding = True
185        play_index += 1
186    if play_sounding and elapsed >= play_end:
187        silence()
188        led.value(0)
189        play_sounding = False
190    if not play_sounding and play_index >= len(play_list):
191        finish_playback("Bitti / Done. DO 1 sn = yeni kayit / DO 1 s = new recording")
192
193
194def on_press(k, now):
195    if mode == PLAY:
196        # Jede Taste stoppt die Wiedergabe
197        keys[k].used = True
198        finish_playback("Durduruldu / Stopped")
199        return
200    held.append(k)
201    update_sound()
202    tag = "[REC] " if mode == REC else ""
203    print("%sNota / Note: %s (%d Hz)" % (tag, NOTE_NAMES[k], NOTE_HZ[k]))
204
205
206def on_release(k, now):
207    if k in held:
208        held.remove(k)
209        update_sound()
210    if keys[k].used or mode != REC:
211        return
212    start = time.ticks_diff(keys[k].down_at, rec_t0)
213    length = time.ticks_diff(now, keys[k].down_at)
214    rec_notes.append([k, start, length])
215    if len(rec_notes) >= MAX_NOTES:
216        print("Kayit dolu / Recording full")
217        stop_recording(now)
218
219
220def on_long_press(k, now):
221    # Langer Druck auf DO: Aufnahme starten oder stoppen. Die gehaltene DO-Note wird nicht
222    # aufgenommen.
223    keys[k].used = True
224    if k in held:
225        held.remove(k)
226    update_sound()
227    if mode == FREE:
228        start_recording(now)
229    elif mode == REC:
230        stop_recording(now)
231
232
233# ============ HAUPTSCHLEIFE ============
234print("== BlueGrays · bluegrays.com ==")
235print("Pico Buton Piyano / Button Piano")
236print("Tuslar / Keys: DO RE MI FA SOL")
237print("DO 1 sn basili = kayit baslat / bitir + geri cal")
238print("Hold DO 1 s = start / stop recording + play back")
239chirp((NOTE_HZ[0], 90), (NOTE_HZ[2], 90), (NOTE_HZ[4], 90), (1047, 180))   # Start
240
241while True:
242    now = time.ticks_ms()
243    for i, key in enumerate(keys):
244        event = key.update(now)
245        if event == 1:
246            on_press(i, now)
247        elif event == -1:
248            on_release(i, now)
249
250    rec_key = keys[REC_KEY]
251    if (mode != PLAY and not rec_key.used
252            and rec_key.held_ms(now) >= LONG_PRESS_MS):
253        on_long_press(REC_KEY, now)
254
255    if mode == PLAY:
256        update_playback(now)
257    elif mode == REC:
258        # LED blinkt während der Aufnahme
259        led.value(1 if (time.ticks_diff(now, rec_t0) // REC_BLINK_MS) % 2 == 0 else 0)
260
261    time.sleep_ms(LOOP_MS)
262

FAQ

Wie nehme ich eine Melodie auf und spiele sie ab?▾

Halte DO eine Sekunde: Zwei steigende Pieptöne zeigen, dass die Aufnahme läuft. Spiel deine Melodie und halte DO erneut eine Sekunde. Nach zwei fallenden Pieptönen erklingt die Melodie; Pausen zwischen den Tönen, die länger als 1,5 Sekunden sind, werden auf 1,5 Sekunden verkürzt. Ein Druck auf eine beliebige Taste stoppt die Wiedergabe.

Brauche ich eine eigene LED für die Aufnahmeanzeige?▾

Nein. Der Code steuert die LED auf der Platine über `Pin("LED")` an; beim Pico ist das GP25, auf dem Pico W funktioniert es ebenfalls. Sie blinkt während der Aufnahme im Halbsekundentakt und leuchtet bei der Wiedergabe mit jedem Ton auf.

Wie ändere ich die Töne oder die Oktave?▾

Die Frequenzen stehen in `NOTE_HZ`: 523, 587, 659, 698 und 784 Hz. Halbiere alle für eine Oktave tiefer oder verdopple sie für eine höher. Für eine sechste Taste ergänzt du `NOTE_PINS`, `NOTE_NAMES` und `NOTE_HZ` um Pin, Namen und Frequenz (880 für LA).

Wie spiele ich das Klavier in Wokwi?▾

Starte die Simulation und klicke einmal auf die Schaltung. Die Tasten 1 bis 5 deiner Tastatur spielen DO bis SOL; je länger du drückst, desto länger der Ton. Zum Aufnehmen hältst du die 1 etwa eine Sekunde. Töne und Melodie erscheinen im seriellen Monitor.

Raspberry Pi Pico Tasten-Klavier mit Melodie-Rekorder | BlueGrays