#168Orta

Raspberry Pi Pico LED Halat Çekme Oyunu (NeoPixel Şerit + 2 Buton + Buzzer)

İki kişilik bir buton düellosu! **Raspberry Pi Pico**, 30 LED'lik **WS2812B NeoPixel şeridi** bir halata çevirir: solu kırmızı oyuncunun, sağı mavi oyuncunun. Butonuna her basışta kendi rengin bir LED ilerler; şeridi tamamen kendi rengine boyayan turu, 3 turu ilk alan maçı kazanır. Geri sayımda erken basan geri itilir, **pasif buzzer** her oyuncuya farklı tık sesi çalar ve maç gökkuşağıyla biter. MicroPython ile `neopixel`, debounce ve bloklamayan ses tek projede.

0.0(0)
Raspberry Pi Pico LED Halat Çekme Oyunu (NeoPixel Şerit + 2 Buton + Buzzer)
Arkadaşınla parmak gücünüzü yarıştıracağınız ışıklı bir halat çekme oyunu. 30 LED'lik ışık şeridinin bir yanı kırmızı, öbür yanı mavidir; kim butonuna daha hızlı basarsa ortadaki düğüm onun tarafına kayar ve şeridi tamamen kendi rengine boyayan turu alır. Erken başlayana ceza var, üç tur alan maçı kazanır ve gökkuşağı kutlamasıyla zafer melodisi gelir.

Video

Devre Şeması

Raspberry Pi Pico LED Halat Çekme Oyunu (NeoPixel Şerit + 2 Buton + Buzzer) - Circuit

Kaynak Kodu

MicroPython
1# ─────────────────────────────────────────
2#   BlueGrays · bluegrays.com
3#   Raspberry Pi Pico LED Halat Çekme Oyunu (NeoPixel Şerit + 2 Buton + Buzzer)
4# ─────────────────────────────────────────
5# İki oyunculu buton düellosu. Her basışta kendi rengin şeritte bir adım
6# ilerler; rengini şeridin sonuna ilk ulaştıran turu kazanır, 3 tur alan
7# maçı kazanır. Geri sayımda erken basan 2 adım geri itilir.
8#
9# Pinler:
10#   GP2  -> WS2812B şerit DIN
11#   GP14 -> Oyuncu 1 butonu (KIRMIZI)
12#   GP16 -> Oyuncu 2 butonu (MAVİ)
13#   GP15 -> Buzzer (+)
14
15from machine import Pin, PWM
16from neopixel import NeoPixel
17import time
18import math
19
20# ============ AYARLAR ============
21NUM_LEDS = 30          # Şeritteki LED sayısı
22BRIGHTNESS = 0.6       # 0-1; 30 LED renkli kullanımda ~0,4 A, USB için uygun
23WIN_ROUNDS = 3         # Maçı kazanmak için tur
24FALSE_START_STEPS = 2  # Erken basma cezası
25SOUND_ON = True        # Ses efektleri
26
27RED = (255, 0, 0)      # Oyuncu 1
28BLUE = (0, 60, 255)    # Oyuncu 2
29
30# Düğüm = kırmızı LED sayısı (iki rengin sınırı). İki oyuncu da aynı adım sayısıyla kazanır.
31START = NUM_LEDS // 2                   # Başlangıç sınırı
32GOAL = min(START, NUM_LEDS - START)     # Kazanmak için adım
33
34# ============ DONANIM ============
35strip = NeoPixel(Pin(2), NUM_LEDS)
36btn1 = Pin(14, Pin.IN, Pin.PULL_UP)   # Oyuncu 1
37btn2 = Pin(16, Pin.IN, Pin.PULL_UP)   # Oyuncu 2
38buzzer = PWM(Pin(15))
39buzzer.duty_u16(0)
40
41
42# ============ SES ============
43class Sound:
44    """
45    TR: Bloklamayan nota calici: oyun ses calarken de butonlari okur
46    EN: Non-blocking note player: the game keeps reading buttons while sound plays
47    """
48
49    def __init__(self, pwm):
50        self.pwm = pwm
51        self.queue = []
52        self.until = 0
53
54    def play(self, notes):
55        # notes = [(frekans, ms), ...], frekans 0 = sus
56        if SOUND_ON:
57            self.queue = list(notes)
58            self.until = 0
59
60    def update(self):
61        now = time.ticks_ms()
62        if self.until and time.ticks_diff(now, self.until) < 0:
63            return
64        if not self.queue:
65            if self.until:
66                self.pwm.duty_u16(0)
67                self.until = 0
68            return
69        freq, ms = self.queue.pop(0)
70        if freq:
71            self.pwm.freq(freq)
72            self.pwm.duty_u16(20000)
73        else:
74            self.pwm.duty_u16(0)
75        self.until = time.ticks_add(now, ms)
76
77
78sound = Sound(buzzer)
79
80
81# ============ BUTON ============
82class Button:
83    """
84    TR: 15 ms debounce ile yeni basislari yakalar
85    EN: Catches new presses with a 15 ms debounce
86    """
87
88    def __init__(self, pin):
89        self.pin = pin
90        self.state = 1
91        self.raw = 1
92        self.changed = time.ticks_ms()
93
94    def pressed(self):
95        now = time.ticks_ms()
96        raw = self.pin.value()
97        if raw != self.raw:
98            self.raw = raw
99            self.changed = now
100        if time.ticks_diff(now, self.changed) > 15 and raw != self.state:
101            self.state = raw
102            return raw == 0
103        return False
104
105    def sync(self):
106        # Eski basışları unut (tur arasında basılı kalan buton ceza almasın)
107        self.state = self.raw = self.pin.value()
108        self.changed = time.ticks_ms()
109
110
111b1 = Button(btn1)
112b2 = Button(btn2)
113
114
115# ============ ÇİZİM YARDIMCILARI ============
116def dim(color, k=1.0):
117    # Rengi genel parlaklık ve k ile ölçekle
118    s = BRIGHTNESS * k
119    return (int(color[0] * s), int(color[1] * s), int(color[2] * s))
120
121
122def fill(color, k=1.0):
123    c = dim(color, k)
124    for i in range(NUM_LEDS):
125        strip[i] = c
126    strip.write()
127
128
129def draw_rope(knot):
130    # knot kadar LED kırmızı (solda), gerisi mavi (sağda); sınırdaki 2'şer LED parlak "düğüm"
131    for i in range(NUM_LEDS):
132        if i < knot:
133            k = 1.0 if knot - i <= 2 else 0.45   # Düğüme yakın LED'ler parlak
134            strip[i] = dim(RED, k)
135        else:
136            k = 1.0 if i - knot < 2 else 0.45
137            strip[i] = dim(BLUE, k)
138    strip.write()
139
140
141def wheel(pos):
142    # 0-255 arası değeri gökkuşağı rengine çevir
143    pos %= 256
144    if pos < 85:
145        return (255 - pos * 3, pos * 3, 0)
146    if pos < 170:
147        pos -= 85
148        return (0, 255 - pos * 3, pos * 3)
149    pos -= 170
150    return (pos * 3, 0, 255 - pos * 3)
151
152
153def wait_ms(ms):
154    # Bekle ama sesi çalmaya devam et
155    end = time.ticks_add(time.ticks_ms(), ms)
156    while time.ticks_diff(end, time.ticks_ms()) > 0:
157        sound.update()
158        time.sleep_ms(5)
159
160
161# ============ OYUN AŞAMALARI ============
162def attract():
163    # Bekleme: iki renk nefes alır, herhangi bir butona basınca oyun başlar
164    print("Baslamak icin bir butona bas / Press a button to start")
165    b1.sync()
166    b2.sync()
167    t0 = time.ticks_ms()
168    while True:
169        sound.update()
170        t = time.ticks_diff(time.ticks_ms(), t0)
171        k = 0.25 + 0.75 * (math.sin(t / 400) + 1) / 2
172        for i in range(NUM_LEDS):
173            if i < START:
174                strip[i] = dim(RED, k)
175            else:
176                strip[i] = dim(BLUE, 1.25 - k)   # Ters fazda nefes
177        strip.write()
178        if b1.pressed() or b2.pressed():
179            return
180        time.sleep_ms(15)
181
182
183def countdown():
184    # 3-2-1 geri sayım; erken basan ceza alır. Dönüş: başlangıç düğüm konumu
185    knot = START
186    b1.sync()
187    b2.sync()
188    colors = [(255, 0, 0), (255, 150, 0), (0, 255, 0)]   # Kırmızı, sarı, yeşil
189    for step in range(3):
190        fill(colors[step], 0.6)
191        if step == 2:
192            sound.play([(1200, 200)])   # BAŞLA!
193            break
194        sound.play([(600, 150)])
195        end = time.ticks_add(time.ticks_ms(), 800)
196        while time.ticks_diff(end, time.ticks_ms()) > 0:
197            sound.update()
198            if b1.pressed():
199                knot -= FALSE_START_STEPS
200                print("Oyuncu 1 erken basti! / Player 1 false start!")
201                sound.play([(150, 200)])
202            if b2.pressed():
203                knot += FALSE_START_STEPS
204                print("Oyuncu 2 erken basti! / Player 2 false start!")
205                sound.play([(150, 200)])
206            time.sleep_ms(5)
207    # Ceza düğümü uca kadar itmesin
208    return max(START - GOAL + 2, min(START + GOAL - 2, knot))
209
210
211def play_round(knot):
212    # Buton düellosu; düğüm bir uca ulaşınca kazanan döner (1 veya 2)
213    draw_rope(knot)
214    while True:
215        sound.update()
216        moved = False
217        if b1.pressed():
218            knot += 1
219            sound.play([(440, 25)])
220            moved = True
221        if b2.pressed():
222            knot -= 1
223            sound.play([(660, 25)])
224            moved = True
225        if moved:
226            if knot >= START + GOAL:   # Şerit tamamen kırmızı
227                return 1
228            if knot <= START - GOAL:   # Şerit tamamen mavi
229                return 2
230            draw_rope(knot)
231        time.sleep_ms(1)
232
233
234def celebrate_round(winner, scores):
235    # Kazanan renk 3 kez yanıp söner, sonra skor noktaları gösterilir
236    color = RED if winner == 1 else BLUE
237    print("Turu kazanan / Round winner: Oyuncu / Player", winner, "| Skor / Score:", scores[0], "-", scores[1])
238    sound.play([(523, 120), (659, 120), (784, 120), (1047, 300)])
239    for _ in range(3):
240        fill(color)
241        wait_ms(180)
242        fill((0, 0, 0))
243        wait_ms(120)
244    # Skor: soldan kırmızı noktalar, sağdan mavi noktalar
245    for i in range(NUM_LEDS):
246        strip[i] = (0, 0, 0)
247    for s in range(scores[0]):
248        strip[s * 2] = dim(RED)
249    for s in range(scores[1]):
250        strip[NUM_LEDS - 1 - s * 2] = dim(BLUE)
251    strip.write()
252    wait_ms(1800)
253
254
255def celebrate_match(winner):
256    # Maç sonu: gökkuşağı dalgası ve zafer melodisi
257    print("MACI KAZANAN / MATCH WINNER: Oyuncu / Player", winner)
258    sound.play([(784, 150), (784, 150), (784, 150), (1047, 450), (0, 80), (988, 150), (1047, 500)])
259    t0 = time.ticks_ms()
260    while time.ticks_diff(time.ticks_ms(), t0) < 3500:
261        sound.update()
262        offset = time.ticks_diff(time.ticks_ms(), t0) // 8
263        for i in range(NUM_LEDS):
264            strip[i] = dim(wheel(i * 256 // NUM_LEDS + offset))
265        strip.write()
266        time.sleep_ms(15)
267    fill(RED if winner == 1 else BLUE)
268    wait_ms(1000)
269
270
271# ============ ANA DÖNGÜ ============
272print("== BlueGrays · bluegrays.com ==")
273print("Pico LED Halat Cekme / LED Tug of War")
274
275scores = [0, 0]
276while True:
277    attract()
278    while max(scores) < WIN_ROUNDS:
279        start_knot = countdown()
280        winner = play_round(start_knot)
281        scores[winner - 1] += 1
282        celebrate_round(winner, scores)
283    celebrate_match(1 if scores[0] > scores[1] else 2)
284    scores = [0, 0]
285

Sıkça Sorulanlar

Şeridim 30 LED'lik değil, başka uzunlukta şerit kullanabilir miyim?▾

Evet. Kodun başındaki `NUM_LEDS` değerini şeridindeki LED sayısı yap; başlangıç noktası (`START`) ve kazanmak için gereken adım (`GOAL`) kendiliğinden hesaplanır. 60 LED'lik şeritte her oyuncu 30 basışla kazanır, turlar uzar.

330 Ω direnç ve 470 µF kondansatör şart mı?▾

Wokwi'de gerekmez, gerçek devrede önerilir. GP2 ile şeridin DIN girişi arasına seri 330 Ω direnç, şeridin 5V-GND girişine 470 µF kondansatör tak (uzun bacak 5V'a). İlk LED'i korur, titremeyi ve rastgele renkleri önler.

Şeridi Pico'nun USB'sinden beslemek yeterli mi?▾

30 LED ve `BRIGHTNESS = 0.6` ayarıyla şerit en fazla yaklaşık 0,4 A çeker, USB yeter. VBUS yalnız USB takılıyken 5V verir. Daha uzun şerit ya da daha yüksek parlaklık için ayrı bir 5V/2A adaptör kullan ve GND'leri birleştir.

Tur sayısını ve erken basma cezasını nasıl değiştiririm?▾

`WIN_ROUNDS` maçı kazanmak için gereken tur sayısıdır (varsayılan 3). `FALSE_START_STEPS` erken basanın kaç adım geri itileceğini belirler (varsayılan 2; 0 yaparsan ceza kalkar). Sessiz oynamak için `SOUND_ON = False` yap.

Raspberry Pi Pico LED Halat Çekme Oyunu: NeoPixel ve 2 Buton | BlueGrays