はじめに
ゲームを作りたいのに億劫になってしまう、そんなストレスを抱えている皆さん、こんにちは。私も同じ悩みを抱える一人です。先月からハードウェアを勉強し直す必要に迫られ、年末のゲーム制作を断念しました。
そんなわけで(?)、レトロゲームエンジン Pyxel と マイコンボード Arduino UNO R3 を連携させた 「7セグLEDアニメーションエディタ」 を作りました。
遊び心を満たすため、 仮面ライダー555 アクセルフォーム のガジェットを再現できるツールを目指しました(ファイズはカッコいいんですよ
)
この記事では、開発中に直面した「Pythonの落とし穴」と「シリアル通信の難所」について共有したいと思います。
作ったもの
システム構成
PC画面のエディタでLEDの点灯パターンを作成し、USB経由でArduinoにデータを送信。Arduinoがそのデータに基づいてLEDをダイナミック点灯させます。
- PC側: Python 3.x + Pyxel (アニメーションエディタ)
- 通信: シリアル通信 (USB, 115200bps)
- マイコン側: Arduino UNO R3
- 表示器: 7セグメントLED(1桁 × 2個)、ダイナミック点灯方式
こだわりポイント:「チラつき」の制御
このエディタは、ストップウォッチのカウント機能のような応用的な機能は設けず、ユーザーが指定した表示パターンを、指定した時間だけ点灯することに特化させました。
低機能だからこそ、アニメーションの演出機能にこだわりました。「エネルギーが充填されるような不安定な発光」を表現するため、データ構造に flicker_delay を持たせています。
// Arduino側のダイナミック点灯処理
delay(REFRESH_MS + flickerDelays[activeAnimeIndex]);
ダイナミック点灯の基本周期 (REFRESH_MS) に、アニメーションフレームごとの遅延時間 (flickerDelays) をあえて加算することで、ノイズ感や不安定さを演出しています。
上の映像だとイマイチですが、実物は良い感じでチラつきを演出できてます!
開発ログ①:ハードウェア制作「1/2の敗北」
失敗談①:アノード?カソード?
久しぶりの電子工作。まずは回路設計と配線です。
2桁の7セグメントLED表示器が手元になかったため、1桁品を2個密着させて配置することにしました。意気揚々と配線し、いざ通電!……光りません。
原因:アノードコモンとカソードコモンの勘違い
データシートを見ながら配線したつもりでしたが、手元の部品は逆のタイプでした。「まあこっちだろう」という1/2の賭けを外したわけです。久々とはいえ雑過ぎましたね![]()
コラム:手を動かすことの効用
久しぶりにワイヤーストリッパーやラジオペンチを使い、老眼なりかけの目で凝視しながら手を動かしました。配線レイアウトは定まっているものの手が高速に動くことはなく、コードを書くようにササッとは済みません。しかし、このチマチマした作業が行き詰まった気分を晴らしてくれたように思います。ここ数ヶ月、PCでの読み書きやAI主体でコードを作らせるようなことしかしてなかったので、余計そのように感じたのかもしれません。
開発上の工夫:ポートの直接制御(高速化)
コード面では、1本のピン毎に信号制御する標準の digitalWrite() ではなく、ポート直接操作(PORTD, PORTBへのビット演算)を採用しました。
// D2〜D7 を出力
DDRD |= B11111100;
// D8〜D11 を出力
DDRB |= B00001111;
これは、高速にデータを受信・更新する中で、ダイナミック点灯の処理を可能な限り速く、シンプルにするための工夫です。
なお、 D0/D1 ピン(ハードウェアシリアルの RX/TX) は Arduino の USB シリアル通信で主要な役割を持つため、今回の回路では使用を避けています。USB経由でパソコンと通信する際、背後でこの D0/D1 のUARTとUSBシリアル変換が使われています。
以下のサイトでとても丁寧に紹介されています
Arduinoのスケッチ書き込みが上手くいかない | ぶらり@web走り書き 様
開発ログ②:Pyxelでのエディタ実装と「Pythonの罠」
UI実装:自作ウィジェット
Pyxel側では、マウス操作でLEDセグメントをON/OFFできる直感的なUIを作りました。
また、Pyxelには標準のテキストボックス等のGUIウィジェットがないため、TextBox クラスも自作しています。カーソルやバリデーションはありませんが、使用者(私)が気をつければ大丈夫、という割り切りで進めました。
どハマりした「Pythonの罠」:参照渡しとスコープ
どちらもPythonの学習初期で遭遇する典型的なミスです。いまだに罠にハマります。
1. リスト参照渡しの罠( .copy() 忘れ )
開発中、エディタ編集結果がArduinoへ正しく送信できないバグに遭遇しました。
実装過程において、1フレーム分だけ送信するには問題なかったのに、本格的にアニメに必要な複数フレームを送信する場面で問題が起きました。
1フレーム分の更新情報を ptrn_vals で束ねて patternList に反映するコードを書いているつもりなのに、なぜか反映されない。
修正前のコード(イメージ)
patternList[i] = ptrn_vals # リストをそのまま代入
値コピーをしていないことが原因だと気づいたものの、書き方を忘れたのでAIに確認。半年前に同じことをやらかしたのに、すぐに忘れてしまいます。
修正後のコード
patternList[i] = ptrn_vals.copy()
2. global宣言漏れの罠
同じレベルのハマりで、global宣言漏れがありました。クラス外で定義した配列データを、forループ内部で更新しているはずが、ループを抜けた途端にループ内で実施したことがチャラになっていたのです。
技術的な山場:シリアル通信のフリーズ問題
現象
最大の壁は、Arduinoへのデータ転送時に発生しました。
送信するアニメーションパターンが少ないうちは動いていたのですが、データ量を増やした途端、以下の症状が発生しました。
- Arduino側でのLED表示がバグる(謎のパターンが光ったまま、アニメしない)
- Arduinoがフリーズし、Pyxel側との通信が途絶える
ハンドシェイクの導入
「通信速度が速すぎるのか?」と思い 115200bpsから9600bpsに落としてみました。動作は安定しましたが、これはあくまで一時凌ぎです。将来、大きなデータを高速で送信したい時のために、原因究明することにしました。
ChatGPTに相談したところ、いくつかの可能性を提示され、その時の状況に関係しそうな ハンドシェイク(フロー制御) を導入することにしました。
Arduino UNO R3の受信バッファ(64バイト)がPC側からの大量データに溢れ、フリーズしていた、というのが原因です。
解決策:データを受け取ったら「R」を返す
Arduino側: データの受信処理後、受信完了通知として 'R' を返す。
// データの受信処理後...
Serial.write("R"); // ハンドシェイク:受信完了通知`
Python (Pyxel)側: データ送信後、Arduinoからの 'R' が来るまで待機する。
# データを送信後...
while ser.read() != b'R': # Arduinoからの完了通知を待つ
# 処理
たったこれだけの処理で、元のスピードである 115200bpsでも完全に安定してデータ転送ができるようになりました。なお、今回の通信内容は、Pyxelの1フレームに対し極短時間で済むデータ転送であるため、気配りする範囲が狭くても問題が出ないだけと感じています。もっと容量が大きく、速いデータ転送が必要な場合は、データを小刻みに分割して送るなどの複雑なフロー制御を考えることになりそうです。
コラム:AI時代における「基本技術」の身につけ方
今回の「フロー制御」はQiitaで検索すれば多くの記事がヒットするほど、通信において基本事項に入ります。今回、AIに通信のひな形コードを用意してもらう時に 『必要最低限』 を強調しました。その指示がなければフロー制御は最初から入っていたでしょう。
私の今の時点の考えですが、初心者が要素技術を身につけることを目的にAIを使って開発する時は、以下のようなプロンプトをおすすめします。
「____をするための必要最低限のひな形コードを提示してください」
わからない範囲を極力小さくして、理解度を上げてから次のステップに進む。範囲を狭めた状態であれば、理解できない状況をAIに尋ねた時、自分のレベルに ビタッとハマる指摘 をもらえる可能性が高まると考えています。
今回は、AIに「通信」の基本コードを依頼したため、通信の「難所」が浮き彫りになり、結果的にハンドシェイク(フロー制御)という基本技術を深く理解する機会となりました。
まとめ:Pyxel × ハードウェア連携の可能性
構想から完成まで6日間。 Pyxelを物理デバイス用のUIとして使う ことにチャレンジし、成果を出せて嬉しかったです。
Pyxelは「 低解像度でレトロな表現が容易 」「Pythonによるシリアル通信などの外部連携が簡単 」という特性から、今回のような レトロガジェットの開発ツール や、ArduinoやRaspberry Piを使った 物理世界とつながったゲームのフロントエンド として可能性を感じています。
手元の物体が自分のプログラム通りに光って動くという体験はワクワクします。皆さんも、Pyxelの描画機能を 物理世界 に拡張してみてはいかがでしょうか。
ソースコード

