5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

M5Core2 で ATEM スイッチャーを作ってみた (※M5Core2ライブラリ ver.0.0.6へ対応)

Last updated at Posted at 2021-01-17

はじめに

M5Core2 で ATEM Mini Pro を操作するスイッチャーを作ってみました。

操作している様子(動画)

https://youtu.be/5ASHgIHe9pQ

これまでは ATEM Mini Pro はボタン (入力ソース) が 4 つで、M5Stack はボタンが 3 つしかなかったので、用途が限定されていました (例えば、ボタンは一部の Program と Preview の切替のみ、液晶画面はタリーライトとして表示、など) が、M5Core2 は液晶画面がタッチパネルとなり、従来のボタンもタッチパネルの一部として認識されます。また、液晶も TFT から IPS へ変更されたため、視野角も広く表示もクリアです。

ATEM Mini Pro と M5Stack、M5Core2

ATEM Mini Extreme

スクリーンショット 2021-03-04 23.32.18.png
ATEM_Extreme_Switcher.png

ATEM スイッチャーの画面・ボタン配置

液晶画面には入力ソースのボタン (タリーライト) を表示し、タッチパネルで Program (上段) と Preview (下段) を切り替えられるようにしました。
また、従来のボタン (ボタンA と ボタンB) は Program と Preview の入れ替え (AUTO と CUT) に割り当てました。

準備

1. Arduino IDE

開発環境となる Arduino IDE をインストールします。

2. ボードマネジャー

M5 公式の手順通り、ボードマネジャーから M5stack を選択してインストールします。(2021年9月5日時点のバージョンは 1.0.9 でした)

3. M5Core2 ライブラリ

M5 公式の手順通り、ライブラリを管理から M5Core2 を選択してインストールします。(2021年9月5日時点のバージョンは 0.0.6 でした)

4. ATEM ライブラリ

ネットワーク経由で ATEM Mini Pro を操作する際に BMD (Black Magic Design) というプロトコルが使用されています。
http://skaarhoj.com/fileadmin/BMDPROTOCOL.html
SKAARHOJ さんが公開されている Arduino 用のライブラリを使用 (一部、コードを修正してインストール) します。
https://github.com/kasperskaarhoj/SKAARHOJ-Open-Engineering
必要なライブラリは以下の 4 つです。

  • ATEMbase
  • ATEMmax
  • ATEMstd
  • SkaarhojPgmspace

修正内容

修正箇所は ATEMbase ライブラリの 3 箇所です。

  • ATEMbase.cpp (1 箇所)
  • ATEMbase.h (2 箇所)

理由は Arduino Ethernet Shield と ESP8266 とで条件付きコンパイルされるためで、M5Core2 は ESP32 が使用されているため、修正が必要になります。

修正前
#ifdef ESP8266        
WiFiUDP Udp;
#else
EthernetUDP Udp;
#endif
修正後
WiFiUDP Udp;

修正済のソースコードを github に置きました。
https://github.com/kitazaki/M5Core2_atemswitcher/raw/main/ArduinoLibs.zip

コード

ソースコードを github に置きました。
https://github.com/kitazaki/M5Core2_atemswitcher

使用する環境に合わせて、Wi-Fi (SSIDとパスワード) と ATEM Mini Pro (IPアドレス) の設定をする必要があります。

IPAddress switcherIp(0, 0, 0, 0);
const char* ssid = "";  // Wi-Fi SSID
const char* password =  "";  // Wi-Fi Password

ATEM Mini / Pro / Pro ISO用 (M5Core2ライブラリ ver.0.0.2)

ソースコード
M5Core2_ATEM_01.ino
// This code is based on
// https://github.com/aaronpk/m5-core2-atem-controller/
//
// Requirements
//
// Download Arduino Libraries from here
// https://github.com/kasperskaarhoj/SKAARHOJ-Open-Engineering/tree/master/ArduinoLibs
//
// 1. copy these folders to libraries directory
// ATEMbase
// ATEMmax
// ATEMstd
// SkaarhojPgmspace
//
// 2. change code in ATEMbase.cpp (1 change) and ATEMbase.h (2 changes)
// refer to: https://oneguyoneblog.com/2020/07/13/autopilot-for-blackmagic-design-atem-switcher/
//
//  from                  to
//  --------------------  --------------------
//  #ifdef ESP8266        WiFiUDP Udp;
//  WiFiUDP Udp;
//  #else
//  EthernetUDP Udp;
//  #endif

#include <M5Core2.h>
#include <Fonts/EVA_20px.h>
#include <stdio.h>
#include <WiFi.h>

#include <SkaarhojPgmspace.h>
#include <ATEMmax.h>
#include <ATEMstd.h>

// Define the IP address of your ATEM switcher
IPAddress switcherIp(0, 0, 0, 0);

// Put your wifi SSID and password here
const char* ssid = "";  // Wi-Fi SSID
const char* password =  "";  // Wi-Fi Password

#define CAMERA_OFF 0
#define CAMERA_PREVIEW 1
#define CAMERA_PROGRAM 2

#define CAMERA_BUTTON_Y 42
#define MACRO_BUTTON_Y 150

ATEMstd AtemSwitcher;

TouchButton c1 = TouchButton(2,   CAMERA_BUTTON_Y, 72, 72, "c1");
TouchButton c2 = TouchButton(2+72, CAMERA_BUTTON_Y, 72, 72, "c2");
TouchButton c3 = TouchButton(2+72+72, CAMERA_BUTTON_Y, 72, 72, "c3");
TouchButton c4 = TouchButton(2+72+72+72,   CAMERA_BUTTON_Y, 72, 72, "c4");

TouchButton b1 = TouchButton(2,   MACRO_BUTTON_Y, 72, 72, "b1");
TouchButton b2 = TouchButton(2+72, MACRO_BUTTON_Y, 72, 72, "b2");
TouchButton b3 = TouchButton(2+72+72, MACRO_BUTTON_Y, 72, 72, "b3");
TouchButton b4 = TouchButton(2+72+72+72,   MACRO_BUTTON_Y, 72, 72, "b4");

void buttonWasPressed(TouchEvent& e) {
  TouchButton& b = *e.button;

  char buttonType = b.name[0];
  int buttonNumber = b.name[1]-'0';

  Serial.printf("debug: buttontype = %c, buttonNumber = %d\n", buttonType, buttonNumber);
  
  if(buttonType == 'c') {
    AtemSwitcher.changeProgramInput(buttonNumber);
  }
  else if( buttonType == 'b') {
    drawButton(buttonNumber, b.isPressed(), false);
//    AtemSwitcher.setMacroAction(buttonNumber-1, 0); 
    AtemSwitcher.changePreviewInput(buttonNumber);
  }
  else {
    if(M5.BtnA.isPressed()) {
      AtemSwitcher.doAuto();
      Serial.println("debug: Button A is Pressed");
    }
    else if(M5.BtnB.isPressed()) {
      AtemSwitcher.doCut();
      Serial.println("debug: Button B is Pressed");
    }
    else if(M5.BtnC.isPressed()) {
      // do something
      Serial.println("debug: Button C is Pressed");
    }
  }
}

void drawCameraButton(int buttonNumber, int state) {
  int font = 1;
  M5.Lcd.setTextSize(8);
  
  M5.Lcd.setTextColor(state == CAMERA_OFF ? WHITE : BLACK);

  int buttonColor;
  switch(state) {
    case CAMERA_OFF:
      buttonColor = TFT_DARKGREY; break;
    case CAMERA_PREVIEW:
      buttonColor = TFT_GREEN; break;
    case CAMERA_PROGRAM:
      buttonColor = TFT_RED; break;
  }

  //char* cameraName = AtemSwitcher.getInputShortName(buttonNumber-1);
  
  switch(buttonNumber) {
    case 1:
      M5.Lcd.fillRoundRect(2, CAMERA_BUTTON_Y, 72, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("1", 2+2+(72/2), 42+10, font);
      break;
    case 2:
      M5.Lcd.fillRoundRect(2+72+2, CAMERA_BUTTON_Y, 72, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("2", 2+2+72+2+(72/2), 42+10, font);
      break;
    case 3:
      M5.Lcd.fillRoundRect(2+72+2+72+2, CAMERA_BUTTON_Y, 72, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("3", 2+2+72+2+72+2+(72/2), 42+10, font);
      break;
    case 4:
      M5.Lcd.fillRoundRect(2+72+2+72+2+72+2, CAMERA_BUTTON_Y, 72, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("4", 2+2+72+2+72+2+72+2+(72/2), 42+10, font);
      break;
  }
}

void drawButton(int buttonNumber, bool isPressed, bool isRunning) {
  int font = 1;
  M5.Lcd.setTextSize(8);
  
  M5.Lcd.setTextColor(isPressed || isRunning ? BLACK : WHITE);

  int buttonColor = isPressed ? WHITE : (isRunning ? TFT_RED : TFT_DARKGREY);
  
  switch(buttonNumber) {
    case 1:
      M5.Lcd.fillRoundRect(2, MACRO_BUTTON_Y, 72, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("1", 2+2+(72/2), MACRO_BUTTON_Y+10, font);
      break;
    case 2:
      M5.Lcd.fillRoundRect(2+72+2, MACRO_BUTTON_Y, 72, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("2", 2+2+72+2+(72/2), MACRO_BUTTON_Y+10, font);
      break;
    case 3:
      M5.Lcd.fillRoundRect(2+72+2+72+2, MACRO_BUTTON_Y, 72, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("3", 2+2+72+2+72+2+(72/2), MACRO_BUTTON_Y+10, font);
      break;
    case 4:
      M5.Lcd.fillRoundRect(2+72+2+72+2+72+2, MACRO_BUTTON_Y, 72, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("4", 2+2+72+2+72+2+72+2+(72/2), MACRO_BUTTON_Y+10, font);
      break;
  }
}

void setupButtons() {
  M5.Touch.addHandler(buttonWasPressed, TE_BTNONLY + TE_TOUCH + TE_RELEASE);

  M5.Lcd.fillScreen(BLACK);

  M5.Lcd.setTextSize(2);
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.drawString("Program", 6, CAMERA_BUTTON_Y-20, 1);
  M5.Lcd.drawString("Preview", 6, MACRO_BUTTON_Y-20, 1);

  drawButton(1, false, false);
  drawButton(2, false, false);
  drawButton(3, false, false);
  drawButton(4, false, false);
  
  drawCameraButton(1, CAMERA_OFF);
  drawCameraButton(2, CAMERA_OFF);
  drawCameraButton(3, CAMERA_OFF);
  drawCameraButton(4, CAMERA_OFF);
}

void setup() {
  M5.begin(true, true, false, true);
  
  Serial.begin(115200);

  Serial.println("About to connect to wifi");
  WiFi.begin(ssid, password);

  int lpcnt=0 ;
  int lpcnt2=0 ;

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    lpcnt += 1 ;                           //
    if (lpcnt > 6) {                       // 6回目(3秒) で切断/再接続
      WiFi.disconnect(true,true);          //
      WiFi.begin(ssid, password);    //
      lpcnt = 0 ;                          //
      lpcnt2 += 1 ;                        // 再接続の回数をカウント
    }                                      //
    if (lpcnt2 > 3) {                      // 3回 接続できなければ、
      ESP.restart() ;                      // ソフトウェアリセット
    }                  
    Serial.print(".");
  }
  Serial.println("Connected to the WiFi network");

  AtemSwitcher.begin(switcherIp);
  AtemSwitcher.serialOutput(0x80);
  AtemSwitcher.connect();
  Serial.println("Connected to AtemSwitcher");

  setupButtons();
}

int currentRunningMacro = -1;
int tallyStates[4];

void loop() {
  M5.update();
  AtemSwitcher.runLoop();

  if(AtemSwitcher.getMacroRunStatusState() && 0x01 == 1) {
    int index = AtemSwitcher.getMacroRunStatusIndex();
    if(index != currentRunningMacro) {
      drawButton(index + 1, false, true);
    }
    currentRunningMacro = index;
  } else {
    if(currentRunningMacro > -1) {
      drawButton(currentRunningMacro + 1, false, false);
      currentRunningMacro = -1;
    }
  }

  for(int i=1; i<=4; i++) {
    int currentTallyState = AtemSwitcher.getProgramTally(i) ? CAMERA_PROGRAM : (AtemSwitcher.getPreviewTally(i) ? CAMERA_PREVIEW : CAMERA_OFF);

    if(currentTallyState != tallyStates[i-1]) {
      drawCameraButton(i, currentTallyState);
    }
    
    tallyStates[i-1] = currentTallyState;
  }
  
}

ATEM Mini Extreme / Extreme ISO用 (M5Core2ライブラリ ver.0.0.2)

タッチした際に反応が返ってくるように(振動モーター対応)しました。

ソースコード
M5Core2_ATEM_02.ino
// for ATEM Mini Extreme / Extreme ISO
// This code is based on
// https://github.com/aaronpk/m5-core2-atem-controller/
//
// Requirements
//
// Download Arduino Libraries from here
// https://github.com/kasperskaarhoj/SKAARHOJ-Open-Engineering/tree/master/ArduinoLibs
//
// 1. copy these folders to libraries directory
// ATEMbase
// ATEMmax
// ATEMstd
// SkaarhojPgmspace
//
// 2. change code in ATEMbase.cpp (1 change) and ATEMbase.h (2 changes)
// refer to: https://oneguyoneblog.com/2020/07/13/autopilot-for-blackmagic-design-atem-switcher/
//
//  from                  to
//  --------------------  --------------------
//  #ifdef ESP8266        WiFiUDP Udp;
//  WiFiUDP Udp;
//  #else
//  EthernetUDP Udp;
//  #endif

#include <M5Core2.h>
#include <Fonts/EVA_20px.h>
#include <stdio.h>
#include <WiFi.h>

#include <SkaarhojPgmspace.h>
#include <ATEMmax.h>
#include <ATEMstd.h>

// Define the IP address of your ATEM switcher
IPAddress switcherIp(0, 0, 0, 0);

// Put your wifi SSID and password here
const char* ssid = "";  // Wi-Fi SSID
const char* password =  "";  // Wi-Fi Password

#define CAMERA_OFF 0
#define CAMERA_PREVIEW 1
#define CAMERA_PROGRAM 2

#define CAMERA_BUTTON_Y 42
#define MACRO_BUTTON_Y 150

ATEMstd AtemSwitcher;

TouchButton c1 = TouchButton(0, CAMERA_BUTTON_Y, 40, 72, "c1");
TouchButton c2 = TouchButton(40, CAMERA_BUTTON_Y, 40, 72, "c2");
TouchButton c3 = TouchButton(40+40, CAMERA_BUTTON_Y, 40, 72, "c3");
TouchButton c4 = TouchButton(40+40+40, CAMERA_BUTTON_Y, 40, 72, "c4");
TouchButton c5 = TouchButton(40+40+40+40, CAMERA_BUTTON_Y, 40, 72, "c1");
TouchButton c6 = TouchButton(40+40+40+40+40, CAMERA_BUTTON_Y, 40, 72, "c6");
TouchButton c7 = TouchButton(40+40+40+40+40+40, CAMERA_BUTTON_Y, 40, 72, "c7");
TouchButton c8 = TouchButton(40+40+40+40+40+40+40, CAMERA_BUTTON_Y, 40, 72, "c8");

TouchButton b1 = TouchButton(0, MACRO_BUTTON_Y, 40, 72, "b1");
TouchButton b2 = TouchButton(40, MACRO_BUTTON_Y, 40, 72, "b2");
TouchButton b3 = TouchButton(40+40, MACRO_BUTTON_Y, 40, 72, "b3");
TouchButton b4 = TouchButton(40+40+40, MACRO_BUTTON_Y, 40, 72, "b4");
TouchButton b5 = TouchButton(40+40+40+40, MACRO_BUTTON_Y, 40, 72, "b5");
TouchButton b6 = TouchButton(40+40+40+40+40, MACRO_BUTTON_Y, 40, 72, "b6");
TouchButton b7 = TouchButton(40+40+40+40+40+40, MACRO_BUTTON_Y, 40, 72, "b7");
TouchButton b8 = TouchButton(40+40+40+40+40+40+40, MACRO_BUTTON_Y, 40, 72, "b8");

void vibration() {
  M5.Axp.SetLDOEnable(3, true);
  delay(50);
  M5.Axp.SetLDOEnable(3, false);
}

void buttonWasPressed(TouchEvent& e) {
  TouchButton& b = *e.button;

  char buttonType = b.name[0];
  int buttonNumber = b.name[1]-'0';

  Serial.printf("debug: buttontype = %c, buttonNumber = %d\n", buttonType, buttonNumber);
  vibration();
  
  if(buttonType == 'c') {
    AtemSwitcher.changeProgramInput(buttonNumber);
  }
  else if( buttonType == 'b') {
    drawButton(buttonNumber, b.isPressed(), false);
//    AtemSwitcher.setMacroAction(buttonNumber-1, 0); 
    AtemSwitcher.changePreviewInput(buttonNumber);
  }
  else {
    if(M5.BtnA.isPressed()) {
      AtemSwitcher.doAuto();
      Serial.println("debug: Button A is Pressed");
    }
    else if(M5.BtnB.isPressed()) {
      AtemSwitcher.doCut();
      Serial.println("debug: Button B is Pressed");
    }
    else if(M5.BtnC.isPressed()) {
      // do something
      Serial.println("debug: Button C is Pressed");
    }
  }
}

void drawCameraButton(int buttonNumber, int state) {
  Serial.printf("debug: buttonNumber = %d, state = %d\n", buttonNumber, state);

  int font = 1;
  M5.Lcd.setTextSize(6);
  
  M5.Lcd.setTextColor(state == CAMERA_OFF ? WHITE : BLACK);

  int buttonColor;
  switch(state) {
    case CAMERA_OFF:
      buttonColor = TFT_DARKGREY; break;
    case CAMERA_PREVIEW:
      buttonColor = TFT_GREEN; break;
    case CAMERA_PROGRAM:
      buttonColor = TFT_RED; break;
  }

  switch(buttonNumber) {
    case 1:
      M5.Lcd.fillRoundRect(0, CAMERA_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("1", 2+(40/2), CAMERA_BUTTON_Y+15, font);
      break;
    case 2:
      M5.Lcd.fillRoundRect(40, CAMERA_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("2", 2+40+(40/2), CAMERA_BUTTON_Y+15, font);
      break;
    case 3:
      M5.Lcd.fillRoundRect(40+40, CAMERA_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("3", 2+40+40+(40/2), CAMERA_BUTTON_Y+15, font);
      break;
    case 4:
      M5.Lcd.fillRoundRect(40+40+40, CAMERA_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("4", 2+40+40+40+(40/2), CAMERA_BUTTON_Y+15, font);
      break;
    case 5:
      M5.Lcd.fillRoundRect(40+40+40+40, CAMERA_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("5", 2+40+40+40+40+(40/2), CAMERA_BUTTON_Y+15, font);
      break;
    case 6:
      M5.Lcd.fillRoundRect(40+40+40+40+40, CAMERA_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("6", 2+40+40+40+40+40+(40/2), CAMERA_BUTTON_Y+15, font);
      break;
    case 7:
      M5.Lcd.fillRoundRect(40+40+40+40+40+40, CAMERA_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("7", 2+40+40+40+40+40+40+(40/2), CAMERA_BUTTON_Y+15, font);
      break;
    case 8:
      M5.Lcd.fillRoundRect(40+40+40+40+40+40+40, CAMERA_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("8", 2+40+40+40+40+40+40+40+(40/2), CAMERA_BUTTON_Y+15, font);
      break;
  }
}

void drawButton(int buttonNumber, bool isPressed, bool isRunning) {
  int font = 1;
  M5.Lcd.setTextSize(6);
  
  M5.Lcd.setTextColor(isPressed || isRunning ? BLACK : WHITE);

  int buttonColor = isPressed ? WHITE : (isRunning ? TFT_RED : TFT_DARKGREY);
  
  switch(buttonNumber) {
    case 1:
      M5.Lcd.fillRoundRect(0, MACRO_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("1", 2+(40/2), MACRO_BUTTON_Y+15, font);
      break;
    case 2:
      M5.Lcd.fillRoundRect(40, MACRO_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("2", 2+40+(40/2), MACRO_BUTTON_Y+15, font);
      break;
    case 3:
      M5.Lcd.fillRoundRect(40+40, MACRO_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("3", 2+40+40+(40/2), MACRO_BUTTON_Y+15, font);
      break;
    case 4:
      M5.Lcd.fillRoundRect(40+40+40, MACRO_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("4", 2+40+40+40+(40/2), MACRO_BUTTON_Y+15, font);
      break;
    case 5:
      M5.Lcd.fillRoundRect(40+40+40+40, MACRO_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("5", 2+40+40+40+40+(40/2), MACRO_BUTTON_Y+15, font);
      break;
    case 6:
      M5.Lcd.fillRoundRect(40+40+40+40+40, MACRO_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("6", 2+40+40+40+40+40+(40/2), MACRO_BUTTON_Y+15, font);
      break;
    case 7:
      M5.Lcd.fillRoundRect(40+40+40+40+40+40, MACRO_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("7", 2+40+40+40+40+40+40+(40/2), MACRO_BUTTON_Y+15, font);
      break;
    case 8:
      M5.Lcd.fillRoundRect(40+40+40+40+40+40+40, MACRO_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("8", 2+40+40+40+40+40+40+40+(40/2), MACRO_BUTTON_Y+15, font);
      break;
  }
}

void setupButtons() {
  M5.Touch.addHandler(buttonWasPressed, TE_BTNONLY + TE_TOUCH + TE_RELEASE);

  M5.Lcd.fillScreen(BLACK);

  M5.Lcd.setTextSize(2);
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.drawString("Program", 6, CAMERA_BUTTON_Y-20, 1);
  M5.Lcd.drawString("Preview", 6, MACRO_BUTTON_Y-20, 1);

  drawButton(1, false, false);
  drawButton(2, false, false);
  drawButton(3, false, false);
  drawButton(4, false, false);
  drawButton(5, false, false);
  drawButton(6, false, false);
  drawButton(7, false, false);
  drawButton(8, false, false);
  
  drawCameraButton(1, CAMERA_OFF);
  drawCameraButton(2, CAMERA_OFF);
  drawCameraButton(3, CAMERA_OFF);
  drawCameraButton(4, CAMERA_OFF);
  drawCameraButton(5, CAMERA_OFF);
  drawCameraButton(6, CAMERA_OFF);
  drawCameraButton(7, CAMERA_OFF);
  drawCameraButton(8, CAMERA_OFF);
}

void setup() {
  M5.begin(true, true, false, true);
  
  Serial.begin(115200);

  Serial.println("About to connect to wifi");
  WiFi.begin(ssid, password);

  int lpcnt=0 ;
  int lpcnt2=0 ;

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    lpcnt += 1 ;                           //
    if (lpcnt > 6) {                       // 6回目(3秒) で切断/再接続
      WiFi.disconnect(true,true);          //
      WiFi.begin(ssid, password);    //
      lpcnt = 0 ;                          //
      lpcnt2 += 1 ;                        // 再接続の回数をカウント
    }                                      //
    if (lpcnt2 > 3) {                      // 3回 接続できなければ、
      ESP.restart() ;                      // ソフトウェアリセット
    }                  
    Serial.print(".");
  }
  Serial.println("Connected to the WiFi network");

  AtemSwitcher.begin(switcherIp);
  AtemSwitcher.serialOutput(0x80);
  AtemSwitcher.connect();
  Serial.println("Connected to AtemSwitcher");

  setupButtons();
}

int currentRunningMacro = -1;
int tallyNum = 8;
int tallyStates[8];

void loop() {
  M5.update();

  AtemSwitcher.runLoop();

  for(int i=1; i<=tallyNum; i++) {
    int currentTallyState = AtemSwitcher.getProgramTally(i) ? CAMERA_PROGRAM : (AtemSwitcher.getPreviewTally(i) ? CAMERA_PREVIEW : CAMERA_OFF);
    if(currentTallyState != tallyStates[i-1]) {
      drawCameraButton(i, currentTallyState);
    }
    tallyStates[i-1] = currentTallyState;
  }
}

M5Core2ライブラリ ver.0.0.6に対応

ソースコード
M5Core2_ATEM_03.ino
// for ATEM Mini Extreme / Extreme ISO
// This code is based on
// https://github.com/aaronpk/m5-core2-atem-controller/
//
// Conditions
//
// include M5Core2 library ver.0.0.6
//
// Requirements
//
// Download Arduino Libraries from here
// https://github.com/kasperskaarhoj/SKAARHOJ-Open-Engineering/tree/master/ArduinoLibs
//
// 1. copy these folders to libraries directory
// ATEMbase
// ATEMmax
// ATEMstd
// SkaarhojPgmspace
//
// 2. change code in ATEMbase.cpp (1 change) and ATEMbase.h (2 changes)
// refer to: https://oneguyoneblog.com/2020/07/13/autopilot-for-blackmagic-design-atem-switcher/
//
//  from                  to
//  --------------------  --------------------
//  #ifdef ESP8266        WiFiUDP Udp;
//  WiFiUDP Udp;
//  #else
//  EthernetUDP Udp;
//  #endif

#include <M5Core2.h>
#include <Fonts/EVA_20px.h>
#include <stdio.h>
#include <WiFi.h>
#include <M5GFX.h>
#include <SkaarhojPgmspace.h>
#include <ATEMmax.h>
#include <ATEMstd.h>

// Define the IP address of your ATEM switcher
IPAddress switcherIp(0, 0, 0, 0);

// Put your wifi SSID and password here
const char* ssid = "";  // Wi-Fi SSID
const char* password =  "";  // Wi-Fi Password

#define CAMERA_OFF 0
#define CAMERA_PREVIEW 1
#define CAMERA_PROGRAM 2

#define CAMERA_BUTTON_Y 42
#define MACRO_BUTTON_Y 150

ATEMstd AtemSwitcher;

Button c1(0, CAMERA_BUTTON_Y, 40, 72, false, "c1");
Button c2(40, CAMERA_BUTTON_Y, 40, 72, false, "c2");
Button c3(40+40, CAMERA_BUTTON_Y, 40, 72, false, "c3");
Button c4(40+40+40, CAMERA_BUTTON_Y, 40, 72, false, "c4");
Button c5(40+40+40+40, CAMERA_BUTTON_Y, 40, 72, false, "c5");
Button c6(40+40+40+40+40, CAMERA_BUTTON_Y, 40, 72, false, "c6");
Button c7(40+40+40+40+40+40, CAMERA_BUTTON_Y, 40, 72, false, "c7");
Button c8(40+40+40+40+40+40+40, CAMERA_BUTTON_Y, 40, 72, false, "c8");

Button d1(0, MACRO_BUTTON_Y, 40, 72, false, "d1");
Button d2(40, MACRO_BUTTON_Y, 40, 72, false, "d2");
Button d3(40+40, MACRO_BUTTON_Y, 40, 72, false, "d3");
Button d4(40+40+40, MACRO_BUTTON_Y, 40, 72, false, "d4");
Button d5(40+40+40+40, MACRO_BUTTON_Y, 40, 72, false, "d5");
Button d6(40+40+40+40+40, MACRO_BUTTON_Y, 40, 72, false, "d6");
Button d7(40+40+40+40+40+40, MACRO_BUTTON_Y, 40, 72, false, "d7");
Button d8(40+40+40+40+40+40+40, MACRO_BUTTON_Y, 40, 72, false, "d8");

void vibration() {
  M5.Axp.SetLDOEnable(3, true);
  delay(50);
  M5.Axp.SetLDOEnable(3, false);
}

void buttonWasPressed(Event& e) {

  const char* buttonName = e.objName();
  const char* eventName = e.typeName();
  char buttonType = buttonName[0];
  int buttonNumber = buttonName[1]-'0';

  // to avoid buttonName == 'background', change buttonType from 'c or b' to 'c or d'
  Serial.printf("debug: buttonName = %s, eventName = %s, buttonType = %c, buttonNumber = %d\n", buttonName, eventName, buttonType, buttonNumber);
  vibration();
  
  if(buttonType == 'c') {
    AtemSwitcher.changeProgramInput(buttonNumber);
  }
  else if( buttonType == 'd') {
    if ( e.type == E_TOUCH) {
      drawButton(buttonNumber, true, false);
    } else {
      drawButton(buttonNumber, CAMERA_OFF, false);
    }

    AtemSwitcher.changePreviewInput(buttonNumber);
  }
  else {
    if(M5.BtnA.isPressed()) {
      AtemSwitcher.doAuto();
      Serial.println("debug: Button A is Pressed");
    }
    else if(M5.BtnB.isPressed()) {
      AtemSwitcher.doCut();
      Serial.println("debug: Button B is Pressed");
    }
    else if(M5.BtnC.isPressed()) {
      // do something
      Serial.println("debug: Button C is Pressed");
    }
  }
}

void drawCameraButton(int buttonNumber, int state) {
  Serial.printf("debug: buttonNumber = %d, state = %d\n", buttonNumber, state);

  int font = 1;
  M5.Lcd.setTextSize(6);
  
  M5.Lcd.setTextColor(state == CAMERA_OFF ? WHITE : BLACK);

  int buttonColor;
  switch(state) {
    case CAMERA_OFF:
      buttonColor = TFT_DARKGREY; break;
    case CAMERA_PREVIEW:
      buttonColor = TFT_GREEN; break;
    case CAMERA_PROGRAM:
      buttonColor = TFT_RED; break;
  }

  switch(buttonNumber) {
    case 1:
      M5.Lcd.fillRoundRect(0, CAMERA_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("1", 2+(40/2), CAMERA_BUTTON_Y+15, font);
      break;
    case 2:
      M5.Lcd.fillRoundRect(40, CAMERA_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("2", 2+40+(40/2), CAMERA_BUTTON_Y+15, font);
      break;
    case 3:
      M5.Lcd.fillRoundRect(40+40, CAMERA_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("3", 2+40+40+(40/2), CAMERA_BUTTON_Y+15, font);
      break;
    case 4:
      M5.Lcd.fillRoundRect(40+40+40, CAMERA_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("4", 2+40+40+40+(40/2), CAMERA_BUTTON_Y+15, font);
      break;
    case 5:
      M5.Lcd.fillRoundRect(40+40+40+40, CAMERA_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("5", 2+40+40+40+40+(40/2), CAMERA_BUTTON_Y+15, font);
      break;
    case 6:
      M5.Lcd.fillRoundRect(40+40+40+40+40, CAMERA_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("6", 2+40+40+40+40+40+(40/2), CAMERA_BUTTON_Y+15, font);
      break;
    case 7:
      M5.Lcd.fillRoundRect(40+40+40+40+40+40, CAMERA_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("7", 2+40+40+40+40+40+40+(40/2), CAMERA_BUTTON_Y+15, font);
      break;
    case 8:
      M5.Lcd.fillRoundRect(40+40+40+40+40+40+40, CAMERA_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("8", 2+40+40+40+40+40+40+40+(40/2), CAMERA_BUTTON_Y+15, font);
      break;
  }
}

void drawButton(int buttonNumber, bool isPressed, bool isRunning) {
  int font = 1;
  M5.Lcd.setTextSize(6);
  
  M5.Lcd.setTextColor(isPressed || isRunning ? BLACK : WHITE);

  int buttonColor = isPressed ? WHITE : (isRunning ? TFT_RED : TFT_DARKGREY);
  
  switch(buttonNumber) {
    case 1:
      M5.Lcd.fillRoundRect(0, MACRO_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("1", 2+(40/2), MACRO_BUTTON_Y+15, font);
      break;
    case 2:
      M5.Lcd.fillRoundRect(40, MACRO_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("2", 2+40+(40/2), MACRO_BUTTON_Y+15, font);
      break;
    case 3:
      M5.Lcd.fillRoundRect(40+40, MACRO_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("3", 2+40+40+(40/2), MACRO_BUTTON_Y+15, font);
      break;
    case 4:
      M5.Lcd.fillRoundRect(40+40+40, MACRO_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("4", 2+40+40+40+(40/2), MACRO_BUTTON_Y+15, font);
      break;
    case 5:
      M5.Lcd.fillRoundRect(40+40+40+40, MACRO_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("5", 2+40+40+40+40+(40/2), MACRO_BUTTON_Y+15, font);
      break;
    case 6:
      M5.Lcd.fillRoundRect(40+40+40+40+40, MACRO_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("6", 2+40+40+40+40+40+(40/2), MACRO_BUTTON_Y+15, font);
      break;
    case 7:
      M5.Lcd.fillRoundRect(40+40+40+40+40+40, MACRO_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("7", 2+40+40+40+40+40+40+(40/2), MACRO_BUTTON_Y+15, font);
      break;
    case 8:
      M5.Lcd.fillRoundRect(40+40+40+40+40+40+40, MACRO_BUTTON_Y, 39, 72, 6, buttonColor);
      M5.Lcd.drawCentreString("8", 2+40+40+40+40+40+40+40+(40/2), MACRO_BUTTON_Y+15, font);
      break;
  }
}

void setupButtons() {
  M5.Buttons.addHandler(buttonWasPressed, E_TAP + E_TOUCH + E_RELEASE);

  M5.Lcd.fillScreen(BLACK);

  M5.Lcd.setTextSize(2);
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.drawString("Program", 6, CAMERA_BUTTON_Y-20, 1);
  M5.Lcd.drawString("Preview", 6, MACRO_BUTTON_Y-20, 1);

  drawButton(1, false, false);
  drawButton(2, false, false);
  drawButton(3, false, false);
  drawButton(4, false, false);
  drawButton(5, false, false);
  drawButton(6, false, false);
  drawButton(7, false, false);
  drawButton(8, false, false);
  
  drawCameraButton(1, CAMERA_OFF);
  drawCameraButton(2, CAMERA_OFF);
  drawCameraButton(3, CAMERA_OFF);
  drawCameraButton(4, CAMERA_OFF);
  drawCameraButton(5, CAMERA_OFF);
  drawCameraButton(6, CAMERA_OFF);
  drawCameraButton(7, CAMERA_OFF);
  drawCameraButton(8, CAMERA_OFF);
}

void setup() {
  M5.begin(true, true, false, true);
  
  Serial.begin(115200);

  Serial.println("About to connect to wifi");
  WiFi.begin(ssid, password);

  int lpcnt=0 ;
  int lpcnt2=0 ;

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    lpcnt += 1 ;                           //
    if (lpcnt > 6) {                       // 6回目(3秒) で切断/再接続
      WiFi.disconnect(true,true);          //
      WiFi.begin(ssid, password);    //
      lpcnt = 0 ;                          //
      lpcnt2 += 1 ;                        // 再接続の回数をカウント
    }                                      //
    if (lpcnt2 > 3) {                      // 3回 接続できなければ、
      ESP.restart() ;                      // ソフトウェアリセット
    }                  
    Serial.print(".");
  }
  Serial.println("Connected to the WiFi network");

  AtemSwitcher.begin(switcherIp);
  AtemSwitcher.serialOutput(0x80);
  AtemSwitcher.connect();
  Serial.println("Connected to AtemSwitcher");

  setupButtons();
}

int currentRunningMacro = -1;
int tallyNum = 8;
int tallyStates[8];

void loop() {
  M5.update();

  AtemSwitcher.runLoop();

  for(int i=1; i<=tallyNum; i++) {
    int currentTallyState = AtemSwitcher.getProgramTally(i) ? CAMERA_PROGRAM : (AtemSwitcher.getPreviewTally(i) ? CAMERA_PREVIEW : CAMERA_OFF);
    if(currentTallyState != tallyStates[i-1]) {
      drawCameraButton(i, currentTallyState);
    }
    tallyStates[i-1] = currentTallyState;
  }
}

参考にしたページ

https://aaronparecki.com/2020/12/30/10/
https://oneguyoneblog.com/2020/07/13/autopilot-for-blackmagic-design-atem-switcher/

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?