62
Raspberry Pi Picoでサイモンゲームを作る方法

Simonさん、Raspberry Pi Pico、お誕生日おめでとうございます!2021年1月21日にリリースされたRaspberry Pi初のチップ、RP2040は、2021年の必須マイクロコントローラーとなりました。この0.70ドルのチップは、世界中のプロジェクトを支えています。シンプルなLEDの点滅から3Dプリンターの制御ボードまで、RP2040は汎用性の高いチップです。Raspberry Pi Picoは、最新のArduinoボードで使用されているDILパッケージに似た、4ドルのマイクロコントローラー開発ボードで、低コストでファーストパーティ製ハードウェアでありながら、ファーストパーティおよびサードパーティによる優れたサポートを提供しています。

Picoは大好きです。多くのメーカーが切望する低価格と使いやすさの絶妙なバランスを実現しています。Raspberry Piのアドオン一般的な電子部品を再利用できるため、わざわざアクセサリを買い足す必要がありません。Picoにアクセサリが不足しているわけではありません。Pimoroni、Adafruit、Kitronikといった企業が、Picoの体験をさらに充実させる素晴らしいアクセサリをリリースしています。 

Raspberry Pi Picoの1歳の誕生日を祝って、ゲームを作りました。ただのゲームではなく、1970年代後半に有名になった記憶ゲーム「Simon」のオリジナルバージョンです。ゲームの目的は、ランダムに選ばれた色の順番を一致させることです。正解すると光のショーが楽しめますが、間違えると赤色になってしまいます。

このプロジェクトのハードウェアはすべてブレッドボードベースで構築されており、Raspberry Pi Picoのシンプルさを体現しています。配線はシンプルで、出力は8個のLEDを搭載したNeoPixelスティック1個、入力は色分けされたボタン4個です。Picoのプログラミングには、使いやすいハードウェアライブラリを備えたAdafruitのMicroPythonの優れたフォークであるCircuitPythonを使用しています。

さあ、パーティーを始めましょう!

このプロジェクトに必要なものは 

  • ラズベリーパイ ピコ
  • 8ピクセルのNeoPixelスティック
  • ハーフサイズのブレッドボード 2 枚(またはフルサイズのブレッドボード 1 枚)
  • 11 x オス-オスジャンパーワイヤー
  • モーメンタリースイッチ 4 個

Raspberry Pi PicoでSimonゲームを作る

ビルドには 2 つの部分があります。4 つのプッシュ ボタンがユーザー インターフェイスを形成し、NeoPixel スティックが出力になります。

Raspberry Pi Picoを使ったサイモンゲーム

(画像提供:Tom's Hardware)

NeoPixelはデータ用にGPIO 28(CircuitPythonではGP28)に接続され、電源にはUSBバスから直接5V(VBUS)ピンを使用します。GNDはPico上の任意のGNDに接続します。

Tom's Hardware の最高のニュースと詳細なレビューをあなたの受信箱に直接お届けします。

Raspberry Pi Picoを使ったサイモンゲーム

(画像提供:Tom's Hardware)

4 つのボタンは、Pico の 4 つの GPIO ピンに接続します。

スワイプして水平にスクロールします

ボタンの色ラズベリーパイ ピコ GPIO
GPIO 27
GPIO 26
GPIO 22
黄色GPIO 21

ボタンブレッドボードのマイナスレール(-印)を使って、PicoからGND接続(黒線)を1つ取り出し、GNDレールを作成します。そこから4本のGND接続をボタンに分岐させます。これにより、ボタンは押された時にGND(グランド)にプルダウンされます。 

Raspberry Pi Picoを使ったサイモンゲーム

(画像提供:Tom's Hardware)

最終的な回路は上記のようになります。先に進む前に配線を確認してください。

CircuitPythonとライブラリをインストールする

CircuitPythonのインストールは非常に簡単で、Adafruitにはインストール方法に関する素晴らしいガイドが用意されています。Adafruitの指示に従って最新バージョンをインストールしてください。 

Raspberry Pi Picoを使ったサイモンゲーム

(画像提供:Tom's Hardware)

Raspberry Pi Picoがファイルマネージャーにドライブとして表示されます。ドライブ名はCIRCUITPYで、そこには一連のファイルとフォルダがあります。今はlibに注目しましょう。ライブラリが詰まったZIPファイルをダウンロードし、プロジェクトに必要なファイルをインストールする必要があります。

1. CircuitPython サイトから最新のライブラリ バンドルをダウンロードします。

Raspberry Pi Picoを使ったサイモンゲーム

(画像提供:Tom's Hardware)

2.ファイルをデスクトップに抽出します。

3.抽出したファイルからadafruit_pixelbuf.mpy と neopixel.mpy をCIRCUITPY ドライブの lib フォルダにコピーします。

Raspberry Pi Picoを使ったサイモンゲーム

(画像提供:Tom's Hardware)

Raspberry Pi PicoでSimonゲームのコードを書く

1. CIRCUITPY ドライブのルートにあるcode.py ファイルを任意のエディターで開きます。

2.ファイル内のすべてのコードを削除します。

3.ゲームの速度制御とGPIOへのアクセスに必要なライブラリをインポートします。TimeとBoardは、CircuitPythonでよく使われるライブラリです。Timeにはコードを一時停止する関数が含まれており、Boardは基本的なGPIOアクセスを提供します。

import time
import board

4. NeoPixelを詳細に制御するために、さらに2つのライブラリをインポートします。Colorwheelは、虹のサイクル効果を作成するために使用するアニメーションツールです。NeoPixelは、8つのRGB LEDを制御するために使用するライブラリです。

from rainbowio import colorwheel
import neopixel

5.最後に2つのライブラリをインポートします。randomからchoiceuniformをインポートし、digitailioからDigitalInOutをインポートします。DirectionとPullは、入出力、ピンの方向、プルアップ抵抗のサポート(抵抗のサイズを識別するには、抵抗のカラーコードを参照してください)を処理します。Randomには多くの機能がありますが、choiceはリスト、タプル、辞書などから「ランダムに」選択できます。Uniformは浮動小数点数用の乱数ジェネレータです。DigitalIOはBoardライブラリと連携して、GPIOの使用方法を微調整します。今回のケースでは、入力(ボタン)を使用し、GPIOピンの内部抵抗をプルアップ(3.3V)に設定します。

from random import choice, uniform
from digitalio import DigitalInOut, Direction, Pull

6. NeoPixel を制御するために使用するピンとして GPIO 28 を設定し、 NeoPixel LED の数を格納するnum_pixels という変数を作成します

pixel_pin = board.GP28
num_pixels = 8

7.オブジェクト「pixels」を作成し、GPIOピンとピクセル数を識別する2つの変数を使ってNeoPixelsに接続します。また、目の疲れを軽減するため、明るさを0.3(30%)に設定します。

pixels = neopixel.NeoPixel(pixel_pin, num_pixels, brightness=0.3, auto_write=False)

8. GPIO 27 に接続された赤いボタンを表すオブジェクト red を作成します。

red = DigitalInOut(board.GP27)

9.赤いボタンの GPIO ピンを入力として設定します。

red.direction = Direction.INPUT

10.赤ボタンのGPIOピンをハイレベルに設定します。プルアップ抵抗を使用して、GPIO 27を3.3Vにプルアップします。ボタンとPicoの接続方法により、赤ボタンが押されるとこのピンはGND(ローレベル)にプルダウンされます。

red.pull = Pull.UP

11.緑のボタンについてもこの手順を繰り返します。

green = DigitalInOut(board.GP26)
green.direction = Direction.INPUT
green.pull = Pull.UP

12.青についても同様に繰り返します。

blue = DigitalInOut(board.GP22)
blue.direction = Direction.INPUT
blue.pull = Pull.UP

13.黄色についても同様に繰り返します。

yellow = DigitalInOut(board.GP21)
yellow.direction = Direction.INPUT
yellow.pull = Pull.UP

14.ボタンの RGB カラー値を保存しLED をオフにするための 5 つのタプル (データ ストレージ オブジェクト) を作成します。

RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 150, 0)
OFF = (0, 0, 0)

15.すべてのNeoPixelを循環的に切り替え、虹色のエフェクトを表示する関数rainbow_cycleを作成します。この関数は、Adafruitの標準デモ「ストランドテスト」の一部です。

def rainbow_cycle(wait): for j in range(255): for i in range(num_pixels): rc_index = (i * 256 // num_pixels) + j pixels[i] = colorwheel(rc_index & 255) pixels.show() time.sleep(wait)

16. 「colors」というリストを作成し、先ほど作成した色のタプルを格納します。

colors = [RED, GREEN, BLUE, YELLOW]

17. 10回繰り返すforループを作成します。  ループが繰り返されるたびに、rainbow_cycle関数を使用して、ゲーム開始時にライトショーを作成します。

for i in range(10): rainbow_cycle(0)

18. プロジェクトの本体は、ゲームコードを実行するwhile Trueです。while Trueループを作成し、pixelsオブジェクトを呼び出してLEDを消灯させます。

while True: pixels.fill(OFF) pixels.show() 

19.ランダムに選択された色の順序を保存するための 2 つの空のリストと、プレーヤーの推測を保存するための 2 つの空のリストを作成します

 rnd_colors = [] player = []

20.ゲーム開始をトリガーするif条件テストを作成します。ゲームを開始するには、緑のボタンを押す必要がありますこのピンはプルアップ抵抗によってハイにプルアップされているため、ピンがロー(False)になったときにゲームを開始する必要があります。ボタンは、ハイにプルアップされたGPIOピンとGNDに接続されています。そのため、ボタンが押されると、ピンはローにプルダウンされます。

 if green.value == False:

21. 0.3から1.0までのランダムに選択された浮動小数点値を保存するための変数「delay」を作成します。この変数をround()関数で囲み、出力を小数点1位にフォーマットします。出力をPythonシェルに出力してデバッグします。これにより、0.3から1までの乱数が生成され、プレイヤーに表示される色シーケンスの間に一時停止が作成されます。

 delay = round(uniform(0.3,1.0),1) print(delay)

22. forループを使って、ランダムに選ばれた4つの色をrnd_colorsリストに追加し、デバッグ用にPythonシェルに出力します。これらの色がプレイヤーが推測する色になります。

 for i in range(4): rnd_colors.append(choice(colors)) print(rnd_colors)

23. プレイヤーに色のシーケンスを表示します。このforループは、rnd_colorsリストに格納されている4つの色を反復処理します。ループが実行されるたびに、pixels.fill()でRGB値を使用してその色を表示します。 

 for color in rnd_colors: print(color) pixels.fill(color) pixels.show()

24.ランダムに選択された遅延を使用して色を表示し、同じ遅延を使用して LED をオフにするように設定します。

 time.sleep(delay) pixels.fill(OFF) pixels.show() time.sleep(delay)

25. while ループを使用してプレーヤー リストの長さを確認し、このループに 0.3 秒の遅延を追加して、ボタンが 2 回押される可能性を減らします (デバウンス)。インデントされたコードは、プレーヤーの推測が 4 回未満の場合にのみ実行されます。

 while len(player) <4: time.sleep(0

26.各ボタンの状態を読み取り、押された色を保存し、デバッグ用に値を表示するif文を作成します。まず赤いボタンです。押されると、RGBカラー値がプレイヤーリストに追加されます。次に、デバッグ用にPythonシェルに値を表示します。

 if red.value == False: player.append((255,0,0)) print(player)

27.プレイヤーにフィードバックを提供するには、ピクセルをオフにする前に、NeoPixels を 0.1 秒間選択した色に設定します。

 time.sleep(0.1) pixels.fill(OFF) pixels.show()

28.緑のボタンについても同様の手順を繰り返します。elif文(else if文)を使って、この条件が真かどうかを確認します。

 elif green.value == False: player.append((0,255,0)) print(player) pixels.fill(GREEN) pixels.show() time.sleep(0.1) pixels.fill(OFF) pixels.show()

29.青いボタンについても、同じ手順を繰り返します。

 elif blue.value == False: player.append((0,0,255)) print(player) pixels.fill(BLUE) pixels.show() time.sleep(0.1) pixels.fill(OFF) pixels.show()

30.黄色のボタンについても、同じ手順を繰り返します。

 elif yellow.value == False: player.append((255, 150, 0)) print(player) pixels.fill(YELLOW) pixels.show() time.sleep(0.1) pixels.fill(OFF) pixels.show()

31. if条件文を使って、プレイヤーの推測とランダムに選ばれた値を比較します。プレイヤーが4つの推測を入力すると、ゲーム開始時に選択された値と比較されます。

 if player == rnd_colors:

32. for ループを使用して 10 回反復するよう設定し、rainbow_cycle() を実行してプレイヤーが勝ったことを示します。

 for i in range(10): rainbow_cycle(0)

33. NeoPixelを3回点滅させるelse条件を作成します。プレイヤーが間違った答えを予想した場合、NeoPixelが赤く点滅します。

 else: for i in range(3): pixels.fill(RED) pixels.show() time.sleep(0.2) pixels.fill(OFF) pixels.show() time.sleep(0.1)

34.コードを code.py として Raspberry Pi Pico に保存すると、rainbow_cycle() 関数を実行してゲームが開始されます。

35.緑のボタンを押してゲームを開始します。

Raspberry Pi Pico の Simon ゲームの完全なコードリスト

import time
import board
from rainbowio import colorwheel
import neopixel
from random import choice, uniform
from digitalio import DigitalInOut, Direction, Pull #NeoPixel setup
pixel_pin = board.GP28
num_pixels = 8
pixels = neopixel.NeoPixel(pixel_pin, num_pixels, brightness=0.3, auto_write=False)
#Button Setup
red = DigitalInOut(board.GP27)
red.direction = Direction.INPUT
red.pull = Pull.UP green = DigitalInOut(board.GP26)
green.direction = Direction.INPUT
green.pull = Pull.UP blue = DigitalInOut(board.GP22)
blue.direction = Direction.INPUT
blue.pull = Pull.UP yellow = DigitalInOut(board.GP21)
yellow.direction = Direction.INPUT
yellow.pull = Pull.UP # RGB Color Codes
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 150, 0)
OFF = (0, 0, 0) #Rainbow Animation
def rainbow_cycle(wait): for j in range(255): for i in range(num_pixels): rc_index = (i * 256 // num_pixels) + j pixels[i] = colorwheel(rc_index & 255) pixels.show() time.sleep(wait)
#List of colors
colors = [RED, GREEN, BLUE, YELLOW]
#The "attract sequence to entice people to play
for i in range(10): rainbow_cycle(0) while True: pixels.fill(OFF) pixels.show() rnd_colors = [] player = []
#Wait for the green button start the game if green.value == False: delay = round(uniform(0.3,1.0),1) print(delay) for i in range(4): rnd_colors.append(choice(colors)) print(rnd_colors) for color in rnd_colors: print(color) pixels.fill(color) pixels.show() time.sleep(delay) pixels.fill(OFF) pixels.show() time.sleep(delay)
#Player has four guesses, each guess saves the color choice to player list while len(player) <4: time.sleep(0.3) if red.value == False: player.append((255,0,0)) print(player) pixels.fill(RED) pixels.show() time.sleep(0.1) pixels.fill(OFF) pixels.show() elif green.value == False: player.append((0,255,0)) print(player) pixels.fill(GREEN) pixels.show() time.sleep(0.1) pixels.fill(OFF) pixels.show() elif blue.value == False: player.append((0,0,255)) print(player) pixels.fill(BLUE) pixels.show() time.sleep(0.1) pixels.fill(OFF) pixels.show() elif yellow.value == False: player.append((255, 150, 0)) print(player) pixels.fill(YELLOW) pixels.show() time.sleep(0.1) pixels.fill(OFF) pixels.show()
#If the player guesses correctly they get a nice rainbow if player == rnd_colors: for i in range(10): rainbow_cycle(0)
#If you lose, you see red! else: for i in range(3): pixels.fill(RED) pixels.show() time.sleep(0.2) pixels.fill(OFF) pixels.show() time.sleep(0.1)

レス・パウンダーは、トムズ・ハードウェアのアソシエイトエディターです。クリエイティブテクノロジストとして、7年間にわたり、老若男女を問わず、教育と啓発のためのプロジェクトを手がけてきました。Raspberry Pi Foundationと協力し、教師向けトレーニングプログラム「Picademy」の執筆・提供にも携わっています。