【エモい】名前わかんないけど"10秒ピッタリに止めるあのゲーム"がやりたい⏱

## "10秒ピッタリに止めるあのゲーム"とは？
言わずもがな、皆さんは小学生の頃ストップウォッチを使って **"10秒ピッタリに止めるあのゲーム"** をやったことありますよね？？

<img width="70%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3597730/b9bc12a5-a2d9-f000-f52b-c1e787da9716.jpeg">

念のため説明しておくと、**ストップウォッチのスタートボタンを押してから、画面を見ずに10秒ピッタリにストップできた人が勝ちのゲーム**です。

誰が考えて誰が広めたのか分かりませんが、おそらく全国、全世代共通ですよね？？

あのころのエモい感覚を再び思い出したい！
ということで今回は`Raspberry Pi Pico W`を使ってゲームの再現、アレンジをしていきます！

:::note warn
Raspberry Pi Picoには`無印タイプ`と`W付きタイプ`がありますが、**無線(Wireless)機能**が付くかどうかだけの違いです。
今回はどちらのタイプでも問題ありません。
:::

### ゲームの要件は？
簡単ではありますが、ゲームの要件は以下のようになるかと思います。
フロー図は[Claude 3.5 Sonnet](https://claude.ai/new)のArtifacts機能を使用して作成しました。

<img width="30%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3597730/a13a4d8f-d0c5-0a2e-48db-97199f5e3217.jpeg">

今回、**ボタン**はどこかで拾った詳細不明の**タクトスイッチ**、**フィードバック**は砲弾型**LED**を使用します。

**フィードバック**はWebアプリやエディタ上に表示すればよいではないかと思うかもしれませんが、愛用している**obniz**にはない**スタンドアロンで動作できる**点を活かすためにあえてLEDを使用しています。

:::note info
まだ`Raspberry Pi Pico`もしくは`Raspberry Pi Pico W`を使ったことがないという方は、以下の記事からまず **環境構築およびLチカ** をやってみてください。
少々古いですが、分かりやすいハンズオン記事です。
:::

https://qiita.com/relu/items/945bcb45120c30837745

## デモ

https://x.com/vanchan2625/status/1815702496662478988

計測タイムが
* 10秒±0.1秒であればスペシャル点滅パターン
* 10秒±0.3秒であれば青色点滅パターン
* 10秒±1秒であれば黄色点滅パターン
* それ以外であれば赤色点滅パターン
となります。

**スタンドアロン動作のためPC接続せずとも、モバイルバッテリーからの給電でゲームを楽しむことができます。**

## 開発環境
* [Thonny](https://thonny.org/) (Python用の無料オープンソース統合開発環境)

## 使用したもの
* Raspberry Pi Pico W（ラズベリーパイ財団）
* 抵抗内蔵 砲弾型 青色LED (OptoSupply社製 OSB5SA5B64A)
* 抵抗内蔵 砲弾型 黄色LED（OptoSupply社製 OSR6LU5B64A）
* 抵抗内蔵 砲弾型 赤色LED（OptoSupply社製 OSY5LU5B64A）
* タクトスイッチ（詳細不明）
* 圧電スピーカー (株式会社村田製作所 PKM13EPYH4000-A0)
* ミニブレッドボード×2 (Cixi Wanjie Electronic社製 BB-601(白))
* ジャンパーワイヤ（Cixi Wanjie Electronic社製 BBJ-20）

## 制作物
<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3597730/ea820fc4-3fbb-5612-272e-2dfa0513babb.jpeg">

### ポート表（使用したポートのみ抜粋）
| Port | デバイス | 機能 |
|:-:|:-:|:-:|
| GPIO13  | 青色LED（アノード側） | 出力 |
| GPIO14  | 黄色LED（アノード側）  | 出力 | 
| GPIO15  | 赤色LED（アノード側）  | 出力  | 
| GPIO16  | タクトスイッチ（VCC側）  | 入力（内蔵プルアップ）  | 
| GPIO18  | 圧電スピーカー（VCC側）  | 出力  | 
| GND  | 各LEDカソード側、タクトスイッチ（GND側）、圧電スピーカー（GND側）  | -  | 

<img width="100%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3597730/93176b3f-da6c-6deb-7088-79c244b4c747.jpeg">

## 今回のソースコード

<details><summary>←の三角マークを押すと確認することができます。</summary>

```py:main.py
from machine import Pin, PWM, Timer
import time

# GPIOピンの設定
led_red = Pin(15, Pin.OUT)
led_yellow = Pin(14, Pin.OUT)
led_blue = Pin(13, Pin.OUT)
button = Pin(16, Pin.IN, Pin.PULL_UP)
speaker = PWM(Pin(18))
led_onboard = Pin('LED', Pin.OUT)

# グローバル変数
button_pressed = False
last_press_time = 0
DEBOUNCE_TIME = 50  # デバウンス時間（ミリ秒）

def turn_off_leds():
    led_red.value(0)
    led_yellow.value(0)
    led_blue.value(0)

def turn_on_leds():
    led_red.value(1)
    led_yellow.value(1)
    led_blue.value(1)

def blink_led_with_sound(led, times, interval, freq):
    for _ in range(times):
        led.value(1)
        beep(freq, interval)
        led.value(0)
        time.sleep(interval)

def beep(frequency, duration):
    speaker.freq(frequency)
    speaker.duty_u16(32768)
    time.sleep(duration)
    speaker.duty_u16(0)

def special_blink():
    leds = [led_blue, led_yellow, led_red]
    freqs = [523, 587, 659]
    for _ in range(2):
        for led, freq in zip(leds, freqs):
            blink_led_with_sound(led, 2, 0.1, freq)
    for _ in range(3):
        turn_on_leds()
        beep(784, 0.1)
        turn_off_leds()
        time.sleep(0.1)

def button_handler(pin):
    global button_pressed, last_press_time
    current_time = time.ticks_ms()
    if time.ticks_diff(current_time, last_press_time) > DEBOUNCE_TIME:
        button_pressed = True
        last_press_time = current_time

def wait_for_button_press():
    global button_pressed
    led_onboard.value(1)
    button_pressed = False
    while not button_pressed:
        led_onboard.toggle()
        time.sleep(1)
    led_onboard.value(0)
    time.sleep(0.1)  # ボタンを離すのを待つ

def game():
    global button_pressed
    
    while True:
        turn_off_leds()
        
        print("タクトスイッチを押してゲームを開始してください。")
        wait_for_button_press()
        
        print("ゲーム開始！")
        for _ in range(3):
            turn_on_leds()
            beep(440, 0.1)
            turn_off_leds()
            time.sleep(0.1)
        
        print("10秒経ったと思ったらもう一度タクトスイッチを押してください。")
        start_time = time.ticks_ms()
        
        button_pressed = False
        while not button_pressed:
            time.sleep(0.01)
        
        end_time = time.ticks_ms()
        elapsed_time = (end_time - start_time) / 1000
        
        print(f"経過時間: {elapsed_time:.3f}秒")
        
        if abs(elapsed_time - 10) <= 0.1:
            print("素晴らしい！ぴったり10秒です！")
            special_blink()
        elif 9.7 <= elapsed_time <= 10.3:
            print("惜しい！もう少しで10秒でした。")
            blink_led_with_sound(led_blue, 5, 0.2, 659)
        elif 9 <= elapsed_time <= 11:
            print("まあまあです。もう少し頑張りましょう。")
            blink_led_with_sound(led_yellow, 5, 0.2, 587)
        else:
            print("残念！もっと練習しましょう。")
            blink_led_with_sound(led_red, 5, 0.2, 523)
        
        time.sleep(1)

# 割り込み設定
button.irq(trigger=Pin.IRQ_FALLING, handler=button_handler)

# メインループ
while True:
    game()
```
</details>

## ファイル名はmain.pyに！
ソースコードができて、Thonny上からの動作は確認することができました。
ただ、ソースファイルをラズパイPicoに書き込んでスタンドアロン動作させようと思うと**動かない。。。**

原因はファイル名を`main.py`にしていないことでした。
ラズパイPicoに電源を投入すると`main.py`のファイルを読みに行くため、該当のファイルがないと何も動作しないっぽいです。

以下の記事を参考にこの現象を解明することができました。

https://note.com/jkawasaki/n/n97cf6ea32cba

## あとがき
携帯を持っていなかった幼いころはこんな単純なゲームにも興奮していたことを思い出しました。

肝心の具体的な秒数のフィードバックがないことが不足点かと思いますが、この不完全さも悪くないと感じました。
計測タイムを確認するためには、**LCD（液晶ディスプレイ）** や **7セグLED**などを実装する必要がありそうです。

ゲームの進化は著しいですが、たまにはアナログなこんなミニゲームも意外と面白いものです。

## WISH
今回、統合開発環境(IDE)として **"Thonny"** を使用しましたが、当初はPython以外でも汎用性の高い **"[VS Code](https://azure.microsoft.com/ja-jp/products/visual-studio-code)"** を使用したいと思っていました。

ただ、Runが走らないなど環境構築の段階でつまづいてしまったため、比較的二次情報の多い"Thonny"を使用しました。

比較的に簡単と言われる`Raspberry Pi Pico`ですらこれだけ苦労したので、`Raspberry Pi 5`などになるともっと大変であると想像します。

一長一短ありますが、環境構築の簡単さからも電子工作初心者には[obniz](https://store.obniz.com/ja/products/obniz-board-1y?variant=31461635981390)の方がおすすめかもしません。

とはいえ、放置しておくには気持ちが悪いのでStack OverflowなどのQAサイトを利用して解決を図っていきたいです。

## おまけ
obnizとの違いを[Genspark](https://www.genspark.ai/)でまとめてみました。
よかったらご覧ください。

https://www.genspark.ai/spark?id=05f39050-d37f-4527-b2ac-b4bcfae4acb7

## これまで作製したプロダクトのご紹介

https://qiita.com/vanchan2625/items/3445b546e44649742c71

https://qiita.com/vanchan2625/items/3892184ef8b37bf40ce9

https://qiita.com/vanchan2625/items/00b9caeb1dadd36d6f5e

https://qiita.com/vanchan2625/items/18791b8873c94dee139a