Skip to content

Commit

Permalink
Better battery performance; Just Live and Mode functionality; AP SSID…
Browse files Browse the repository at this point in the history
… appends partial MAC; More robust restart mechanism;
  • Loading branch information
guido-visser committed Nov 5, 2020
1 parent 47c4ce9 commit 3466de3
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 29 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,16 @@
- 🚀 Multi input support
- 🚀 Automatic wifi scanning to easily select your wifi network
- 🚀 Set an interval to automatically reconnect to vMix after the interval has passed
- 🚀 Option to just show the LIVE state and ignore the PRE and SAFE states
- 🚀 Option to show the tally number instead of the SAFE, PRE and LIVE texts

---

## FAQ

**Q: Does this work with ATEM, TriCaster, OBS Studio and others?**
A: Yes, however [Tally Arbiter](https://github.com/josephdadams/TallyArbiter) is required for this functionality. The plan is to integrate seemless with TA. Stay tuned for updates.

## Getting started: Uploading the code to the stick

### Video tutorial
Expand Down Expand Up @@ -142,6 +149,16 @@ Set the brightness of the LCD. Option to choose from:

NOTE: Obviously, the battery will drain faster when the brightness is set to a high value. I recommend setting it at 60%.

**Just Live**

`true`: The stick only lights up when the configured input(s) is/are live.
`false`: The stick shows when the configured input(s) is/are safe, are in preview or when it is live.

**Mode**

`Text (SAFE, PRE, LIVE)`: Show the textual representation of the status of the configured input(s).
`Tally Number`: Show the configured main tally number and only update the background color when the status changes.

---

## Plugins
Expand All @@ -166,6 +183,7 @@ Since M5Stack has a lot of different products, some wonderful people forked my c

## TODO

- [ ] Create a seemless integration with [Tally Arbiter](https://github.com/josephdadams/TallyArbiter)
- [ ] Configuration tutorial
- [x] Written
- [ ] Video
Expand Down
6 changes: 5 additions & 1 deletion src/a_GLOBAL/a_GLOBAL.ino
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

#define LED_BUILTIN 10

int tnlen = 1; //LET THIS BE

Preferences preferences;

//DON'T CHANGE THESE VARIABLES, YOU CAN CHANGE THEM IN THE WEB UI
Expand All @@ -24,5 +26,7 @@ int VMIX_PORT = 8099; //USES THE TCP API PORT, THIS IS FIXED IN VMIX
int TALLY_NR = 1;
int BRIGHTNESS = 12; //100%
int CONN_INT = 0;
int MODE = 0; //0 for words like SAFE, PRE and LIVE. 1 for numbers with changing background
int JUSTLIVE = 0; //When 1, SAFE and PRE are not used. Just the LIVE screen

String semver = "2.2.0";
String semver = "2.3.0";
9 changes: 6 additions & 3 deletions src/a_GLOBAL/b_SETTINGS.ino
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@ void loadSettings()
if(preferences.getString("vmix_ip").length() > 0){
TALLY_NR = preferences.getUInt("tally");
VMIX_IP = preferences.getString("vmix_ip");
if(TALLY_NR > 9){
tnlen = 2;
}
}

if(preferences.getUInt("conn_int")){
CONN_INT = preferences.getUInt("conn_int");
}
CONN_INT = preferences.getUInt("conn_int") || CONN_INT;
MODE = preferences.getUInt("mode") || MODE;
JUSTLIVE = preferences.getUInt("justLive") || JUSTLIVE;

if(preferences.getString("m_tally").length() > 0){
M_TALLY = preferences.getString("m_tally");
Expand Down
1 change: 1 addition & 0 deletions src/a_GLOBAL/c_MAIN.ino
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ float accZ = 0;
void setup()
{
uint8_t c;
setCpuFrequencyMhz(80); //Thanks Irvin Cee
Serial.begin(115200);
M5.begin();
M5.IMU.Init();
Expand Down
94 changes: 74 additions & 20 deletions src/a_GLOBAL/d_VMIX.ino
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,43 @@ boolean retryConnectionvMix(int tryCount) {
return true;
}

void posTallyNums(){
int y = lcdCoordY(70);
int x = lcdCoordX(30);
if(screenRotation == 1 || screenRotation == 3){
x = lcdCoordX(70);
y = lcdCoordY(23);
if(tnlen == 2){
x = lcdCoordX(50);
}
M5.Lcd.setCursor(x, y);
} else {
if(tnlen == 2){
x = lcdCoordX(10);
}
M5.Lcd.setCursor(x, y);
}
}

void setTallyProgram()
{
digitalWrite(LED_BUILTIN, LOW);
M5.Lcd.fillScreen(RED);
M5.Lcd.setTextColor(WHITE, RED);
if(screenRotation == 1 || screenRotation == 3){
M5.Lcd.setCursor(lcdCoordX(25), lcdCoordY(23));
M5.Lcd.println("LIVE");
if(MODE == 0){
M5.Lcd.println("LIVE");
}
} else if(screenRotation == 0 || screenRotation == 2) {
M5.Lcd.setCursor(30, 70);
M5.Lcd.println("L");
M5.Lcd.setCursor(lcdCoordX(30), lcdCoordY(70));
if(MODE == 0){
M5.Lcd.println("L");
}
}
if(MODE == 1){
posTallyNums();
M5.Lcd.println(TALLY_NR);
}
pm.onLive();
}
Expand All @@ -93,10 +119,18 @@ void setTallyPreview() {
M5.Lcd.setTextColor(BLACK, GREEN);
if(screenRotation == 1 || screenRotation == 3){
M5.Lcd.setCursor(lcdCoordX(40), lcdCoordY(23));
M5.Lcd.println("PRE");
if(!JUSTLIVE && MODE == 0){
M5.Lcd.println("PRE");
}
} else if(screenRotation == 0 || screenRotation == 2) {
M5.Lcd.setCursor(lcdCoordX(30), lcdCoordY(70));
M5.Lcd.println("P");
if(!JUSTLIVE && MODE == 0){
M5.Lcd.println("P");
}
}
if(MODE == 1){
posTallyNums();
M5.Lcd.println(TALLY_NR);
}
pm.onPre();
}
Expand All @@ -107,10 +141,18 @@ void setTallyOff() {
M5.Lcd.setTextColor(WHITE, BLACK);
if(screenRotation == 1 || screenRotation == 3){
M5.Lcd.setCursor(lcdCoordX(23), lcdCoordY(23));
M5.Lcd.println("SAFE");
if(!JUSTLIVE && MODE == 0){
M5.Lcd.println("SAFE");
}
} else if(screenRotation == 0 || screenRotation == 2) {
M5.Lcd.setCursor(lcdCoordX(30), lcdCoordY(70));
M5.Lcd.println("S");
if(!JUSTLIVE && MODE == 0){
M5.Lcd.println("S");
}
}
if(MODE == 1){
posTallyNums();
M5.Lcd.println(TALLY_NR);
}
pm.onSafe();
}
Expand Down Expand Up @@ -175,20 +217,32 @@ void showTallyScreen() {
} else {
M5.Lcd.setTextSize(5);
}
switch (currentState)
{
case '0':
setTallyOff();
break;
case '1':
setTallyProgram();
break;
case '2':
setTallyPreview();
break;
default:
setTallyOff();
if(!JUSTLIVE){
switch (currentState)
{
case '0':
setTallyOff();
break;
case '1':
setTallyProgram();
break;
case '2':
setTallyPreview();
break;
default:
setTallyOff();
}
} else {
switch (currentState)
{
case '1':
setTallyProgram();
break;
default:
setTallyOff();
}
}

renderBatteryLevel();
}

Expand Down
4 changes: 3 additions & 1 deletion src/a_GLOBAL/e_WIFI.ino
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ void startWiFi()
// This starts the M5Stack as a WiFi Access Point so you can configure it
void startLocalWiFi()
{
char apSsid[32];
sprintf(apSsid, "vMix-M5Stick-Tally %s", WiFi.macAddress().substring(9, WiFi.macAddress().length()));
WiFi.mode(WIFI_AP);
WiFi.softAP("vMix-M5Stick-Tally", "12345678");
WiFi.softAP(apSsid, "12345678");
apEnabled = true;
showAPScreen();
delay(100);
Expand Down
20 changes: 18 additions & 2 deletions src/a_GLOBAL/f_WEBSERVER.ino
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ String FOOTER = "</div>\
void handle_root()
{
String tally = (String)TALLY_NR;
String justLive = (String)JUSTLIVE;
String bright = (String)BRIGHTNESS;
String _mode = (String)MODE;
String HTML = HEADER;
HTML += "<div class=wrapper data-theme=light><h1>vMix M5Stack Tally Settings</h1><form action=/save id=frmData method=post onsubmit=return!1><div>SSID:<br><select id=ssid><option disabled selected>Scanning wifi...</select></div><div class=ssidCustomDiv style=display:none>Hidden SSID Name:<br><input id=ssidCustom type=text value='" + (String)WIFI_SSID + "'name=ssidCustom></div><div>Password:<br><input id=pwd type=text value='" + (String)WIFI_PASS + "'name=pwd></div><div>vMix IP Address:<br><input id=vmixip type=text value='" + (String)VMIX_IP + "'name=vmixip></div><div>Main Tally Number:<br><input id=tally_num type=number value='" + tally + "'name=tally_num max=1000 min=1></div><div>Multi Input (comma separated):<br><input id=m_tally type=text value='" + (String)M_TALLY + "'name=m_tally></div><div>Reconnect interval (in seconds, 0 means no reconnection interval):<br><input id=conn_int type=number value='" + CONN_INT + "'name=conn_int></div><div>Brightness:<br><select id=drpBright name=bright><option value=7>0%<option value=8>20%<option value=9>40%<option value=10>60%<option value=11>80%<option value=12>100%</select></div><input id=btnSave type=submit value=SAVE class='btn btn-primary'></form><h2>Reconnect to vMix</h2><form action=/reconnect id=frmReconnect method=post onsubmit=return!1><input id=btnReconnect type=submit value=RECONNECT></form></div><script>const btnSave = document.querySelector('#btnSave'); const btnReconnect = document.querySelector('#btnReconnect'); const drpBright = document.querySelector('#drpBright'); const ssidSelect = document.querySelector('#ssid'); drpBright.value = '" + bright + "'; btnSave.addEventListener('click', async function(e){ e.preventDefault(); let ssid = document.querySelector('#ssid').value; if(ssid === '__hidden__'){ ssid = document.querySelector('#ssidCustom').value; } const pwd = document.querySelector('#pwd').value; const vmixip = document.querySelector('#vmixip').value; const m_tally = document.querySelector('#m_tally').value; const frmData = document.querySelector('#frmData'); const tally_num = document.querySelector('#tally_num').value; const conn_int = parseInt(document.querySelector('#conn_int').value, 10) || 0; let formData = new FormData(); formData.append('ssid', ssid.trim()); formData.append('pwd',pwd.trim()); formData.append('vmixip', vmixip.trim()); formData.append('m_tally', m_tally.trim().replace(/[^0-9,]+/g, '')); formData.append('tally_num', tally_num); formData.append('conn_int', conn_int); btnSave.setAttribute('disabled', ''); const res = await fetch('/save', { method: 'POST', cache: 'no-cache', referrerPolicy: 'no-referrer', body: formData }); if(res.status === 200){ btnSave.value = 'SETTINGS SAVED!'; await setTimeout(()=>{btnSave.value = 'SAVE';}, 3000); } btnSave.removeAttribute('disabled'); }); btnReconnect.addEventListener('click', function(e){ e.preventDefault(); fetch('/reconnect'); }); ssidSelect.addEventListener('change', e => { const val = e.target.value; const ssidcd = document.querySelector('.ssidCustomDiv'); if(val === '__hidden__'){ ssidcd.style.display = 'block'; } else { ssidcd.style.display = 'none'; } }); document.addEventListener('DOMContentLoaded', async function(){ const res = await fetch('/scanNetwork'); res.text().then(text=>{ let networks = [text]; let str = ''; if(text.indexOf('|||') !== -1){ networks = text.split('|||'); } let sel = document.getElementById('ssid'); sel.innerHTML = ''; let existingNetwork = ''; networks.forEach(network => { let opt = document.createElement('option'); opt.appendChild( document.createTextNode(network) ); opt.value = network; if('" + (String)WIFI_SSID + "' === network){ existingNetwork = network; } sel.appendChild(opt); }); let opt = document.createElement('option'); opt.appendChild( document.createTextNode('Hidden network') ); opt.value = '__hidden__'; sel.appendChild(opt); if(existingNetwork !== ''){ sel.value = existingNetwork; } }); });</script>";

HTML += "<div class=wrapper data-theme=light><h1>vMix M5Stack Tally Settings</h1><form action=/save id=frmData method=post onsubmit=return!1><div>SSID:<br><select id=ssid><option disabled selected>Scanning wifi...</option></select></div><div class=ssidCustomDiv style=display:none>Hidden SSID Name:<br><input id=ssidCustom type=text value='" + (String)WIFI_SSID + "'name=ssidCustom></div><div>Password:<br><input id=pwd type=text value='" + (String)WIFI_PASS + "'name=pwd></div><div>vMix IP Address:<br><input id=vmixip type=text value='" + (String)VMIX_IP + "'name=vmixip></div><div>Main Tally Number:<br><input id=tally_num type=number value='" + tally + "'name=tally_num max=1000 min=1></div><div>Multi Input (comma separated):<br><input id=m_tally type=text value='" + (String)M_TALLY + "'name=m_tally></div><div>Reconnect interval (in seconds, 0 means no reconnection interval):<br><input id=conn_int type=number value='" + CONN_INT + "'name=conn_int></div><div>Brightness:<br><select id=drpBright name=bright><option value=7>0%</option><option value=8>20%</option><option value=9>40%</option><option value=10>60%</option><option value=11>80%</option><option value=12>100%</option></select></div><div>Just Live:<br><select id=drpJustLive name=justLive><option value=0>False</option><option value=1>True</option></select></div><div>Mode:<br><select id=drpMode name=mode><option value=0>Text (SAFE, PRE, LIVE)</option><option value=1>Tally Number</option></select></div><input id=btnSave type=submit value=SAVE class='btn btn-primary'></form><h2>Reconnect to vMix</h2><form action=/reconnect id=frmReconnect method=post onsubmit=return!1><input id=btnReconnect type=submit value=RECONNECT></form></div><script>const btnSave = document.querySelector('#btnSave'); const btnReconnect = document.querySelector('#btnReconnect'); const drpBright = document.querySelector('#drpBright'); const ssidSelect = document.querySelector('#ssid'); const drpJustLive = document.querySelector('#drpJustLive'); const drpMode = document.querySelector('#drpMode'); drpBright.value = '" + bright + "'; drpJustLive.value = '" + justLive + "'; drpMode.value = '" + _mode + "'; btnSave.addEventListener('click', async function(e){ e.preventDefault(); let ssid = document.querySelector('#ssid').value; if(ssid === '__hidden__'){ ssid = document.querySelector('#ssidCustom').value; } const pwd = document.querySelector('#pwd').value; const vmixip = document.querySelector('#vmixip').value; const m_tally = document.querySelector('#m_tally').value; const frmData = document.querySelector('#frmData'); const tally_num = document.querySelector('#tally_num').value; const bright = drpBright.value; const mode = drpMode.value; const justLive = drpJustLive.value; const conn_int = parseInt(document.querySelector('#conn_int').value, 10) || 0; let formData = new FormData(); formData.append('ssid', ssid.trim()); formData.append('pwd',pwd.trim()); formData.append('vmixip', vmixip.trim()); formData.append('m_tally', m_tally.trim().replace(/[^0-9,]+/g, '')); formData.append('tally_num', tally_num); formData.append('conn_int', conn_int); formData.append('bright', bright); formData.append('mode', mode); formData.append('justLive', justLive); btnSave.setAttribute('disabled', ''); const res = await fetch('/save', { method: 'POST', cache: 'no-cache', referrerPolicy: 'no-referrer', body: formData }); if(res.status === 200){ btnSave.value = 'SETTINGS SAVED!'; await setTimeout(()=>{btnSave.value = 'SAVE';}, 3000); } btnSave.removeAttribute('disabled'); }); btnReconnect.addEventListener('click', function(e){ e.preventDefault(); fetch('/reconnect'); }); ssidSelect.addEventListener('change', e => { const val = e.target.value; const ssidcd = document.querySelector('.ssidCustomDiv'); if(val === '__hidden__'){ ssidcd.style.display = 'block'; } else { ssidcd.style.display = 'none'; } }); document.addEventListener('DOMContentLoaded', async function(){ const res = await fetch('/scanNetwork'); res.text().then(text=>{ let networks = [text]; let str = ''; if(text.indexOf('|||') !== -1){ networks = text.split('|||'); } let sel = document.getElementById('ssid'); sel.innerHTML = ''; let existingNetwork = ''; networks.forEach(network => { let opt = document.createElement('option'); opt.appendChild( document.createTextNode(network) ); opt.value = network; if('" + (String)WIFI_SSID + "' === network){ existingNetwork = network; } sel.appendChild(opt); }); let opt = document.createElement('option'); opt.appendChild( document.createTextNode('Hidden network') ); opt.value = '__hidden__'; sel.appendChild(opt); if(existingNetwork !== ''){ sel.value = existingNetwork; } }); });</script>";
HTML += FOOTER;

server.send(200, "text/html", HTML);
Expand All @@ -47,6 +50,8 @@ void handle_save()

String tally = server.arg("tally_num");
String bright = server.arg("bright");
String justLive = server.arg("justLive");
String _mode = server.arg("mode");
String conn_int = server.arg("conn_int");

Serial.print("BRIGHTNESS: ");
Expand All @@ -65,6 +70,17 @@ void handle_save()
preferences.putUInt("bright", BRIGHTNESS);
Serial.println("PUT BRIGHT");
}
if(justLive != ""){
JUSTLIVE = std::atoi(justLive.c_str());
preferences.putUInt("justLive", JUSTLIVE);
Serial.println("PUT JUSTLIVE");
}
if(_mode != ""){
MODE = std::atoi(_mode.c_str());
preferences.putUInt("mode", MODE);
Serial.println("PUT MODE");
}

if (server.arg("ssid") != "")
{
WIFI_SSID = server.arg("ssid");
Expand Down Expand Up @@ -95,7 +111,7 @@ void handle_save()
cls();

//Reboot stick
start();
ESP.restart(); //Thanks to Babbit on vMix forums!
}

void handleReconnect(){
Expand Down
3 changes: 1 addition & 2 deletions src/a_GLOBAL/k_PLUGINMANAGER.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ class PluginManager {
}

void onSafe() {
//ledHat.onSafe();
//ledHat.onSafe();
}

};
24 changes: 24 additions & 0 deletions src/a_GLOBAL/webui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,20 @@ <h1>vMix M5Stack Tally Settings</h1>
<option value='11'>80%</option>
<option value='12'>100%</option>
</select>
</div>
<div>
Just Live:<br/>
<select id='drpJustLive' name='justLive'>
<option value='0'>False</option>
<option value='1'>True</option>
</select>
</div>
<div>
Mode:<br/>
<select id='drpMode' name='mode'>
<option value='0'>Text (SAFE, PRE, LIVE)</option>
<option value='1'>Tally Number</option>
</select>
</div>
<input type='submit' value='SAVE' id='btnSave' class='btn btn-primary'>
</form>
Expand All @@ -102,8 +116,12 @@ <h2>Reconnect to vMix</h2>
const btnReconnect = document.querySelector('#btnReconnect');
const drpBright = document.querySelector('#drpBright');
const ssidSelect = document.querySelector('#ssid');
const drpJustLive = document.querySelector('#drpJustLive');
const drpMode = document.querySelector('#drpMode');

drpBright.value = '" + bright + "';
drpJustLive.value = '" + justLive + "';
drpMode.value = '" + _mode + "';
btnSave.addEventListener('click', async function(e){
e.preventDefault();
let ssid = document.querySelector('#ssid').value;
Expand All @@ -115,6 +133,9 @@ <h2>Reconnect to vMix</h2>
const m_tally = document.querySelector('#m_tally').value;
const frmData = document.querySelector('#frmData');
const tally_num = document.querySelector('#tally_num').value;
const bright = drpBright.value;
const mode = drpMode.value;
const justLive = drpJustLive.value;
const conn_int = parseInt(document.querySelector('#conn_int').value, 10) || 0;
let formData = new FormData();
formData.append('ssid', ssid.trim());
Expand All @@ -123,6 +144,9 @@ <h2>Reconnect to vMix</h2>
formData.append('m_tally', m_tally.trim().replace(/[^0-9,]+/g, ''));
formData.append('tally_num', tally_num);
formData.append('conn_int', conn_int);
formData.append('bright', bright);
formData.append('mode', mode);
formData.append('justLive', justLive);
btnSave.setAttribute('disabled', '');
const res = await fetch('/save', {
method: 'POST',
Expand Down

0 comments on commit 3466de3

Please sign in to comment.