Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: M5ATOM Listener Client Doesn't Tally PGM/PVW if Source is assigned to any ATEM Aux Output #738

Open
allen-harris opened this issue Sep 15, 2024 · 10 comments
Assignees
Labels
bug Something isn't working

Comments

@allen-harris
Copy link

What happened?

I am using 6 M5Atom Listener Clients with TA monitoring a BMD ATEM 8K Constellation. If the source is assigned to an AUX out, it tallies the Aux colour but doesn't tally when on program or preview. It is as if the Aux out is prioritized over the program and preview tallies. If I unassign the source to the aux outputs, the problem goes away. The tallies are correct on the Producer and Sources & Devices pages. This is only an issue on the M5Atom.

Version

3.1.1

Distribution

Docker

OS

Linux

What browsers are you seeing the problem on?

Chrome

If applicable, What Listener Clients are You Using?

M5 Stick

TallyArbiter configuration

{
  "security": {
    "jwt_private_key": ""
  },
  "users": [
    {
      "username": "allen",
      "roles": "admin",
      "password": ""
    },
    {
      "username": "prod",
      "roles": "producer",
      "password": ""
    },
    {
      "username": "tally",
      "roles": "tally_viewer",
      "password": ""
    }
  ],
  "cloud_destinations": [],
  "cloud_keys": [],
  "device_actions": [],
  "device_sources": [
    {
      "deviceId": "e7187315",
      "sourceIdx": "0",
      "address": "1",
      "sourceId": "29052054",
      "id": "934389c7"
    },
    {
      "deviceId": "695dfb02",
      "sourceIdx": "0",
      "address": "2",
      "sourceId": "29052054",
      "id": "862fcca3"
    },
    {
      "deviceId": "6f15f803",
      "sourceIdx": "0",
      "address": "3",
      "sourceId": "29052054",
      "id": "96165151"
    },
    {
      "deviceId": "85007d8d",
      "sourceIdx": "0",
      "address": "4",
      "sourceId": "29052054",
      "id": "9f82a3a1"
    },
    {
      "deviceId": "0aa4ecf8",
      "sourceIdx": "0",
      "address": "5",
      "sourceId": "29052054",
      "id": "560d2b45"
    },
    {
      "deviceId": "0aa4ecf8",
      "sourceIdx": "0",
      "address": "7",
      "sourceId": "29052054",
      "id": "2e839185"
    },
    {
      "deviceId": "5f6d4611",
      "sourceIdx": "0",
      "address": "6",
      "sourceId": "29052054",
      "id": "ae34bcd2"
    },
    {
      "deviceId": "960fa853",
      "sourceIdx": "0",
      "address": "14",
      "sourceId": "29052054",
      "id": "fe422e41"
    },
    {
      "deviceId": "3d3def23",
      "sourceIdx": "0",
      "address": "15",
      "sourceId": "29052054",
      "id": "43549d42"
    },
    {
      "deviceId": "b3f60b55",
      "sourceIdx": "0",
      "address": "11",
      "sourceId": "29052054",
      "id": "22453837"
    },
    {
      "deviceId": "48bef713",
      "sourceIdx": "0",
      "address": "12",
      "sourceId": "29052054",
      "id": "44daaf6f"
    }
  ],
  "devices": [
    {
      "name": "TA-01",
      "enabled": true,
      "id": "e7187315"
    },
    {
      "name": "TA-02",
      "enabled": true,
      "id": "695dfb02"
    },
    {
      "name": "TA-03",
      "enabled": true,
      "id": "6f15f803"
    },
    {
      "name": "TA-04",
      "enabled": true,
      "id": "85007d8d"
    },
    {
      "name": "TA-05",
      "enabled": true,
      "id": "0aa4ecf8"
    },
    {
      "name": "TA-06",
      "enabled": true,
      "id": "5f6d4611"
    },
    {
      "name": "HD-1",
      "enabled": true,
      "id": "960fa853"
    },
    {
      "name": "HD-2",
      "enabled": true,
      "id": "3d3def23"
    },
    {
      "name": "HD-3",
      "enabled": true,
      "id": "c46d75ca"
    },
    {
      "name": "VT-X",
      "enabled": true,
      "id": "b3f60b55"
    },
    {
      "name": "VT-Y",
      "enabled": true,
      "id": "48bef713"
    }
  ],
  "sources": [
    {
      "data": {
        "ip": "192.168.241.42",
        "me_onair": [
          "1"
        ],
        "cut_bus_mode": "off"
      },
      "name": "CON 2",
      "sourceTypeId": "44b8bc4f",
      "reconnect": true,
      "enabled": true,
      "id": "29052054",
      "max_reconnects": 5,
      "connected": true
    }
  ],
  "tsl_clients": [],
  "tsl_clients_1secupdate": false,
  "bus_options": [
    {
      "id": "e393251c",
      "label": "Preview",
      "type": "preview",
      "color": "#00ff00",
      "priority": 50
    },
    {
      "id": "334e4eda",
      "label": "Program",
      "type": "program",
      "color": "#ff0000",
      "priority": 200
    },
    {
      "id": "12c8d699",
      "label": "Aux 1",
      "type": "aux",
      "color": "#7d48f9",
      "priority": 100,
      "visible": false
    },
    {
      "id": "12c8d689",
      "label": "Aux 2",
      "type": "aux",
      "color": "#c2450f",
      "priority": 100,
      "visible": false
    }
  ],
  "externalAddress": "http://0.0.0.0:4455/#/tally",
  "remoteErrorReporting": false,
  "uuid": "c61a3f65"
}

Relevant log output

THIS IS THE M5ATOM LISTENER CODE -- THERE WAS NO USEFUL LOG INFO

I HAVE MADE A CHANGE TO FORCE THE DISPLAY TO A PARTICULAR CAMERA NUMBER. THIS PROBLEM EXISTED BEFORE I MADE THIS CODE CHANGE.


#include <M5Atom.h>
#include <WiFi.h>
#include <WebSocketsClient.h>
#include <SocketIOclient.h>
#include <Arduino_JSON.h>
#include <PinButton.h>
#include <stdint.h>
#include <Arduino.h>
#include <WiFiManager.h>
#include <ArduinoOTA.h>
#include <ESPmDNS.h>
#include <Preferences.h>
#define DATA_PIN_LED 27 // NeoPixelArray

//Tally Arbiter Server
char tallyarbiter_host[40] = "tally.arbitor.net";
char tallyarbiter_port[6] = "4455";

//Local Default Camera Number. Used for local display only - does not impact function. Zero results in a single dot displayed.
int camNumber = 1;

// Name of the device - the 3 last bytes of the mac address will be appended to create a unique identifier for the server.
String listenerDeviceName = "TA01-";


// Set to true if you want to compile with the ability to show camera number during pvw and pgm
#define SHOW_CAMERA_NUMBER_DURING_PVW_AND_PGM false

// Enables the GPIO pinout
#define TALLY_EXTRA_OUTPUT true

//M5 variables
PinButton btnAction(39); //the "Action" button on the device - aka the front screen button for reset - push the front of the led display - ITS A BUTTON!
Preferences preferences;

/* USER CONFIG VARIABLES
    Change the following variables before compiling and sending the code to your device.
*/



//Set staticIP to 1 if you want the client to use a static IP address. Default is DHCP.
//Note that addresses entered here will need to be confirmed when WiFi Manager runs on client.
//
//local static IP config:
#define staticIP 0
#if staticIP == 1
IPAddress stationIP = IPAddress(192, 168, 1, 195);
IPAddress stationGW = IPAddress(192, 168, 1, 1);
IPAddress stationMask = IPAddress(255, 255, 255, 0);
#endif



//M5atom Access Point Password
//minimum of 8 characters
//leave empty for open Access Point
const char* AP_password ="";

// Global array to hold rotated numbers
int rotatedNumber[25];

/* END OF USER VARIABLES
 *  
 */

//Tally Arbiter variables
SocketIOclient socket;
WiFiManager WiFiManager; // global WiFiManager instance

JSONVar BusOptions;
JSONVar Devices;
JSONVar DeviceStates;
String DeviceId = "unassigned";
String DeviceName = "unassigned";
String ListenerType = "m5";
const unsigned long reconnectInterval = 5000;
unsigned long currentReconnectTime = 0;
bool isReconnecting = false;

#if TALLY_EXTRA_OUTPUT
const int led_program = 33; //Led for program on pin G33 - if TALLY_EXTRA_OUTPUT set to true at top of file G32 for grove pin 4
const int led_preview = 23; //Led for preview on pin G23 - if TALLY_EXTRA_OUTPUT set to true at top of file G26 for grove pin 3
const int led_aux = 19;     //Led for aux on pin G19 - if TALLY_EXTRA_OUTPUT set to true at top of file
#endif

String prevType = ""; // reduce display flicker by storing previous state
String actualType = "";
String actualColor = "";
int actualPriority = 0;
long colorNumber = 0;

// default color values
int RGB_COLOR_WHITE = 0xffffff;
int RGB_COLOR_DIMWHITE = 0x555555;
int RGB_COLOR_WARMWHITE = 0xFFEBC8;
int RGB_COLOR_DIMWARMWHITE = 0x877D5F;
int RGB_COLOR_BLACK = 0x000000;
int RGB_COLOR_RED = 0xff0000;
int RGB_COLOR_ORANGE = 0xa5ff00;
int RGB_COLOR_YELLOW = 0xffff00;
int RGB_COLOR_DIMYELLOW = 0x555500;
int RGB_COLOR_GREEN = 0x008800; // toning this down as the green is way brighter than the other colours
int RGB_COLOR_BLUE = 0x0000ff;
int RGB_COLOR_PURPLE = 0x008080;

int numbercolor = RGB_COLOR_WARMWHITE;

int flashcolor[] = {RGB_COLOR_WHITE, RGB_COLOR_WHITE};
int offcolor[] = {RGB_COLOR_BLACK, numbercolor};
int badcolor[] = {RGB_COLOR_BLACK, RGB_COLOR_RED};
int readycolor[] = {RGB_COLOR_BLACK, RGB_COLOR_GREEN};
int alloffcolor[] = {RGB_COLOR_BLACK, RGB_COLOR_BLACK};
int wificolor[] = {RGB_COLOR_BLACK, RGB_COLOR_BLUE};
int infocolor[] = {RGB_COLOR_BLACK, RGB_COLOR_ORANGE};

//this is the array that stores the number layout
int number[17][25] = {{
    0, 0, 0, 0, 0,
    0, 0, 0, 0, 0,
    0, 0, 0, 0, 1,
    0, 0, 0, 0, 0,
    0, 0, 0, 0, 0
  },
  { 0, 0, 0, 0, 0,
    0, 0, 0, 0, 1,
    1, 1, 1, 1, 1,
    0, 1, 0, 0, 1,
    0, 0, 0, 0, 0
  },
  { 0, 0, 0, 0, 0,
    1, 1, 1, 0, 1,
    1, 0, 1, 0, 1,
    1, 0, 1, 1, 1,
    0, 0, 0, 0, 0
  },
  { 0, 0, 0, 0, 0,
    1, 1, 1, 1, 1,
    1, 0, 1, 0, 1,
    1, 0, 1, 0, 1,
    0, 0, 0, 0, 0
  },
  { 0, 0, 0, 0, 0,
    1, 1, 1, 1, 1,
    0, 0, 1, 0, 0,
    1, 1, 1, 0, 0,
    0, 0, 0, 0, 0
  },
  { 0, 0, 0, 0, 0,
    1, 0, 1, 1, 1,
    1, 0, 1, 0, 1,
    1, 1, 1, 0, 1,
    0, 0, 0, 0, 0
  },
  { 0, 0, 0, 0, 0,
    1, 0, 1, 1, 1,
    1, 0, 1, 0, 1,
    1, 1, 1, 1, 1,
    0, 0, 0, 0, 0
  },
  { 0, 0, 0, 0, 0,
    1, 1, 0, 0, 0,
    1, 0, 1, 0, 0,
    1, 0, 0, 1, 1,
    0, 0, 0, 0, 0
  },
  { 0, 0, 0, 0, 0,
    1, 1, 1, 1, 1,
    1, 0, 1, 0, 1,
    1, 1, 1, 1, 1,
    0, 0, 0, 0, 0
  },
  { 0, 0, 0, 0, 0,
    1, 1, 1, 1, 1,
    1, 0, 1, 0, 1,
    1, 1, 1, 0, 1,
    0, 0, 0, 0, 0
  },
  { 1, 1, 1, 1, 1,
    1, 0, 0, 0, 1,
    1, 1, 1, 1, 1,
    0, 0, 0, 0, 0,
    1, 1, 1, 1, 1
  },
  { 0, 0, 0, 0, 0,
    1, 1, 1, 1, 1,
    0, 0, 0, 0, 0,
    1, 1, 1, 1, 1,
    0, 0, 0, 0, 0
  },
  { 1, 1, 1, 0, 1,
    1, 0, 1, 0, 1,
    1, 0, 1, 1, 1,
    0, 0, 0, 0, 0,
    1, 1, 1, 1, 1
  },
  { 1, 1, 1, 1, 1,
    1, 0, 1, 0, 1,
    1, 0, 1, 0, 1,
    0, 0, 0, 0, 0,
    1, 1, 1, 1, 1
  },
  { 1, 1, 1, 1, 1,
    0, 0, 1, 0, 0,
    1, 1, 1, 0, 0,
    0, 0, 0, 0, 0,
    1, 1, 1, 1, 1
  },
  { 1, 0, 1, 1, 1,
    1, 0, 1, 0, 1,
    1, 1, 1, 0, 1,
    0, 0, 0, 0, 0,
    1, 1, 1, 1, 1
  },
  { 1, 0, 1, 1, 1,
    1, 0, 1, 0, 1,
    1, 1, 1, 1, 1,
    0, 0, 0, 0, 0,
    1, 1, 1, 1, 1
  },
};

// this array stores all the icons for the display
int icons[13][25] = {
  { 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1,
    1, 1, 1, 1, 1,
    1, 1, 1, 1, 1,
    1, 1, 1, 1, 1
  }, // full blank
  { 0, 0, 1, 1, 1,
    0, 1, 0, 0, 0,
    1, 0, 0, 1, 1,
    1, 0, 1, 0, 0,
    1, 0, 1, 0, 1
  }, // wifi 3 rings
  { 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0,
    0, 0, 0, 1, 1,
    0, 0, 1, 0, 0,
    0, 0, 1, 0, 1
  }, // wifi 2 rings
  { 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0,
    0, 0, 0, 0, 0,
    0, 0, 0, 0, 0,
    0, 0, 0, 0, 1
  }, // wifi 1 ring
  { 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0,
    0, 0, 1, 0, 0,
    0, 0, 0, 0, 0,
    0, 0, 0, 0, 0
  }, // reassign 1
  { 0, 0, 0, 0, 0,
    0, 1, 1, 1, 0,
    0, 1, 0, 1, 0,
    0, 1, 1, 1, 0,
    0, 0, 0, 0, 0
  }, // reassign 2
  { 1, 1, 1, 1, 1,
    1, 0, 0, 0, 1,
    1, 0, 0, 0, 1,
    1, 0, 0, 0, 1,
    1, 1, 1, 1, 1
  }, // reassign 3
  { 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0,
    0, 0, 1, 0, 0,
    0, 0, 0, 0, 0,
    0, 0, 0, 0, 0
  }, // setup 1
  { 0, 0, 0, 0, 0,
    0, 0, 1, 0, 0,
    0, 1, 0, 1, 0,
    0, 0, 1, 0, 0,
    0, 0, 0, 0, 0
  }, // setup 2
  { 0, 0, 1, 0, 0,
    0, 0, 0, 0, 0,
    1, 0, 0, 0, 1,
    0, 0, 0, 0, 0,
    0, 0, 1, 0, 0
  }, // setup 3
  { 1, 0, 0, 0, 1,
    0, 1, 0, 1, 0,
    0, 0, 1, 0, 0,
    0, 1, 0, 1, 0,
    1, 0, 0, 0, 1
  }, // error
  { 0, 1, 0, 0, 0,
    0, 0, 1, 0, 0,
    0, 0, 0, 1, 0,
    0, 0, 0, 0, 1,
    0, 0, 0, 1, 0
  }, // good
  { 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0,
    0, 0, 0, 0, 0,
    0, 0, 0, 0, 0,
    0, 0, 0, 0, 0
  }, // no icon
};

// Logger - logs to serial number
void logger(String strLog, String strType) {
  if (strType == "info") {
    Serial.println(strLog);
  }
  else {
    Serial.println(strLog);
  }
}
// Set Device name
void setDeviceName(){
  for (int i = 0; i < Devices.length(); i++) {
    if (JSON.stringify(Devices[i]["id"]) == "\"" + DeviceId + "\"") {
      String strDevice = JSON.stringify(Devices[i]["Type"]);
      DeviceName = strDevice.substring(1, strDevice.length() - 1);
      break;
    }
  }
  preferences.begin("tally-arbiter", false);
  preferences.putString("devicename", DeviceName);
  preferences.putString("deviceid", DeviceId);
  preferences.putInt("camNumber",camNumber);
  preferences.end();
  logger("-------------------------------------------------", "info-quiet");
  logger("DeviceName: " + String(DeviceName), "info-quiet");
  logger("DeviceId: " + String(DeviceId), "info-quiet");
  logger("-------------------------------------------------", "info-quiet");
}

//---------------------------------------------------------------
//HERE IS THE MAIN LED DRAWING ROUTINE aka drawNumber
void drawNumber(int arr[], int colors[]) {
  for (int i = 0; i < 25; i++)
  {
    //Serial.println("i: " + String(i) + " color: " + String(colors[arr[i]]));
    M5.dis.drawpix(i, colors[arr[i]]);
  }
}

void drawMultiple(int arr[], int colors[], int param_times, int delays) {
  for (int times = param_times; times > 0; times--) {
    drawNumber(arr, colors);
    delay(delays);
  }
}
//---------------------------------------------------------------

// Determine if the device is currently in preview, program, or both
void evaluateMode() {
  if(actualType != prevType) {
    //M5.dis.clear();
    actualColor.replace("#", "");
    String hexstring = actualColor;
    long colorNumber = (long) strtol( &hexstring[1], NULL, 16);
 // This order is to compensate for Matrix needing grb.
    int r = strtol(hexstring.substring(3, 5).c_str(), NULL, 16);
    int g = strtol(hexstring.substring(1, 3).c_str(), NULL, 16);
    int b = strtol(hexstring.substring(5).c_str(), NULL, 16);
    
    if (actualType != "") {
     int backgroundColorhex = (g << 16) | (r << 8) | b; // Swap positions of RGB to GRB conversion
      int currColor[] = {backgroundColorhex, numbercolor};
      logger("Current color: " + String(backgroundColorhex), "info");
      //logger("Current camNumber: " + String(camNumber), "info");
#if SHOW_CAMERA_NUMBER_DURING_PVW_AND_PGM
      // If you want the camera number displayed during Pgm and Pvw, change the variable at the top of the file
      drawNumber(&rotatedNumber[camNumber], currColor);
#else
      drawNumber(icons[12], currColor);
#endif
    } else {
      drawNumber(rotatedNumber, offcolor);
    }

    #if TALLY_EXTRA_OUTPUT
    if (actualType == "\"program\"") {
      digitalWrite(led_program, HIGH);
      digitalWrite (led_preview, LOW);
      digitalWrite (led_aux, LOW);
    } else if (actualType == "\"preview\"") {
      digitalWrite(led_program, LOW);
      digitalWrite (led_preview, HIGH);
      digitalWrite (led_aux, LOW);
    } else if (actualType == "\"aux\"") {
      digitalWrite(led_program, LOW);
      digitalWrite (led_preview, LOW);
      digitalWrite (led_aux, HIGH);
    } else {
      digitalWrite(led_program, LOW);
      digitalWrite (led_preview, LOW);
      digitalWrite (led_aux, LOW);
    }
    #endif

    logger("Device is in " + actualType + " (color " + actualColor + " priority " + String(actualPriority) + ")", "info");
    // This is a hack to compensate for the Matrix needing GRB.
    logger(" r: " + String(g) + " g: " + String(r) + " b: " + String(b), "info");

    prevType = actualType;
  }  
}

void startReconnect() {
  if (!isReconnecting)
  {
    isReconnecting = true;
    currentReconnectTime = millis();
  }
}

void connectToServer() {
  logger("---------------------------------", "info-quiet");
  logger("Connecting to Tally Arbiter host: " + String(tallyarbiter_host), "info-quiet");
  socket.onEvent(socket_event);
  socket.begin(tallyarbiter_host, atol(tallyarbiter_port));
  logger("---------------------------------", "info-quiet");
}

// Here are all the socket listen events - messages sent from Tally Arbiter to the M5

void socket_Disconnected(const char * payload, size_t length) {
  logger("Disconnected from server, will try to re-connect: " + String(payload), "info-quiet");
  Serial.println("disconnected, going to try to reconnect");
  startReconnect();
}

void ws_emit(String event, const char *payload = NULL) {
  if (payload) {
    String msg = "[\"" + event + "\"," + payload + "]";
    Serial.println(msg);
    socket.sendEVENT(msg);
  } else {
    String msg = "[\"" + event + "\"]";
    Serial.println(msg);
    socket.sendEVENT(msg);
  }
}

String strip_quot(String str) {
  if (str[0] == '"') {
    str.remove(0, 1);
  }
  if (str.endsWith("\"")) {
    str.remove(str.length()-1, 1);
  }
  return str;
}

void socket_event(socketIOmessageType_t type, uint8_t * payload, size_t length) {
  String eventMsg = "";
  String eventType = "";
  String eventContent = "";

  switch (type) {
    case sIOtype_CONNECT:
      socket_Connected((char*)payload, length);
      break;

    case sIOtype_DISCONNECT:
      socket_Disconnected((char*)payload, length);
      break;
    case sIOtype_ACK:
    case sIOtype_ERROR:
    case sIOtype_BINARY_EVENT:
    case sIOtype_BINARY_ACK:
      // Not handled
      break;

    case sIOtype_EVENT:
      eventMsg = (char*)payload;
      eventType = eventMsg.substring(2, eventMsg.indexOf("\"",2));
      eventContent = eventMsg.substring(eventType.length() + 4);
      eventContent.remove(eventContent.length() - 1);

      logger("Got event '" + eventType + "', data: " + eventContent, "VERBOSE");

      if (eventType == "bus_options") socket_BusOptions(eventContent);
      if (eventType == "deviceId") socket_DeviceId(eventContent);
      if (eventType == "devices") socket_Devices(eventContent);
      if (eventType == "device_states") socket_DeviceStates(eventContent);
      if (eventType == "flash") socket_Flash();
      if (eventType == "reassign") socket_Reassign(eventContent);

      break;

    default:
      break;
  }
}

void socket_Reassign(String payload) {
  logger("Socket Reassign: " + String(payload), "info-quiet");
  Serial.println(payload);
  String oldDeviceId = payload.substring(0, payload.indexOf(','));
  String newDeviceId = payload.substring(oldDeviceId.length()+1);
  newDeviceId = newDeviceId.substring(0, newDeviceId.indexOf(','));
  oldDeviceId = strip_quot(oldDeviceId);
  newDeviceId = strip_quot(newDeviceId);
  
  String reassignObj = "{\"oldDeviceId\": \"" + oldDeviceId + "\", \"newDeviceId\": \"" + newDeviceId + "\"}";
  char charReassignObj[1024];
  strcpy(charReassignObj, reassignObj.c_str());
  ws_emit("listener_reassign_object", charReassignObj);
  ws_emit("devices");
  
  // Flash 2 times
  drawNumber(icons[1], alloffcolor);
  delay(200);
  drawNumber(icons[4], readycolor);
  delay(300);
  drawNumber(icons[1], alloffcolor);
  delay(200);
  drawNumber(icons[5], readycolor);
  delay(300);
  drawNumber(icons[1], alloffcolor);
  delay(200);
  drawNumber(icons[6], readycolor);
  delay(300);
  drawNumber(icons[1], alloffcolor);
  delay(200);

  logger("newDeviceId: " + newDeviceId, "info-quiet");
  DeviceId = newDeviceId;
  preferences.begin("tally-arbiter", false);
  preferences.putString("deviceid", newDeviceId);
  preferences.end();
  setDeviceName();
}
void socket_Flash() {
  //flash the screen white 3 times
  logger("The device flashed.", "info-quiet");
  for (int k = 0; k < 3; k++) {
    //Matrix Off
    drawNumber(icons[1], alloffcolor);
    delay(100);

    //Matrix On
    drawNumber(icons[1], flashcolor);
    delay(100);
  }
  //Matrix Off
  drawNumber(icons[1], alloffcolor);
  delay(100);
  //then resume normal operation
  evaluateMode();
}

void socket_Connected(const char * payload, size_t length) {
  logger("---------------------------------", "info-quiet");
  logger("Connected to Tally Arbiter host: " + String(tallyarbiter_host), "info-quiet");
  isReconnecting = false;
  String deviceObj = "{\"deviceId\": \"" + DeviceId + "\", \"listenerType\": \"" + listenerDeviceName.c_str() + "\", \"canBeReassigned\": true, \"canBeFlashed\": true, \"supportsChat\": false }";
  logger("deviceObj = " + String(deviceObj), "info-quiet");
  logger("DeviceId = " + String(DeviceId), "info-quiet");
  char charDeviceObj[1024];
  strcpy(charDeviceObj, deviceObj.c_str());
  ws_emit("listenerclient_connect", charDeviceObj);
  logger("charDeviceObj = " + String(charDeviceObj), "info-quiet");
  logger("---------------------------------", "info-quiet");
}

void socket_BusOptions(String payload) {
  //logger("Socket Message BusOptions: " + String(payload), "info-quiet");
  BusOptions = JSON.parse(payload);
}

void socket_Devices(String payload) {
  //logger("Socket Message Devices: " + String(payload), "info-quiet");
  Devices = JSON.parse(payload);
  setDeviceName();
}

void socket_DeviceId(String payload) {
  //logger("Socket Message DeviceId: " + String(payload), "info-quiet");
  DeviceId = strip_quot(String(payload));
  setDeviceName();
}

void socket_DeviceStates(String payload) {
  //logger("Socket Message DeviceStates: " + String(payload), "VERBOSE");
  DeviceStates = JSON.parse(payload);
  processTallyData();
}

String getBusTypeById(String busId) {
  for (int i = 0; i < BusOptions.length(); i++) {
    if (JSON.stringify(BusOptions[i]["id"]) == busId) {
      return JSON.stringify(BusOptions[i]["type"]);
    }
  }

  return "invalid";
}

String getBusColorById(String busId) {
  for (int i = 0; i < BusOptions.length(); i++) {
    if (JSON.stringify(BusOptions[i]["id"]) == busId) {
      return JSON.stringify(BusOptions[i]["color"]);
    }
  }

  return "invalid";
}

int getBusPriorityById(String busId) {
  for (int i = 0; i < BusOptions.length(); i++) {
    if (JSON.stringify(BusOptions[i]["id"]) == busId) {
      return (int) JSON.stringify(BusOptions[i]["priority"]).toInt();
    }
  }

  return 0;
}

void processTallyData() {
  bool typeChanged = false;
  for (int i = 0; i < DeviceStates.length(); i++) {
    if (DeviceStates[i]["sources"].length() > 0) {
      typeChanged = true;
      actualType = getBusTypeById(JSON.stringify(DeviceStates[i]["busId"]));
      actualColor = getBusColorById(JSON.stringify(DeviceStates[i]["busId"]));
      actualPriority = getBusPriorityById(JSON.stringify(DeviceStates[i]["busId"]));
    }
  }
  if(!typeChanged) {
    actualType = "";
    actualColor = "";
    actualPriority = 0;
  }
  evaluateMode();
}

// A whole ton of WiFiManager stuff, first up, here is the Paramaters
WiFiManagerParameter* custom_taServer;
WiFiManagerParameter* custom_taPort;
//WiFiManagerParameter* custom_tashownumbersduringtally;

void connectToNetwork() {
  // allow for static IP assignment instead of DHCP if stationIP is defined as something other than 0.0.0.0
  #if staticIP == 1
  if (stationIP != IPAddress(0, 0, 0, 0))
  {
    WiFiManager.setSTAStaticIPConfig(stationIP, stationGW, stationMask); // optional DNS 4th argument 
  }
  #endif
  
  WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP
  logger("Connecting to SSID: " + String(WiFi.SSID()), "info");

  //reset settings - wipe credentials for testing
  //WiFiManager.resetSettings();

  //add TA fields
  custom_taServer = new WiFiManagerParameter("taHostIP", "Tally Arbiter Server", tallyarbiter_host, 40);
  custom_taPort = new WiFiManagerParameter("taHostPort", "Port", tallyarbiter_port, 6);
 // custom_tashownumbersduringtally = new WiFiManagerParameter("tashownumbersduringtally", "Show Number During Tally (true/false)", SHOW_CAMERA_NUMBER_DURING_PVW_AND_PGM, 6);

  WiFiManager.addParameter(custom_taServer);
  WiFiManager.addParameter(custom_taPort);
  //WiFiManager.addParameter(custom_tashownumbersduringtally);

  WiFiManager.setSaveParamsCallback(saveParamCallback);

  // custom menu via array or vector
  std::vector<const char *> menu = {"wifi","param","info","sep","restart","exit"};
  WiFiManager.setMenu(menu);

  // set dark theme
  WiFiManager.setClass("invert");

  WiFiManager.setConfigPortalTimeout(120); // auto close configportal after n seconds

  bool res;
  
  res = WiFiManager.autoConnect(listenerDeviceName.c_str(),AP_password);

  if (!res) {
    logger("Failed to connect", "error");
    drawNumber(icons[10], badcolor); //display failed mark
    // ESP.restart();
  } else {
    //if you get here you have connected to the WiFi
    logger("connected...yay :)", "info");

    // Flash screen if connected to wifi.
    drawNumber(icons[3], wificolor); //1 ring
    delay(500);
    drawNumber(icons[2], wificolor); //2 rings
    delay(500);
    drawNumber(icons[1], wificolor); //3 rings
    delay(500);
    drawNumber(icons[11], readycolor); //display okay mark
    delay(400);
    
    //TODO: fix MDNS discovery
    /*

    int nrOfServices = MDNS.queryService("tally-arbiter", "tcp");

    if (nrOfServices == 0) {
      logger("No server found.", "error");
    } else {
      logger("Number of servers found: ", "info");
      Serial.print(nrOfServices);
     
      for (int i = 0; i < nrOfServices; i=i+1) {
 
        Serial.println("---------------");
       
        Serial.print("Hostname: ");
        Serial.println(MDNS.hostname(i));
 
        Serial.print("IP address: ");
        Serial.println(MDNS.IP(i));
 
        Serial.print("Port: ");
        Serial.println(MDNS.port(i));
 
        Serial.println("---------------");
      }
    }
    */
  }
}

String getParam(String name) {
  //read parameter from server, for customhmtl input
  String value;
  if (WiFiManager.server->hasArg(name)) {
    value = WiFiManager.server->arg(name);
  }
  return value;
}


void saveParamCallback() {
  logger("[CALLBACK] saveParamCallback fired", "info-quiet");
  logger("PARAM tally Arbiter Server = " + getParam("taHostIP"), "info-quiet");
  String str_taHost = getParam("taHostIP");
  String str_taPort = getParam("taHostPort");
  String str_tashownumbersduringtally = getParam("tashownumbersduringtally");
  //saveEEPROM(); // this was commented out as prefrences is now being used in place
  logger("Saving new TallyArbiter host", "info-quiet");
  logger(str_taHost, "info-quiet");
  preferences.begin("tally-arbiter", false);
  preferences.putString("taHost", str_taHost);
  preferences.putString("taPort", str_taPort);
  preferences.putString("tashownumbersduringtally", str_tashownumbersduringtally);
  preferences.end();

}

// --------------------------------------------------------------------------------------------------------------------
// Setup is the pre-loop running program

void setup() {
  Serial.begin(115200);
  while (!Serial);
  logger("Initializing M5-Atom.", "info-quiet");
  
  //Save battery by turning off BlueTooth
  btStop();

    // Initialize IMU (MPU6886) The rotation sensor
  if (M5.IMU.Init() != 0) {
    Serial.println("MPU6886 initialization failed!");
    while (1) delay(100);
  } else {
    Serial.println("MPU6886 initialization successful!");
  }

  // Append last three pairs of MAC to listenerDeviceName to make it some what unique
  byte mac[6];              // the MAC address of your Wifi shield
  WiFi.macAddress(mac);
  listenerDeviceName = listenerDeviceName + String(mac[3], HEX) + String(mac[4], HEX) + String(mac[5], HEX);
  logger("Listener device name: " + listenerDeviceName, "info");

  // Set WiFi hostname
  WiFiManager.setHostname ((const char *) listenerDeviceName.c_str());

  M5.begin(true, false, true);
  delay(50);
  M5.dis.drawpix(0, 0xf00000);

  // blanks out the screen
  drawNumber(icons[0], alloffcolor);
  delay(100); //wait 100ms before moving on

  //do startup animation
  drawNumber(icons[7], infocolor);
  delay(400);
  drawNumber(icons[8], infocolor);
  delay(400);
  drawNumber(icons[9], infocolor);
  delay(400);
  
  connectToNetwork(); //starts Wifi connection

  // Load from non-volatile memory
  preferences.begin("tally-arbiter", false);

    if (preferences.getString("deviceid").length() > 0) {
      DeviceId = preferences.getString("deviceid");
      //DeviceId = "unassigned";
    }
    if (preferences.getString("devicename").length() > 0) {
      DeviceName = preferences.getString("devicename");
      //DeviceName = "unassigned";
    }

    if(preferences.getString("taHost").length() > 0){
      String newHost = preferences.getString("taHost");
      logger("Setting TallyArbiter host as " + newHost, "info-quiet");
      newHost.toCharArray(tallyarbiter_host, 40);
    }
    if(preferences.getString("taPort").length() > 0){
      String newPort = preferences.getString("taPort");
      logger("Setting TallyArbiter port as " + newPort, "info-quiet");
      newPort.toCharArray(tallyarbiter_port, 6);
    }
  // camNumber = preferences.getInt("camNumber"); // Get camera from memory
//    SHOW_CAMERA_NUMBER_DURING_PVW_AND_PGM = preferences.getInt("tashownumbersduringtally"); // Get prefrence for Showing numbers during tally (default false)

  preferences.end();

  //debug
    char message[200]; // Adjust the size as needed
    sprintf(message, "After the preferences.end TA Host is: %s TA Port is: %s", tallyarbiter_host, tallyarbiter_port);
    logger(message, "info-quiet");


  ArduinoOTA.setHostname(listenerDeviceName.c_str());
  ArduinoOTA.setPassword("tallyarbiter");
  ArduinoOTA
    .onStart([]() {
      String type;
      if (ArduinoOTA.getCommand() == U_FLASH)
        type = "sketch";
      else // U_SPIFFS
        type = "filesystem";

      // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
      Serial.println("Start updating " + type);
    })
    .onEnd([]() {
      Serial.println("\nEnd");
    })
    .onProgress([](unsigned int progress, unsigned int total) {
      Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
    })
    .onError([](ota_error_t error) {
      Serial.printf("Error[%u]: ", error);
      if (error == OTA_AUTH_ERROR) logger("Auth Failed", "error");
      else if (error == OTA_BEGIN_ERROR) logger("Begin Failed", "error");
      else if (error == OTA_CONNECT_ERROR) logger("Connect Failed", "error");
      else if (error == OTA_RECEIVE_ERROR) logger("Receive Failed", "error");
      else if (error == OTA_END_ERROR) logger("End Failed", "error");
    });

  ArduinoOTA.begin();

  #if TALLY_EXTRA_OUTPUT
  // Enable external led for program trigger
  pinMode(led_program, OUTPUT);
  digitalWrite(led_program, HIGH);
  pinMode(led_preview, OUTPUT);
  digitalWrite(led_preview, HIGH);
  pinMode(led_aux, OUTPUT);
  digitalWrite(led_aux, HIGH);
  #endif  
  
  connectToServer();
  delay (100);
}
// --------------------------------------------------------------------------------------------------------------------

// Functions for Screen Rotation for AutoRotation

void rotate90(int source[25], int dest[25]) {
    for (int i = 0; i < 5; ++i) {
        for (int j = 0; j < 5; ++j) {
            dest[j * 5 + (4 - i)] = source[i * 5 + j];
        }
    }
}

void rotate180(int source[25], int dest[25]) {
    for (int i = 0; i < 25; ++i) {
        dest[24 - i] = source[i];
    }
}

void rotate270(int source[25], int dest[25]) {
    for (int i = 0; i < 5; ++i) {
        for (int j = 0; j < 5; ++j) {
            dest[(4 - j) * 5 + i] = source[i * 5 + j];
        }
    }
}

// Screen Update Routine
void updateDisplayBasedOnOrientation(float accX, float accY, float accZ) {

  //Serial.print("Screen Orientation: ");
  //Serial.print("Accel X: ");
  //Serial.print(accX);
  //Serial.print(" Y: ");
  //Serial.print(accY);
  //Serial.print(" Z: ");
  //Serial.print(accZ);
  //Serial.println(" m/s^2");
  
  // 0.8 is used as a deadband control

  // Check for USB right (Normal orientation)
  if (accX > 0.8) {
  //  Serial.println("USB Right - 0 degrees (normal)");
    memcpy(rotatedNumber, number[camNumber], sizeof(int) * 25);
  } 
  // Check for USB left (180 degrees)
  else if (accX < -0.8) {
  //  Serial.println("USB Left - 180 degrees");
    rotate180(number[camNumber], rotatedNumber);
  } 
  // Check for USB up
  else if (accY > 0.8) {
  //  Serial.println("USB Up - 90 degrees");
    rotate90(number[camNumber], rotatedNumber);
  } 
  // Check for USB down
  else if (accY < -0.8) {
  //  Serial.println("USB Down - 270 degrees");
    rotate270(number[camNumber], rotatedNumber);
  } 
  else {
  //  Serial.println("Flat or undefined orientation");
    memcpy(rotatedNumber, number[camNumber], sizeof(int) * 25);
  }

}


// --------------------------------------------------------------------------------------------------------------------
// This is the main program loop
void loop(){
  socket.loop();
  if (M5.Btn.wasPressed()){
  //   // Switch action below
  //   if (camNumber < 16){
  //     camNumber++;  
  //       preferences.begin("tally-arbiter", false);          // Open Preferences with no read-only access
  //       preferences.putInt("camNumber", camNumber);      // Save camera number
  //       delay(100);                                         // Introduce a short delay before closing
  //       preferences.end();                                  // Close the Preferences after saving
  //   } else {
  //     camNumber = 0;
  //       preferences.begin("tally-arbiter", false);          // Open Preferences with no read-only access
  //       preferences.putInt("camNumber", camNumber);      // Save camera number
  //       delay(100);                                         // Introduce a short delay before closing
  //       preferences.end();                                  // Close the Preferences after saving
  //   }
    drawNumber(rotatedNumber, offcolor);

    // Lets get some info sent out the serial connection for debugging
    logger("---------------------------------", "info-quiet");
    logger("Button Pressed.", "info-quiet");
    logger("M5Atom IP Address: " + WiFi.localIP().toString(), "info-quiet");
    logger("Tally Arbiter Server: " + String(tallyarbiter_host), "info-quiet");
    logger("Device ID: " + String(DeviceId), "info-quiet");
    logger("Device Name: " + String(DeviceName), "info-quiet");
    logger("Cam Number: " + String(camNumber), "info-quiet");
    logger("---------------------------------", "info-quiet");
  }
    
  // Is WiFi reset triggered?
  if (M5.Btn.pressedFor(5000)){
    WiFiManager.resetSettings();
    ESP.restart();
  }

  // handle reconnecting if disconnected
  if (isReconnecting)
  {
  unsigned long currentTime = millis();
    
    if (currentTime - currentReconnectTime >= reconnectInterval)
    {
      Serial.println("trying to re-connect with server");
      connectToServer();
      currentReconnectTime = millis();
    }
  }

//#if AUTO_ORIENTATION
  // Check orientation and autorotate the screen

    // Orientation Sensor Data variables
  float accX, accY, accZ;

  // Read acceleration data
  M5.IMU.getAccelData(&accX, &accY, &accZ);

  // Run the rotation change to the variabne rotatedNumber
  updateDisplayBasedOnOrientation(accX, accY, accZ);
    
//  #else
//  #endif
  
  delay(100);
  M5.update();
}
// --------------------------------------------------------------------------------------------------------------------

Error stacktrace (if applicable)

No Error
@allen-harris allen-harris added the bug Something isn't working label Sep 15, 2024
Copy link

Hello there allen-harris 👋

Welcome to TallyArbiter!

Thank you for opening your first issue for the Tally Arbiter project. Tally Arbiter fosters an open and welcoming environment for all our contributors. Please adhere to our Code Of Conduct.

If you have more to contribute to this issue, please comment down below! We will try to get back to you as soon as we can.

@josephdadams
Copy link
Owner

Does it happen on TA v3.0.10?

@allen-harris
Copy link
Author

Does it happen on TA v3.0.10?

I believe it is the same on 3.0.10. I was struggling with 3.0.10, I upgraded to 3.1, then 3.1.1 to see if the problem went away.. I am in production right now, but I will try 3.0.10 tomorrow to confirm if the same issue is there.

@josephdadams
Copy link
Owner

It would help narrow down. I implanted a different way of calculating tally as what was previously in place did not allow for multiple aux busses.

@josephdadams
Copy link
Owner

(In 3.1.0/.1)

@allen-harris
Copy link
Author

I just spun up 3.0.10 and it is working correctly.

@josephdadams
Copy link
Owner

Ok. This prob means we will need to fix all listener clients that reference an aux bus. I will try to look at it sometime. Feel free to look at the git history to see what I changed if you want to help.

@allen-harris
Copy link
Author

I will take a look.

@allen-harris
Copy link
Author

Looking through the history, this issue is mentioned here.
#669 (comment) in 3.1

@josephdadams
Copy link
Owner

Makes sense. This was merged June 22 and v3.0.10 was released in April.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants