マザーボードのARGB LED(アドレサブルRGB LED)の信号をワイヤレスで伝送して、離れた場所のLEDを光らせるデバイスを作ってみました。これを使えばケーブルを引っ張ってこなくても、家中ゲーミング仕様にすることができます。

システム構成

マザーボードのARGB LED信号をデコードして色情報に変換し、WiFi (ESP-NOW)で送信する送信機と、WiFiから受け取った色情報をLEDに表示する受信機で構成されています。

flowchart LR
  マザーボード ---> 送信機 --Wi-Fi(ESP-NOW)--> 受信機 ---> LED

ESP-NOWはWiFiのアクセスポイント等を使わずに、直接デバイス同士で通信ができる仕組みです。SSIDなどの設定が不要で、簡単にデータ通信ができます。

今回や送信側の親機をM5Stack AtomS3 Lite、子機をM5Stack NanoC6を使いました。

ARGB LED信号とは?

マザーボードには謎の3ピン端子があります。左から5V, DATA, GNDで、データはシリアル方式で送信されています。LED 1つにつきG,R,B各色8ビットずつ計24ビットで構成されます。たとえばLED 100個分のデータを送る場合は、100個分のデータが数珠つなぎになって送信されます。

信号は800KHzの周波数で送信され、パルスの幅によって値が1か0かを区別します。

T0Hが220ns~380nsなら値は0、T1Hが580ns~1usなら値は1となります。

データ信号はこのように、DIに入ってDOから出ていき、次のLEDのDIへと向かいます。このとき1個目のLEDは自分の分のデータを取り除いてから次へと送ります。簡単な回路なのに1本の信号線だけで各LEDの色をコントロールできるんですね。

M5Stackでデコードする

仕組みがわかれば、これをデコードすればいいだけです。M5Stack (ESP32-S3)でこの信号を解析する方法はいくつかあります。GPIOの割り込みを使うのが手っ取り早いのですが、周波数が800KHzと高速で、380nsと580nsのパルス幅の違いを判定させるのは厳しいと思います。

そこで今回はRMT (Remote Control Transceiver) を使うことにします。元々はリモコンの赤外線信号のデコードに使うものだそうです。これを使えばパルスのHighの幅やLowの幅を測定して記録することができます。今回は10MHz(最小単位100ns)に設定しました。100nsあればギリギリ220ns~380nsと580ns~1usの違いを区別できます。

また入力データ1ビットあたり、1バイトのバッファーを必要とします。今回は5120バイトのメモリを用意しました。LED 1個あたり24ビットの情報があるので、バッファーは24バイト必要。5120 / 24 で 約213個までのLEDなら受信ができます。実際にマザーボードのデータをデコードしたところ、120個分のLED情報が送信されていました。このへんはきっとこれはメーカーによってバラバラでしょう。

ESP-NOWで送信する

ESP-NOWはWiFiルーターを経由せずに、デバイス同士で直接通信できる仕組みです。SSID等の設定も不要なので手軽にデータのやりとりができます。そのかわり送信できるデータ量は少なく、250バイトもしくは1470バイトです。Arduino IDEのボードマネージャーでesp32をver.3以上に更新していれば1470バイトまで扱えるはずです。

送信する際は相手のMACアドレスを指定するか、ブロードキャストアドレスで全てに送信する方法がありますが、今回はMACアドレスを指定することにしました。他にもESPデバイスがある環境の場合、ブロードキャストすると関係のないデバイスも受信してしまいます。

ESP-NOWでは以下のような構造のデータを送信しています。

#define ESP_LED_MAX 150   // ESP-NOWで送信するLEDの最大数
struct RGB_Color {
  uint8_t r;
  uint8_t g;
  uint8_t b;
};
struct EspnowData {
  uint16_t eid;
  uint8_t count;
  uint32_t serial;
  RGB_Color data[ESP_LED_MAX];
};

eidは識別用のIDで値は適当なものです。countはLEDの個数、serialは送信する度にカウントされるシリアル値です。dataはr,g,bの色情報が150個分あり、実際のLEDの個数の方が多ければ切り捨てられ、少なければ残りはゼロで埋められます。

プログラム

今回作成したプログラムは GitHub にアップしました。

接続方法

ARGB LEDの3ピンコネクタはM5StackのGroveポートに接続します。

ところで、この丸ピンヘッダーをデファクトスタンダード化したやつ誰ですか?抜けやすいし挿すときも引っかかってなかなか入らないし、誰かちゃんと規格化しようとはしなかったんだろうか…(自作PC業界、そういうとこだぞ)。

親機(送信機)

マザーボード側の出力TTLレベル(5V)なので、直接M5Stackに入力することはできません。今回は抵抗を分圧してレベル変換しました。M5Stackの電源はGroveポートの5Vから取ってるので、USBを同時に繋げないようにします。

flowchart LR
  subgraph マザーボード側
    direction TB
    LED_5V
    LED_DATA
    LED_GND
  end
  subgraph M5Stack側
    direction TB
    M5Stack_5V
    M5Stack_GPIO_2
    M5Stack_GND
  end

  LED_DATA --- 抵抗5.6KΩ --- P1(( ))
  P1 --- 抵抗10KΩ --- P2
  P1 --- M5Stack_GPIO_2

  LED_GND --- P2(( )) --- M5Stack_GND

  LED_5V --- M5Stack_5V

Mermaid記法難しい…。マザーボード側のDATAとGND間を抵抗で分圧して、中間のところからM5StackのGPIOに入力しています。

基板を使うまでもないのでケーブル内で空中配線することにしました。でも結構手間がかかって、小さくカットしたユニバーサル基板を使った方がよかったです。

子機(受信機)

子機側はそのままつないだだけです。

flowchart LR
  subgraph LED側
    direction TB
    LED_5V
    LED_DATA
    LED_GND
  end
  subgraph M5Stack側
    direction TB
    M5Stack_5V
    M5Stack_GPIO_1
    M5Stack_GND
  end

  LED_5V --- M5Stack_5V
  LED_DATA --- M5Stack_GPIO_1
  LED_GND --- M5Stack_GND

電源はUSBポートから取ります。

ハマったこと、工夫したこと

デコード結果が安定しない

ちゃんとデータがデコードできるときと、できないときがありました。カラーが滅茶苦茶、全部黒データ(黒=0x000000)、途中から黒データ、LEDの個数が足らない、などなど。これが結構な頻度で起こるので、これでLEDを光らせたら気が狂うでしょう。オシロスコープで波形を見ると波形がなまっていたので、これが原因かと思ってバッファー回路 TC74HC4050APを入れてみたものの効果なし。

そこで仕方ないのでキャリブレーション機能を実装しました。仕組みは100回データを受信した結果、検出されたLEDの個数が最も多く検出された回数、を正しいデータとして採用するというものです。

・LED 120個の出現回数 80回 ← これが一番多いから多分正しい
・LED 35個の出現回数 5回
・LED 110個の出現回数 15回

これで異常なデータを防げるようになりました。しかし連続90回くらい異常なデータが続くこともあり、一瞬LEDの動きが止まるという現象が防げませんでした。

自動再キャリブレーション

ソフトウェアでライティングの制御をしている場合、発光パターンが変わればLEDの個数も変わるかもしれません。LEDの個数が変わればデコードエラーとして扱われるので、デコードエラーが一定数を超えたら再度キャリブレーションを行うことにしました。

一時停止機能

ボタンを押すと一時停止します。

内蔵LEDでプレビュー、LCDでプレビュー

内蔵LEDが搭載されているモデルならこのLEDも光るので、動作チェックに役立ちます。またLCDが搭載されているモデルなら、LCDにLEDの発光パターンが表示されます。

明るさ変更機能

受信側でボタンを長押しすると、プログラムで設定したデフォルト値ではなく、5段階に明るさを変更できます。

自動消灯機能

RGB LEDは最後に受け取ったデータを保持し続けるため、親機側の電源を切っても子機側は光り続けてしまいます。そこで親機からのデータが途切れて一定期間経つと、自動的にオフにするようにしました。新しいデータが来ればすぐに再開します。

完成!

ということで実際にマザーボードに接続してみました。

子機はモバイルバッテリーから給電してLEDテープライトを接続してみました。

なかなかシンプルな感じでいいですね。M5Stack NanoC6はコネクタとほぼ同じ幅の超小型デバイスで、発売当初は開発環境が整ってなくてずっと積んだままでした。今は普通にM5Unifiedを入れとけば、他のM5Stack製品と同じように簡単に使えるようになってました。

「M5Stackまた新製品出たよ。でも買っても使うかなぁ。まぁとりあえず2つくらいポチっておくか。」

そうやって積まれた基板もいつか使う日が来るかもしれないし、必要になったときにあると便利だから、とりあえず新製品が出たら買っちゃいましょう。

LINEで送る
Pocket