Raspberry Pi Pico Buton Piyano (5 Buton + Buzzer + Melodi Kaydı)
Beş butonlu bir mini piyano, üstelik çaldığını hatırlıyor! **Raspberry Pi Pico**, **beş butonla** DO, RE, MI, FA ve SOL notalarını **pasif buzzer**'dan çalar; tuş basılı kaldıkça nota uzar. **DO'yu 1 saniye basılı tut**, melodini çal ve DO'yu yine basılı tut: Pico melodiyi geri çalar; notalar arasındaki **1,5 saniyeden uzun sessizlikleri 1,5 saniyeye kısaltır**. Kayıt göstergesi kartın üstündeki LED, ek parça yok. PWM ile nota, debounce ve bloklamayan zamanlama tek projede.

Video
Devre Şeması

Kaynak Kodu
1# ─────────────────────────────────────────
2# BlueGrays · bluegrays.com
3# Raspberry Pi Pico Buton Piyano (5 Buton + Buzzer + Melodi Kaydı)
4# ─────────────────────────────────────────
5# Beş butonlu mini piyano: DO, RE, MI, FA, SOL. Tuş basılı kaldıkça nota
6# çalar. DO tuşunu 1 sn basılı tut: kayıt başlar, çalınan notalar
7# süreleriyle saklanır. DO'yu tekrar 1 sn basılı tut: kayıt biter ve
8# melodi aynı ritimle geri çalınır.
9#
10# Pinler:
11# GP7 -> DO butonu (diğer ucu GND)
12# GP8 -> RE butonu (diğer ucu GND)
13# GP9 -> MI butonu (diğer ucu GND)
14# GP10 -> FA butonu (diğer ucu GND)
15# GP11 -> SOL butonu (diğer ucu GND)
16# GP17 -> Buzzer (+), diğer ucu GND
17# LED -> Kart üstü LED (GP25): kayıt / çalma göstergesi
18
19from machine import Pin, PWM
20import time
21
22# ============ AYARLAR ============
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 # PWM doluluk (0-65535), 32768 = %50
27
28REC_KEY = 0 # Kayıt tuşu: DO
29LONG_PRESS_MS = 1000 # Kayıt için basılı tutma süresi
30DEBOUNCE_MS = 20 # Buton titreşim süzgeci
31MAX_NOTES = 64 # Kayıttaki en fazla nota
32MAX_GAP_MS = 1500 # Geri çalmada en uzun sessizlik
33PLAY_DELAY_MS = 500 # Kayıt bitince çalmadan önce bekleme
34REC_BLINK_MS = 500 # Kayıtta LED yanıp sönme
35LOOP_MS = 2 # Ana döngü adımı
36
37FREE, REC, PLAY = 0, 1, 2 # Modlar
38
39# ============ DONANIM ============
40buzzer = PWM(Pin(17))
41buzzer.duty_u16(0)
42try:
43 led = Pin("LED", Pin.OUT) # Pico ve Pico W kart üstü LED
44except (TypeError, ValueError):
45 led = Pin(25, Pin.OUT) # Eski firmware
46led.value(0)
47
48
49class Key:
50 # Titreşim süzgeçli buton; update() basınca +1, bırakınca -1 döndürür
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 # Uzun basış ya da durdurma için kullanıldı
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# ============ SES ============
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 # Kısa sinyal sesi: (Hz, ms) çiftleri; 0 Hz = sessizlik
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# ============ DURUM ============
102mode = FREE
103held = [] # Basılı notalar, son basılan en sonda
104rec_notes = [] # Kayıt: [tuş, başlangıç ms, süre ms]
105rec_t0 = 0
106play_list = [] # Çalma listesi: (zaman ms, tuş, süre ms)
107play_index = 0
108play_start = 0
109play_end = 0
110play_sounding = False
111
112
113def update_sound():
114 # Buzzer tek nota çalar: en son basılan tuş kazanır
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)) # Yükselen sinyal
127 print("Kayit basladi / Recording started (en fazla / max %d nota / notes)" % MAX_NOTES)
128
129
130def build_play_list():
131 # Notaları başlangıca göre sırala, ilk notayı 0'a çek, uzun sessizlikleri kısalt
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)) # Alçalan sinyal
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 # Bloklamayan geri çalma: zamanı gelen notayı başlat, süresi dolanı sustur
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) # LED notayla yanar
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 # Çalarken herhangi bir tuş çalmayı durdurur
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 # DO uzun basış: kayıt başlat ya da bitir. Basılı DO notası kayda girmez.
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# ============ ANA DÖNGÜ ============
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)) # Açılış
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 # Kayıtta LED yanıp söner
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)
260Sıkça Sorulanlar
Melodiyi nasıl kaydedip geri çaldırırım?▾
DO tuşunu 1 saniye basılı tut: yükselen iki bip duyulur ve kayıt başlar. Melodini çal, sonra DO'yu yine 1 saniye basılı tut. Alçalan iki bipten sonra melodi geri çalınır; notalar arasındaki 1,5 saniyeden uzun sessizlikler 1,5 saniyeye kısaltılır. Çalarken herhangi bir tuşa basmak çalmayı durdurur.
Kayıt göstergesi için ayrı bir LED takmam gerekiyor mu?▾
Hayır. Kod, kartın üstündeki LED'i `Pin("LED")` ile kullanır; bu Pico'da GP25'e bağlıdır, Pico W'de de çalışır. LED kayıt sırasında yarım saniye aralıkla yanıp söner, geri çalarken her notayla birlikte yanar.
Notaları ya da oktavı nasıl değiştiririm?▾
Frekanslar `NOTE_HZ` içinde: 523, 587, 659, 698 ve 784 Hz. Bir oktav pes için hepsini ikiye böl, tiz için iki ile çarp. Altıncı tuş eklemek için `NOTE_PINS`, `NOTE_NAMES` ve `NOTE_HZ` listelerine yeni pini, adı ve frekansı (LA için 880) ekle.
Wokwi'de piyanoyu nasıl çalarım?▾
Simülasyonu başlat ve devreye bir kez tıkla. Klavyedeki 1–5 tuşları DO'dan SOL'a kadar notaları çalar, tuşu basılı tuttukça nota uzar. Kayıt için 1'i yaklaşık 1 saniye basılı tut; çalınan notalar ve melodi seri monitörde görünür.
