M5Stack 2台と ESP32光ファイバー LED テープオブジェを1つの画面として制御する実験

M5Stack

中央の 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 を制御して、一つの画面として卓上イルミネーションを作ってみました。
やっぱりスタンドアローンでパソコンなしで動作させるようにしてみたいですね。
いつかチャレンジしてみたいとは頭の隅で思っています。

ということで、今年の私の小さいクリスマスイルミネーション制作はこれで完結とします。
さぁ、そろそろ大掃除しなきゃ。

ではまた・・・。

 

コメント

タイトルとURLをコピーしました