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

記事公開日:2018年12月21日

スポンサーリンク

中央の NeoPixel Ring および LED テープ用のスケッチ(プログラムソースコード)

次に、中央のイルミネーションオブジェ、ESP32-DevKitC と NeoPixel ( WS2812B )のスケッチを紹介します。

基本的に、前回の記事とほぼ同じなのですが、LED テープの中央部分の 12×4 pixel だけを使う所が異なります。
意外と小難しくなりますね。

16行目で Universe を1としているところに注意してください。

あとは解説を省略させていただきます。

#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行で平仮名にしているだけです。

#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 ( パッチ )しました。

M5Stack2_ESP32_neopixel10

 

こうすると、Jinx ソフトウェアとの相性が良くなり、全体を一画面としてコントロールできます。
光ファイバーを使わず、LED テープを5列にしても良いですし、M5Stack 側を4列にしても良いですし、これは自由に設定してみてください。

Jinx 上の Patch ( パッチ )は以下のような感じです。

M5Stack2_ESP32_neopixel11

まず、Jinx の Matrix Options は以下のように、28×5 pixel とします。

M5Stack2_ESP32_neopixel12

次に、「Output Devices」設定で「Add」をクリックして、以下のように3つのUniverseを作ります。

Universe #1 : DMX 120ch
Universe #2 : DMX 180ch
Universe #3 : DMX 120ch

M5Stack2_ESP32_neopixel13

そうすると、「Output Devices」画面は以下のようになります。

M5Stack2_ESP32_neopixel14

次に、「Output Patch」設定では、まず、左側の M5Stack 用のパッチをします。
以下のように「Fast Patch」をクリックして、Universe #0 で設定します。

M5Stack2_ESP32_neopixel15

「OK」すると、以下のようになります。

M5Stack2_ESP32_neopixel16

同じように、今度は中央の NeoPixel Ring と LED テープのパッチをします。
ここの Patch Area は 12:5 であることに注意してください。
下図の様に Universe #1 で設定します。

M5Stack2_ESP32_neopixel17

すると、下図のようになります。

M5Stack2_ESP32_neopixel18

次に同じように右側 M5Stack 用のパッチを下図のようにします。

M5Stack2_ESP32_neopixel19

すると下図のようになり、全てパッチ完了です。

M5Stack2_ESP32_neopixel20

Jinx ソフトウェア実行

では、Jinx ソフトウェアで、Start Output させてみてください。
最初に紹介した動画のように表示されればOKです。

シーンを記憶して、Chase で各シーンを自動ループ再生すれば、立派なイルミネーションになると思います。

ただ、テキストスクロールについては、LED テープの列がもっと多くないと読み取りは厳しいですね。

ところで、以前、Twitter でツイートしましたが、M5Stack 1台を 40×30 pixel として、2台を横に置いて、80×30 pixel として Jinx で動かしたこともありました。

でも、これってあまり利用する場面が思いつかず、ブログ記事にはしませんでした。
要するに、Art-Net DMX を使えば、いろんな制御ができるということです。
TouchDesigner などを使えばもっといろんなことができそうですね。

編集後記

どうでしょうか。
今回はパソコンで3台の ESP32 を制御して、一つの画面として卓上イルミネーションを作ってみました。
やっぱりスタンドアローンでパソコンなしで動作させるようにしてみたいですね。
いつかチャレンジしてみたいとは頭の隅で思っています。

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

ではまた・・・。

このブログの維持運営にご支援いただけると助かります。
支援方法はこちらの記事をご覧ください。
(管理人:mgo-tec)

 

スポンサーリンク

mgo-tec電子工作 関連コンテンツ ( 広告含む )

 

mgo-tec電子工作ブログ管理人おすすめ

投稿者:

mgo-tec

ESP32 ( ESP-WROOM-32 ) , M5Stack , ESP8266 ( ESP-WROOM-02 ) , Arduino 等を使って、主にスマホと連携した電子工作やプログラミング記事を書いてます。ライブラリも作ったりしてます。趣味、独学でやっているアマチュア中高年です。 ここで紹介していることは動作保証し兼ねます。 電子回路やプログラミングの専門家ではありません。 畑違いの仕事をしていますが、少しだけ電気の知識が必要な仕事なので、電気工事士や工事担任者等の資格は持っています。

コメントを残す

メールアドレスが公開されることはありません。

*画像の文字を入力してください。(スパム防止の為)

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください