Pico早押し機

Raspberry Pi Pico を使って早押し機を作りました! (2022/5/1 にバージョンアップしました)

ソースコード一式 uf2ファイル 回路図

単体でも12端子用早押し機として動作します。(音を鳴らすためには別途スピーカーが必要です。)

PCとシリアル接続させることによって非常に多くの拡張機能を実現させています。

(改めて見返すと、「なんでこんな機能を作ったのだろう」というものもありますが。)

また、どのボタンがいつ押されたかを送信するため、PCのプログラムを頑張ればオリジナルの早押しクイズゲームを作ることができます。


サンプルとして、ボタンに連動してマルチメディアキーの「再生・一時停止」を押す Python スクリプト を書いてみました。あくまでもサンプルです。

再生時に早押しボタンを押すと一時停止、リセットボタンで再生・一時停止、問題出題ボタンで停止・次のトラックへ移動・再生、が押されます。

(もう少しちゃんと作れば)問題読み上げの音声 or 動画 で読み上げクイズをしたり、楽曲を入れてイントロクイズ を楽しむことができるはずです。

基本性能

Micro USB からの電源で動く12端子の早押し機・ランプは通常の LED なので原則として室内用

時間分解能は平均で 1マイクロ秒だが、タイミングが悪いと 15マイクロ秒程度になることがある

1着~12着までの着順判定があり、不正解時には次の着順に解答権が移る

レリーズは、正解・不正解・リセット・問題出題の4種類

デフォルトでは誰もボタンを押していないときの正解ボタンは「5秒のカウントダウンの後にスルーを示す不正解音を鳴らす」ボタンとして機能する

<早押し機ボタン操作>

o 正解 正解が規定数出たらリセット

 誰も押していない時は音が鳴るだけ

 長押し時は正解音が長く出る

x 不正解 不正解が規定数出たらリセット

 誰も押していない時に押してもリセット

 長押し時は不正解音が長く出る

リセット 音を出さずにリセット

 長押しするとモード・ハンディキャップなどをすべてリセットする

問題 リセット直後に押すと問題を出す音を出す

   その後に押すと5秒のカウントの後に不正解音と共にリセット(誰かがボタン押すか判定ボタンを押したらキャンセル)

   ランプが付いている時に押すと5秒のカウントダウンを行う(判定ボタンを押したらキャンセル)

長押しするとモードセレクトに移行する


<早押し機ボタン操作・モードセレクト時>

正解ボタン モード1.3.5.7 になる

不正解ボタン モード2.4.6.8になる

リセットボタン モードセレクトをキャンセルする

問題ボタン モードを 12.34.56.78 と順送りする


<それぞれのモードの説明>

モード1 デフォルトモード

 エンドレスチャンス(不正解時は解答できない)、正解でリセット

モード2 タイムアタックモード

 シングルチャンス、問題ボタンのカウントが2秒になる、問題出題音が省略される

モード3 一問多答クイズモード

 正解でリセットされない、正解が続く限り何度も解答権が取得可能

モード4 ハンディキャップモード

 ボタンを押しているプレイヤーに+0.1秒のハンデを設定する

モード5 チーム戦モード

 チーム内でランプは1つしかつかないようになるモード

モード6 フェンシング判定

 最初の人と0.04秒以内に押した人にもランプがつくモード

 ボタンを押していると押した個数によって同着判定時間が長くなる

モード7 バラエティモード

 エンドレスチャンス&多答&間違えても押せるバラエティクイズっぽいモード

モード8 外部センサーモード

 偶数番の端子に音声センサーをつけてシャウトでもランプがつくようにする

 (偶数番の端子に、ランプとセンサーのVin、ボタンとセンサーの出力、グランド同士をつなぐ)

Pico からの出力

<即時反応を行うのが望ましいもの>

A-Z [chaku] [time] プレイヤー1-26のボタンが押されてライトが付いた

[chaku] はすでに押されているボタンの数

[time]は内部的な時間(マイクロ秒単位)、差をを取ればマイクロ秒単位で差が判明する


プレイヤー1のボタンが押されたときの出力

A 0 12345678 プレイヤー1が1番目の着順(つまりほかの人が押していない状態で)で押した

A 3 12345678 プレイヤー1が4番目の着順(他のプレイヤーのうち3人がすでに押している状態で)で押した

プレイヤー2のボタンが押されたときの出力(以降、C, D, E, ... と続く)

B 2 12345678 プレイヤー2が3番目の着順(他のプレイヤーのうち2人がすでに押している状態で)で押した

(A-Z はいつでてきても即座に反応してほしいが、一応文頭に来る)


<CR もしくは LF を受け取った時点で反応すればよいもの>

reset @ [time] [delay1] [delay2] リセットのボタンが押されたことを示す。

[time]は内部的な時間(マイクロ秒単位)、差をを取ればマイクロ秒単位で差が判明する

(delay1 は早押しボタンの取得周期の最悪値、delay2 はサブルーチンの周期の最悪値で、

  プログラムのループが遅延していないか確認するために出力している。

  delay1 は 20us 以下、delay2 は 1000us 以下であれば無理はさせていないと考える)


reset @ 12345678 14 310


reset @ [time] 正解・不正解が規定の回数に達したなどの理由でリセットされたことを示す

[time]は内部的な時間(マイクロ秒単位)、差を取ればマイクロ秒単位で差が判明する


correct [a-z@] [time] 正解ボタンが押された

a-z は解答権が誰にあったかを示す(@は誰も押していない状態の解答)

[time]は内部的な時間(マイクロ秒単位)、差を取ればマイクロ秒単位で差が判明する


correct @ 12345678 だれも押していない状態で正解

correct a 12345678 プレイヤー1が正解 (b なら プレイヤー2, ... と続く)


wrong [a-z@] [time] 不正解ボタンが押された

a-z は解答権が誰にあったかを示す(@は誰も押していない状態の解答)

[time]は内部的な時間(マイクロ秒単位)、差を取ればマイクロ秒単位で差が判明する


wrong @ 12345678 だれも押していない状態で不正解

wrong a 12345678 プレイヤー1が不正解 (b なら プレイヤー2, ... と続く)


question [time] (出題音が鳴る状態で)問題ボタンが押された

[time]は内部的な時間(マイクロ秒単位)、差を取ればマイクロ秒単位で差が判明する


question 12345678


timer [time] (タイマーカウントが鳴る状態で)問題ボタンが押された・カウント音が鳴る

[time]は内部的な時間(マイクロ秒単位)、差を取ればマイクロ秒単位で差が判明する


timer 12345678


untimer [time] (タイマーカウントが鳴っている状態で)問題ボタンが押された・カウント音がリセットされる

[time]は内部的な時間(マイクロ秒単位)、差を取ればマイクロ秒単位で差が判明する


untimer 12345678


again [a-z] [time] プレイヤーの解答権を復活させる(a-z が 1-26に対応)

[time]は内部的な時間(マイクロ秒単位)、差を取ればマイクロ秒単位で差が判明する


again a 12345678


feedmenu [page] 問題ボタンを押して、設定メニューの[page]目に移動したことを示す

setmode [num] 設定によって、モードが[mode]に移行したことを示す

execute:文字数:コマンド シリアルからコマンドが入力されたことを表示する

$ は設定を変更したことを示す

$h[a-z][time] 遅延ハンディキャップを設定


# で始まる行は無視して良いことを示す

シリアルからの入力によって実行された内容は # 付きのコメントとして返す

#reset (シリアルから . が入力されて実行した)

#correct (シリアルから @ が入力されて実行した)

#wrong (シリアルから / が入力されて実行した)

#question (シリアルから , が入力されて実行した)

#timer (シリアルから : が入力されて実行した)

#feedmenu (シリアルからメニューの設定がされた)

#long push reset リセットボタンの長押し(モードのリセット)がされたことを表示

#long push question 問題の長押し(モードの選択)がされたことを表示


Pico への入力

<即時反応するボタン>

A-Z プレイヤー1~26の解答ランプを点灯させる

/ 不正解ボタンを押す

@ 正解ボタンを押す

. リセットボタンを押す

, 準備ができていれば出題音を出す

: カウントダウンを行う

<コマンドとして CR or LF が入力されたら実行する>

m チーム戦の設定を行う

NULL チーム戦を解除して個人戦にする

0 チーム戦を解除して個人戦にする

1[num] [num]チームに分けるが、 1, 2, ..., [num], 1, ... と分ける

2[num] [num]チームに分けるが、 1, ..., 2, ..., [num], [num] と分ける

3[num_x] 各プレイヤーを、[num_1], [num_2], ... と分ける(人数分チーム番号を指定する)

h ハンディキャップを設定する

NULL 全員のハンディキャップを削除する

0[time] 全員のハンディキャップを [time]ミリ秒に設定する

[a-z][time] プレイヤー[num]のハンディキャップを [time]ミリ秒に設定する

r 複数回押さないと解答ランプがつかないようにする

NULL 全員の連打回数を解除する(普通に1回押せば解答ランプがつくようにする)

0[time] 全員の連打回数を [time]回に設定する

[a-z][time] プレイヤー[num]の連打回数を [time]回に設定する

j ボタンとランプを連動させるか

NULL 連動させる(独立した早押し機として機能させる)

0 連動させる(独立した早押し機として機能させる)

1 連動させない(ボタンの情報をシリアルに送信し、PC側で処理したランプの情報を受け取って動作させる)

z 失った解答権を復活させる

NULL 全員の解答権を復活させる

0 全員の解答権を復活させる

[a-z] プレイヤー[a-z]の解答権を復活させる

n リセットまでに必要な正解の個数・不正解の個数・解答の個数を制御する

c[time] 正解の個数を[time]個に設定する

w[time] 不正解の個数を[time]個に設定する

a[time] 解答の個数を[time]個に設定する


nc リセットがかかるまでの正解数を 1 に設定する(いわゆる一問一答)

nc2 リセットがかかるまでの正解数を 3 に設定する(いわゆる一問二答)

nc999 リセットがかかるまでの正解数を 999 に設定する(いわゆる一問多答)

nw リセットがかかるまでの不正解数を 999 に設定する(いわゆるエンドレスチャンス)

nw1 リセットがかかるまでの不正解数を 1 に設定する(いわゆるシングルチャンス)

nw2 リセットがかかるまでの不正解数を 2 に設定する(いわゆるダブルチャンス)

nw999 リセットがかかるまでの不正解数を 999 に設定する(いわゆるエンドレスチャンス)

na リセットがかかるまでの解答数の制限を無効にする(nc, nw を使うと自動的に解答数が調整される)

na2 リセットがかかるまでの解答数を 2 に設定する(一問二答で2着まで解答が有効にしたいときに使う)


p プレイヤーの人数・GPIOピンの割り当てを変更する

p#[time] プレイヤー数を Time に設定(1-26)

p[a-z][command][param] プレイヤー[a-z]のGPIO を変更する


g GPIOピンのステータスを強制的に変更する

g[0-9a-v][param] GPIO 0-9 or 10-31 を in/out に変更する


w 不正解からの復活時間を指定する

w0[time] 全員の誤答からの復活時間を[time]マイクロ秒に設定する

w[a-z][time] プレイヤー[a-z] の誤答からの復活時間を[time]ミリ秒に設定する

[time] にゼロを設定するか何も書かないと 10分間(実質復活なし)に設定する


c 正解からの復活時間を指定する

c0[time] 全員の正解からの復活時間を[time]マイクロ秒に設定する

c[a-z][time] プレイヤー[a-z] の正解からの復活時間を[time]ミリ秒に設定する

[time] にゼロを設定するか何も書かないと 10分間(実質復活なし)に設定する


x 正解・不正解の待ち時間ルールを設定する

0[time] 全員にルール[time]を適用する

[a-z][time]

0 定数

1 定数

2-10 n回目の正解・不正解で解答権を失う

11 正解・不正解ごとに上で指定された分待ち時間が比例的に増える(5, 10, 15, 20, ...)

12-20 11の設定に加えて(n-10)回の正解・不正解で解答権を失う

21 正解・不正解ごとに上で指定された分待ち時間が倍々に増える(5, 10, 20, 40, ...)

22-30 21の設定に加えて(n-20)回の正解・不正解で解答権を失う

t 同時判定を防ぐための時間設定

tr[time] リセット付きの正誤判定ボタンが押された後の解答ボタンの無効時間を[time]ミリ秒に設定する

tf[time] 最初のボタンが押された後の正誤判定ボタンの無効時間を[time]ミリ秒に設定する

td[time] 1st解答権のボタンが押された後、解答ボタンが無効になるまでの時間を[time]ミリ秒に設定する

[time] にゼロを設定するか何も書かないとこの機能自身が無効となる

tq[time] 最初に問題ボタンを押したときに、

[time]ミリ秒のカウントダウン->不正解を取るようにする。

[time] にゼロを設定するか何も書かないとこの機能自身が無効となる(単に正解音が鳴るだけとなる)

tt[time] 2回目に問題ボタンを押したときに、

[time]ミリ秒のカウントダウン->不正解を取るようにする。

[time] にゼロを設定するか何も書かないとこの機能自身が無効となる(単に正解音が鳴るだけとなる)

tc[time] ランプがついているときに問題ボタンを押したときに、

[time]ミリ秒のカウントダウン->不正解を取るようにする。

[time] にゼロを設定するか何も書かないとこの機能自身が無効となる(単に正解音が鳴るだけとなる)

tw[time] ランプがついていないときに正解or不正解ボタンを押したときに、

[time]ミリ秒のカウントダウン->不正解を取るようにする。

[time] にゼロを設定するか何も書かないとこの機能自身が無効となる(単に正解音が鳴るだけとなる)


b ランプの光り方を設定する

bn 1着点滅・2着点灯・3着以降が瞬間点滅

bm 1着点滅・2着以降が点灯

br 1着点灯・2着点滅・3着以降が瞬間点滅

bs 1着点灯・2着以降が点滅

bc 1着~12着点滅

bt 1着~12着点灯


v 効果音の有効・無効を設定する

v[num] num で指定した音を無効にする(和を取ると複数指定が可能)

2: 解答ボタンを押す音

4: 不正解の音

8: 正解の音

16: 問題出題時の音

32: カウントダウンの音


d ハンディキャップ・連打回数を表示する

h[player] ハンディキャップを出力する $h[player][time_microsecond] と出力する

r[player] 連打回数を指定する $r[player][time_microsecond] と出力する


! 特定のクイズモードをまとめて選択する

! デフォルトのエンドレスチャンスモード

!n デフォルトのエンドレスチャンスモード

!t タイムレース用のシングルチャンスモード

!m 多答クイズ:正解しても不正解でも解答は一度のみ

!l 多答クイズ:正解したら続けて解答できるが不正解の場合は解答権を失う

!z 多答クイズ:早抜けクイズ:不正解が続く限り解答権があるが、正解したら解答権を失う

!c (カプリディオでよく行われる)お手付き2回で解答権を失う

!f フェンシングの判定機のように、最初のボタン点灯から 0.04秒以内に押されたボタンのみを2着以降判定する

!v バラエティ用のエンドレス&誤答ペナルティなし

!0 早押し機でリセットの長押しをした時に移行する、デフォルトモードに移行する

![0-8] 早押し機で設定できる、モード1-8に移行する


\ デバッグ用に何か出力する