中央の NeoPixel Ring および LED テープ用のスケッチ(プログラムソースコード)
次に、中央のイルミネーションオブジェ、ESP32-DevKitC と NeoPixel ( WS2812B )のスケッチを紹介します。
基本的に、前回の記事とほぼ同じなのですが、LED テープの中央部分の 12×4 pixel だけを使う所が異なります。
意外と小難しくなりますね。
16行目で Universe を1としているところに注意してください。
あとは解説を省略させていただきます。
【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)
#include <WiFi.h> #include <WiFiUdp.h> #include <ArtnetWifi.h> #include <FastLED.h> const char* ssid = "xxxxxxxx"; //ご自分のルーターのSSIDに書き換えてください const char* password = "xxxxxxxx"; //ご自分のルーターのパスワードに書き換えてください const int numLeds = 144 + 12; // CHANGE FOR YOUR SETUP const int numberOfChannels = numLeds * 3; // Total number of channels you want to receive (1 led = 3 channels) const byte dataPin = 21; //ESP32 GPIO pin CRGB leds[numLeds]; ArtnetWifi artnet; const int max_dmx_ch = (48 + 12) * 3; uint8_t dmx[max_dmx_ch] = {}; //DMX 1univers : 512ch const uint8_t device_universe = 1; //************************************************* void setup(){ Serial.begin(115200); ConnectWifi(); artnet.begin(); artnet.setArtDmxCallback(onDmxFrame); FastLED.addLeds<WS2812B, dataPin, GRB>(leds, numLeds); FastLED.setBrightness(20); //※これ以上大きくすると、回路電流が大きすぎて危険注意。(Max 255) TaskHandle_t th; //マルチタスクハンドル定義 xTaskCreatePinnedToCore(Task1, "Task1", 8192, NULL, 5, &th, 0); //マルチタスク起動 } //************************************************* void loop(){ int i; int ch = 0; //DMX channel number for(i = 0; i < numLeds; i++){ if(i >= 0 && i < 12){ leds[11-i] = CRGB(dmx[ch], dmx[ch + 1], dmx[ch + 2]); ch = ch + 3; }else if(i >= 24 && i < 36){ leds[i] = CRGB(dmx[ch], dmx[ch + 1], dmx[ch + 2]); ch = ch + 3; }else if(i >= 60 && i < 72){ leds[i] = CRGB(dmx[ch], dmx[ch + 1], dmx[ch + 2]); ch = ch + 3; }else if(i >= 96 && i < 108){ leds[i] = CRGB(dmx[ch], dmx[ch + 1], dmx[ch + 2]); ch = ch + 3; }else if(i >= 132 && i < 144){ leds[i] = CRGB(dmx[ch], dmx[ch + 1], dmx[ch + 2]); ch = ch + 3; }else{ leds[i] = CRGB(0, 0, 0); } } FastLED.show(); delay(1); //これ重要!これが無いと点灯しない。 } //************ マルチタスクループ ****************** void Task1( void *pvParameters ){ while(1){ artnet.read(); delay(1); //マルチタスクの場合、これ絶対必要! } } //*************************************** boolean ConnectWifi(void){ boolean state = true; int i = 0; WiFi.begin(ssid, password); Serial.println(""); Serial.println("Connecting to WiFi"); // Wait for connection Serial.print("Connecting"); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); if (i > 20){ state = false; break; } i++; } if (state){ Serial.println(""); Serial.print("Connected to "); Serial.println(ssid); Serial.print("IP address: "); Serial.println(WiFi.localIP()); } else { Serial.println(""); Serial.println("Connection failed."); } return state; } //************************************************** void onDmxFrame(uint16_t universe, uint16_t length, uint8_t sequence, uint8_t* data){ int ch; //DMX channel number if(universe == device_universe){ for (ch = 0; ch < length; ch++){ dmx[ch] = data[ch]; ch++; dmx[ch] = data[ch]; ch++; dmx[ch] = data[ch]; } } }
右側 M5Stack 用のスケッチ(プログラムソースコード)
次に、右側の M5Stack のスケッチです。
先に紹介した左側と殆ど同じですが、18行目で Universe を2番としています。
そして、36行目で、LCD表示を上下逆転させています。
これは、先に述べたように、USBケーブル給電をし易くするためです。
あとは、41-50行で平仮名にしているだけです。
【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)
#include <WiFi.h> #include <WiFiUdp.h> #include <ArtnetWifi.h> #define MGO_TEC_BV1_M5STACK_SD_SKETCH #include <mgo_tec_bv1_m5stack_sd_simple1.h> //ESP32_mgo_tec library beta ver 1.0.67 const char* ssid = "xxxxxxxx"; //ご自分のルーターのSSIDに書き換えてください const char* password = "xxxxxxxx"; //ご自分のルーターのパスワードに書き換えてください const char* utf8sjis_file = "/font/Utf8Sjis.tbl"; //UTF8 Shift_JIS 変換テーブルファイル名を記載しておく const char* shino_full_font_file = "/font/shnmk16.bdf"; //オリジナル東雲全角フォントファイル const char* shino_half_font_file = "/font/shnm8x16.bdf"; //半角フォントファイル名を定義 ArtnetWifi artnet; const uint16_t max_pixels = 40; //文字数 const uint16_t max_dmx_ch = max_pixels * 3; const uint8_t device_universe = 2; uint8_t dmx[max_dmx_ch] = {}; uint8_t lcd_px[max_dmx_ch] = {}; uint16_t c0, c1, c2; String str[max_pixels][8]; uint16_t font_width; uint16_t font_height; double str_per_dmx = (double)7.0 / 255.0; //************************************************* void setup(){ Serial.begin(115200); ConnectWifi(); artnet.begin(); artnet.setArtDmxCallback(onDmxFrame); mM5.init( utf8sjis_file, shino_half_font_file, shino_full_font_file ); LCD.brightness(255); //LCD LED Full brightness LCD.dispRotation(2); //上下逆表示 mM5.font[0].Xsize = 2, mM5.font[0].Ysize = 3; font_width = mM5.font[0].Xsize * 16 + 8; font_height = mM5.font[0].Ysize * 16; for(int i = 0; i < max_pixels; i++){ str[i][0] = "め"; str[i][1] = "り"; str[i][2] = "ー"; str[i][3] = "く"; str[i][4] = "り"; str[i][5] = "す"; str[i][6] = "ま"; str[i][7] = "す"; } TaskHandle_t th; //マルチタスクハンドル定義 xTaskCreatePinnedToCore(Task1, "Task1", 8192, NULL, 10, &th, 0); //マルチタスク起動 } //************************************************* void loop(){ int i; int ch = 0; //DMX channel number uint16_t x0 = 0, y0 = 0; int str_num = 0; for(i = 0; i < max_pixels; i++){ c0 = ch++, c1 = ch++, c2 = ch++; if((dmx[c0] != lcd_px[c0]) || (dmx[c1] != lcd_px[c1]) || (dmx[c2] != lcd_px[c2])) { lcd_px[c0] = dmx[c0]; lcd_px[c1] = dmx[c1]; lcd_px[c2] = dmx[c2]; x0 = i * font_width - 320 * (uint16_t)floor((double)i / 8.0); y0 = font_height * (uint16_t)floor((double)i / 8.0); str_num = (uint8_t)round( (double)lcd_px[c0] * str_per_dmx ); mM5.font[0].x0 = x0; mM5.font[0].y0 = y0; mM5.font[0].colorRGB255( lcd_px[c0], lcd_px[c1], lcd_px[c2] ); mM5.disp_fnt[0].dispText( mM5.font[0], str[i][str_num] ); } } } //************ マルチタスクループ ****************** void Task1( void *pvParameters ){ while(1){ artnet.read(); delay(1); //マルチタスクの場合、これ絶対必要! } } //*************************************** boolean ConnectWifi(void){ boolean state = true; int i = 0; WiFi.begin(ssid, password); Serial.println(""); Serial.println("Connecting to WiFi"); // Wait for connection Serial.print("Connecting"); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); if (i > 20){ state = false; break; } i++; } if (state){ Serial.println(""); Serial.print("Connected to "); Serial.println(ssid); Serial.print("IP address: "); Serial.println(WiFi.localIP()); } else { Serial.println(""); Serial.println("Connection failed."); } return state; } //************************************************** void onDmxFrame(uint16_t universe, uint16_t length, uint8_t sequence, uint8_t* data){ int ch; //DMX channel number if(universe == device_universe){ for (ch = 0; ch < length; ch++){ dmx[ch] = data[ch]; ch++; dmx[ch] = data[ch]; ch++; dmx[ch] = data[ch]; } } }
コンパイル書き込み実行
では、2台の M5Stack と ESP32-DevKitC をコンパイル書き込み実行させてみてください。
その時、Arduino core for the ESP32 の「Core Debug Level」は「なし」にしておいてください。
そして、Wi-Fiルーターを起動して、ESP32 を接続しておきます。
Jinx ソフトウェアの DMX Patch ( パッチ )
では、Jinx の Art-Net DMX Patch の割り当てがちょっと特殊です。
Patch 方法については、こちらの記事も合わせてご参照ください。
下の図を見てください。
M5Stack と連動させるために、中央のLEDテープ4列の上に NeoPixel Ring 12連を Patch ( パッチ )しました。
こうすると、Jinx ソフトウェアとの相性が良くなり、全体を一画面としてコントロールできます。
光ファイバーを使わず、LED テープを5列にしても良いですし、M5Stack 側を4列にしても良いですし、これは自由に設定してみてください。
Jinx 上の Patch ( パッチ )は以下のような感じです。
まず、Jinx の Matrix Options は以下のように、28×5 pixel とします。
次に、「Output Devices」設定で「Add」をクリックして、以下のように3つのUniverseを作ります。
Universe #1 : DMX 120ch
Universe #2 : DMX 180ch
Universe #3 : DMX 120ch
そうすると、「Output Devices」画面は以下のようになります。
次に、「Output Patch」設定では、まず、左側の M5Stack 用のパッチをします。
以下のように「Fast Patch」をクリックして、Universe #0 で設定します。
「OK」すると、以下のようになります。
同じように、今度は中央の NeoPixel Ring と LED テープのパッチをします。
ここの Patch Area は 12:5 であることに注意してください。
下図の様に Universe #1 で設定します。
すると、下図のようになります。
次に同じように右側 M5Stack 用のパッチを下図のようにします。
すると下図のようになり、全てパッチ完了です。
Jinx ソフトウェア実行
では、Jinx ソフトウェアで、Start Output させてみてください。
最初に紹介した動画のように表示されればOKです。
シーンを記憶して、Chase で各シーンを自動ループ再生すれば、立派なイルミネーションになると思います。
ただ、テキストスクロールについては、LED テープの列がもっと多くないと読み取りは厳しいですね。
ところで、以前、Twitter でツイートしましたが、M5Stack 1台を 40×30 pixel として、2台を横に置いて、80×30 pixel として Jinx で動かしたこともありました。
でも、これってあまり利用する場面が思いつかず、ブログ記事にはしませんでした。
要するに、Art-Net DMX を使えば、いろんな制御ができるということです。
TouchDesigner などを使えばもっといろんなことができそうですね。
編集後記
どうでしょうか。
今回はパソコンで3台の ESP32 を制御して、一つの画面として卓上イルミネーションを作ってみました。
やっぱりスタンドアローンでパソコンなしで動作させるようにしてみたいですね。
いつかチャレンジしてみたいとは頭の隅で思っています。
ということで、今年の私の小さいクリスマスイルミネーション制作はこれで完結とします。
さぁ、そろそろ大掃除しなきゃ。
ではまた・・・。
コメント