お年玉抽選マシーンをRaspberry Pi Picoでつくってみた

この記事について

この記事は Akatsuki Games Advent Calendar 2023 の4日目の記事です。
昨日の記事はちょう さんの 「負荷に強い(かもしれない)サーバーエンジニアが開発時に心掛けていること」でした。
サーバの負荷はぼくもきになるジャンルなのでとても興味深い記事でした。

発端

先日友人から「お年玉の金額をその場でランダムで決めたい」って相談受けたのが発端

要求仕様としてはこんなかんじ

・0~9999円の数字がランダムで出る
・確率はすべて均等
・出来上がったら郵送

というところを踏まえて話しながら結果的に仕様はこんなかんじ

・Raspberry pi picoを使用
・小形で比較的安価なカラー液晶を使用
・適当なガワ(ケース)にはいっている
・モバイルバッテリーなどのUSB電源で動作
・抽選開始押しボタンを別途用意
・音が鳴る

パーツ

Raspberry Pi Pico
今回WiFiは使わないのと材料費すこしでも安くしたいのでWじゃない方。
フラッシュストレージにファイルとして音声ファイルなどを書き込める点も採用理由。
おかげでSDカードスロットのハードウェアと読み出しルーチンの実装が省けるのでよき。700円くらい。
https://www.marutsu.co.jp/pc/i/2194960/

Raspberry Pi Pico用 1.14インチ 液晶モジュール 240×135
型番的にはこれ
WAVESHARE-19340
ピンヘッダさすだけの簡単確実接続。
フレキシブルケーブルとかあると輸送中に壊れたりしやすそうなのと、
ワンチャン好評で令和最新版電子サイコロとして量産するときも楽ということでこれにした。
あとカラー液晶としてはそこそこ安いのも理由。
タクトスイッチ2つと十字キーがついているけど今回は直接使用せず。1340円。
https://www.marutsu.co.jp/pc/i/2229754/

超小型D級アンプキット
最初PWMの音声出力を直接スピーカーにつないでいたが、さすがに音が小さすぎるので追加。
外付けのアンプスピーカにまかせればいいかとあきらめかけたけど
ちょうど固定ゲインの小型のアンプキットがあったので採用。しかも安い。300円。
https://akizukidenshi.com/catalog/g/gK-08161/

リード線
押しボタンなどなにかと無理やり配線する用

100円ショップのプッシュライト
見た目が昔懐かしのへぇボタンに似ているアレ。
中身のスイッチがトグルスイッチなのでプッシュスイッチに取り換えて使用。いまどきめずらしいちゃんと100円。

プッシュスイッチ
友人の親戚のキッズたちに乱暴に押されるかもしれないのでホームセンター電工用のちょっと頑丈なやつにした

100円ショップの置き時計(ケースとして)
最初そのまま液晶を転用できないか検討したけど、型番など不明なのでロジックアナライザとかで解析しないといけないので時間的にあきらめ。いろいろ取り外してガワだけつかうことに。ふりかえってみるとタッパウェアとかでもよかったんじゃないか説。高性能なので意外と高い。500円。
https://jp.daisonet.com/collections/electricity0220/products/4550480131825

Type-C – 3.5mmイヤフォンマイクジャック変換ケーブル
ばらしてイヤフォンジャックとしてL,R,GNDだけ使う。
直でスピーカーつないでもいけど任意の有線スピーカが使えるように。100円。
https://ec.cando-web.co.jp/item/4962242489325


有線スピーカー
100円ショップで最近はなかなか売ってないので意外と貴重。
ほとんどBluetoothになってしまった。500円とかでBluetoothスピーカー売ってるのもすごい。なんか隔世の感がある。

表面覆う用ウレタン
ホームセンターでいい感じのやつを選定。B4で300円くらい。

その他手元にあったいろいろ
部屋の防音につかった吸音ウレタンとか

プロトタイプ

最初は数字が4桁ランダムででるものを作成

まずはピンヘッダをつける。ブレッドボードにさしてはんだ付けすると楽

公式手順にそってPico本体にあるボタンをおしながらPCとつないでファームウェアインストール

正常に更新できたらいったんPCとの接続を外したのちに
液晶をPinを確認しながら接続する

https://www.waveshare.com/wiki/Pico-LCD-1.14#Python_codes より

GPIO8 ~ 13は液晶制御で使っているのでここは使わないようにする。

IDEのThonnyをインストール

ひとまずMicroPythonのサンプルコードをうごかしてみる

ちゃんとAボタンとの入力もとれてる

しかしライブラリのドキュメントを読むとこのままだと小さい文字しか出せないとのこと
そこでしらべてみたところこのような感じでいったん普通に描画した後指定の色のピクセルだけ拾って拡大した文字データを作成している方がいらっしゃった。
ただ、このままだとたまたま描画したい文字の色がもともと書かれていた場合文字が崩れてしまうので、背景色を指定することにして描画領域をその色で塗りつぶしてから拡大処理を行うようにした。
かわりに背景透過で描画するのはできなくなったけれど今回の目的としてはヨシなのでひとまずこれで。

    def write_text(self,text,x,y,size,color,background):
        ''' Method to write Text on OLED/LCD Displays
            with a variable font size

            Args:
                text: the string of chars to be displayed
                x: x co-ordinate of starting position
                y: y co-ordinate of starting position
                size: font size of text
                color: color of text to be displayed
        '''
        # background = self.pixel(x,y)
        info = []
        # 拡大処理を行うまえに作業領域をbackgroundの色で塗りつぶしておく
        self.fill_rect(x,y,8*len(text),8,background)
        # Creating reference charaters to read their values
        self.text(text,x,y,color)
        for i in range(x,x+(8*len(text))):
            for j in range(y,y+8):
                # Fetching amd saving details of pixels, such as
                # x co-ordinate, y co-ordinate, and color of the pixel
                px_color = self.pixel(i,j)
                info.append((i,j,px_color)) if px_color == color else None
        # Clearing the reference characters from the screen
        # self.text(text,x,y,background)
        self.fill_rect(x,y,8*size*len(text),8*size,background)
        # Writing the custom-sized font characters on screen
        for px_info in info:
            self.fill_rect(size*px_info[0] - (size-1)*x , size*px_info[1] - (size-1)*y, size, size, px_info[2]) 

拡大表示できた。

プッシュライト型の押しボタン式にする

材料の説明にもあったように、もとはプッシュライトなので一回押したらONのままもう一度押すとOFFという挙動をする(あたりまえ)
今回の用途としては押してるあいだだけONになってほしいので、いったんばらしてプッシュスイッチに取り換える。
ちょっと不格好だけどもともとある壁掛け用の穴から配線を引き出してこれをAボタンのピンとグランドにつなぐ(プルアップしているので)

回路図としてはこんな感じ

単純な音声を鳴らす

最初はビープ音を音階で鳴らしたりしてまずは音がPWMで聞こえるかどうか確認。

このように接続してPWM出力で単純な波形の音声が鳴ることが確認できた。

PCMの音声を鳴らす

抽選されたお年玉金額を読み上げたいのでPCMで音声を鳴らしたい。
今回はPicoAudioPWMというライブラリを使用して音声を鳴らす。
単純にならすだけれあればサンプリング周波数8kHz 16bitのwavをPicoに転送したうえで次のように書くだけで鳴るのはありがたい。

    from wavePlayer import wavePlayer

    player = wavePlayer(Pin(SPK_L), Pin(SPK_R), Pin(SPK_VGND))
    player.play('/sounds/sample.wav')
    player.stop()

が、しかしメインスレッドで鳴らしてしまうと音声再生中はグラフィックの書き換えができない。
そこで_threadモジュールをつかって別スレッドで再生するようにした。
ところどころprintしているのはデバッグ用に正しくwavファイルが読めているか確認のためのデバッグ出力。

from wavePlayer import wavePlayer
import wave
import _thread

led_onboard = Pin(25, Pin.OUT) # sound indicator
player = wavePlayer(Pin(SPK_L), Pin(SPK_R), Pin(SPK_VGND))
lock = _thread.allocate_lock()

def play_wav(filename):
    print("play_wav "+filename)
    f = wave.open(filename,'rb')
    print("{0:<45}".format('File Path'), 'Frame Rate  Width Chans Frames')
    print("{0:<50}".format(filename), "{0:>5}".format(f.getframerate()), "{0:>5}".format(f.getsampwidth()), "{0:>6}".format(f.getnchannels()), "{0:>6}".format(f.getnframes()) )
    lockP = lock.acquire(1, 2.0)
    if not lockP:
        print("_play_wav can not get the lock. "+filename)
        return

    _thread.start_new_thread(_play_wav, (filename,))

def _play_wav(filename):
    time.sleep(0.2)
    led_onboard.value(1)
    player.play(filename)
    player.stop()
    led_onboard.value(0)
    time.sleep(0.2)
    lock.release()

D級アンプをかませる

このままだとかなり音量が小さいので秋月電子の「TPA2006使用 超小型D級アンプキット」を使用して音声信号を増幅する

ローパスフィルタをかませる

そのまま増幅するとノイズ(特に高周波)がやばいのでローパスフィルタをwavePlayerのライブラリのコメントにあるとおりにつくる

             2K
    PIO2   -/\/\/-----+-----    headphone left
                      |
                     === 0.1uF
                      |
    PIO4   -----------+-----    headphone ground
                      |
                     === 0.1uF
              2k      |
    PIO3   -/\/\/-----+-----    headphone right

音声素材をつくる

自分の声で「0~9、十、百、千」と収録して単語ごとにwavファイルにする。もちろんそのままだとおっさんの声でよみあげられてしまうので当然ボイチェンにかける。はづかしいけどややかわゆくしゃべるのがコツ。
これを最終的にコンプレッサーをかけてからノーマライズしてボリューム調整したものを8kHz 16bit monoで出力。

Audacityの場合の設定例

できたファイルをThornny上で右クリックしてPicoにアップロード

3.5mmヘッドフォンコネクタをつける

100円ショップのUSB Type C -> ヘッドフォンマイクコネクタ変換ケーブルをばらす

左からR,L,MIC,AC(GND)が見える

今回はモノラル出力なのでRとLをショートさせアンプからのOUT+と接続、OUT-をAC(GND)と接続する。MICについてはどこにも接続しないままにしておく。

右下がアンプ回路。このあたりから部屋の荒れ具合がやばくなる。

ケースに入れる

100円ショップで買ったデジタル置時計を分解して中のパーツをとりだしてケーブルなどを通す穴をあける

電池ボックスも不要なので削ってスペースを確保する

正面もウレタンで覆って完成

動作の様子

https://youtube.com/shorts/Ybx9tYJs2fo?feature=share

ご注意

既製品を改造するときは十分注意して自己責任でお願いします。

参考

https://github.com/danjperron/PicoAudioPWM/tree/main

https://docs.micropython.org/en/latest/library/_thread.html#module-_thread

https://www.waveshare.com/wiki/Pico-LCD-1.14

http://www.yas-s.com/index.php?key=jo3nzeaja-51

https://serverarekore.blogspot.com/2021/07/raspberry-pi-picotpa2006dwav.html

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です