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

Video
Schaltplan

Quellcode
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)
262FAQ
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.
