Skip to content

Instantly share code, notes, and snippets.

@anthonyeden
Created March 6, 2021 10:40
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anthonyeden/a439bd3f14217b4a34cf1acfd274b6e2 to your computer and use it in GitHub Desktop.
Save anthonyeden/a439bd3f14217b4a34cf1acfd274b6e2 to your computer and use it in GitHub Desktop.
Q-SYS Camera Tally - ESP32 M5StickC
/*****************
ESP32 Tally Light for Q-SYS
Version 1.0
A wireless (WiFi) tally light for Q-SYS, based on the M5StickC ESP32 development board and the Arduino IDE.
Based on: https://github.com/oneguyoneblog/tally-light-esp32-for-blackmagic-atem-switcher/blob/master/tally-light-esp32-for-blackmagic-atem-switcher.ino
******************/
#include <M5StickC.h>
#include <WiFi.h>
#include <esp_wifi.h>
WiFiClient client;
IPAddress qsysIp(10, 1, 1, 9); // IP address of the Q-SYS Core
int qsysPortNumber = 10001; // Port number for this TCP server on Q-SYS (one TCP server per tally)
const char* ssid = "WIFI NETWORK NAME";
const char* password = "1234567890";
int ledPin = 10;
String TCPReceived = "";
// Set your new MAC Address
uint8_t newMACAddress[] = {0x32, 0xAE, 0xA4, 0x07, 0x0D, 0x66};
void setup() {
Serial.begin(9600);
// initialize the M5StickC object
M5.begin();
Serial.println("Beginning session");
// Blank the screen
String resetString = "X";
drawLabel(0xF800, 0, LOW, resetString);
// Start the Ethernet, Serial (debugging) and UDP:
WiFi.begin(ssid, password);
Serial.print("ESP Board MAC Address: ");
Serial.println(WiFi.macAddress());
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.println("Connecting to WiFi..");
}
Serial.println("Connected to the WiFi network");
pinMode(ledPin, OUTPUT); // LED: 1 is on Program (Tally)
digitalWrite(ledPin, HIGH); // off
// Initialize a connection to the Q-SYS:
connect_qsys();
}
void connect_qsys() {
if (client.connect(qsysIp, qsysPortNumber)) {
Serial.println("connected");
} else {
Serial.println("connection failed");
delay(1000);
connect_qsys();
}
}
void loop() {
if (!client.connected()) {
Serial.println("disconnecting.");
client.stop();
connect_qsys();
}
if (client.available() > 0) {
char thisChar = client.read();
TCPReceived = String(TCPReceived) + String(thisChar);
if (thisChar == '\n') {
// Parse commands in TCPReceived, then reset it
String screenColor = getValue(TCPReceived, ';', 0);
unsigned long int screenColor2 = atol(screenColor.c_str());
String labelColor = getValue(TCPReceived, ';', 1);
unsigned long int labelColor2 = atol(labelColor.c_str());
String ledValue = getValue(TCPReceived, ';', 2);
bool ledValueBool = HIGH;
if (ledValue == "LOW") {
ledValueBool = LOW;
}
String textDisplay = getValue(TCPReceived, ';', 3);
// Update the display
drawLabel(screenColor2, labelColor2, ledValueBool, textDisplay);
// Reset the buffer
Serial.println(TCPReceived);
TCPReceived = "";
}
}
}
void drawLabel(unsigned long int screenColor, unsigned long int labelColor, bool ledValue, String textDisplay) {
digitalWrite(ledPin, ledValue);
M5.Lcd.fillScreen(screenColor);
M5.Lcd.setTextColor(labelColor, screenColor);
M5.Lcd.setTextSize(6);
M5.Lcd.drawString(textDisplay, 15, 40);
}
String getValue(String data, char separator, int index) {
// https://stackoverflow.com/questions/9072320/split-string-into-string-array
int found = 0;
int strIndex[] = {0, -1};
int maxIndex = data.length() - 1;
for (int i = 0; i <= maxIndex && found <= index; i++) {
if (data.charAt(i) == separator || i == maxIndex) {
found++;
strIndex[0] = strIndex[1] + 1;
strIndex[1] = (i == maxIndex) ? i + 1 : i;
}
}
return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}
server = TcpSocketServer.New()
-- Specify the input number on the ATEM
thisInputNumber = 1
-- Stores the state of the ATEM
currentPgm = 0
currentPvw = 0
server.EventHandler = function(SocketInstance)
-- Setup a connection handler. One handler per connection
SocketInstance.ReadTimeout = 0
print( "Got connect", SocketInstance )
SocketInstance.EventHandler = SocketHandler
timer_func(SocketInstance)
end
function SocketHandler(NewSocketInstance, event)
-- Get new TCP data from any connected client
print( "TCP Socket Event: "..event )
end
-- On script startup, begin listing on TCP port 20001
server:Listen(2000 .. thisInputNumber)
function timer_func(SocketInstance)
Timer.CallAfter(function()
timer_func(SocketInstance)
end, 0.5)
changed = false
for i=1,8 do
if NamedControl.GetValue("ATEMControlStreaming:ScriptableControlsME1Program" .. i) == 1.0 and currentPgm ~= i then
currentPgm = i
changed = true
end
if NamedControl.GetValue("ATEMControlStreaming:ScriptableControlsME1Preview" .. i) == 1.0 and currentPvw ~= i then
currentPvw = i
changed = true
end
end
-- OUTPUT FORMAT: BG_COL;TXT_COL;LOW/HIGH;TEXT
if changed == true then
if currentPgm == thisInputNumber then
SocketInstance:Write("1700000;0;LOW;" .. currentPgm .. "\n")
elseif currentPvw == thisInputNumber then
SocketInstance:Write("600000;0;HIGH;" .. currentPgm .. "\n")
else
SocketInstance:Write("0;600000;HIGH;" .. currentPgm .. "\n")
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment