12 Commits

6 changed files with 313 additions and 31 deletions
@@ -0,0 +1,116 @@
#include <WiFi.h>
#include <esp_wifi.h>
#include <esp_now.h>
const int camPin = 16; // Any GPIO pin
const int pressTime = 250; // 0.25s HIGH pulse
const int gapTime = 250; // Gap between pulses for double press
void doSinglePress() {
digitalWrite(camPin, HIGH);
delay(pressTime);
digitalWrite(camPin, LOW);
}
void doDoublePress() {
doSinglePress();
delay(gapTime);
doSinglePress();
}
// Must match the sender struct exactly
typedef struct struct_message_send {
int boardID;
bool buttonSTART;
bool buttonSTARTforced;
bool buttonPAUSE;
bool buttonPIT;
bool buttonPIThold;
bool buttonRESET;
bool buttonREDTEAM;
bool buttonREDTEAMtapout;
bool buttonBLUETEAM;
bool buttonBLUETEAMtapout;
} struct_message_send;
struct_message_send incoming;
// Recording state variable
bool RECORDING = false;
bool lastRECORDING = false; // for change detection
// LED blink timing
unsigned long lastBlink = 0;
bool ledState = false;
// ESP-NOW receive callback
void onDataRecv(const esp_now_recv_info *info, const uint8_t *data, int len) {
if (len != sizeof(incoming)) return;
memcpy(&incoming, data, sizeof(incoming));
// Only accept packets from boardID 0 (referee remote)
if (incoming.boardID != 0) return;
// START or STARTforced → RECORDING = true
if (incoming.buttonSTART || incoming.buttonSTARTforced) {
RECORDING = true;
}
// RESET → RECORDING = false
if (incoming.buttonRESET) {
RECORDING = false;
}
}
void setup() {
pinMode(camPin, OUTPUT);
digitalWrite(camPin, LOW); // Idle state
Serial.begin(115200);
delay(200);
Serial.println("Extra Receiver Ready");
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
WiFi.mode(WIFI_STA);
WiFi.setSleep(false);
if (esp_now_init() != ESP_OK) {
Serial.println("ESP-NOW init failed!");
return;
}
esp_now_register_recv_cb(onDataRecv);
}
void loop() {
// Detect changes in RECORDING state
if (RECORDING != lastRECORDING) {
// Debug print
Serial.print("RECORDING state changed: ");
Serial.println(RECORDING ? "TRUE" : "FALSE");
// Trigger camera once on state change
doSinglePress();
lastRECORDING = RECORDING;
}
// Non-blocking LED blink when RECORDING = true
if (RECORDING) {
unsigned long now = millis();
if (now - lastBlink >= 1000) { // blink every 1s
lastBlink = now;
ledState = !ledState;
digitalWrite(LED_BUILTIN, ledState);
}
} else {
// Ensure LED is off when not recording
digitalWrite(LED_BUILTIN, LOW);
ledState = false;
}
}
@@ -25,9 +25,11 @@ void openPITmanually() {
void arenaLIGHT() { void arenaLIGHT() {
if (switchLIGHT.on()) { if (switchLIGHT.on()) {
digitalWrite(LIGHT_PIN, LOW); digitalWrite(LIGHT_PIN, LOW);
digitalWrite(LIGHT_PIN2, LOW);
} }
else { else {
digitalWrite(LIGHT_PIN, HIGH); digitalWrite(LIGHT_PIN, HIGH);
digitalWrite(LIGHT_PIN2, HIGH);
} }
} }
@@ -1,12 +1,18 @@
// ROFLS+ Arena Controller // ROFLS+ Arena Controller
#include <WiFi.h> #include <WiFi.h>
#include <esp_wifi.h> // Required for wifi_tx_info_t
#include <esp_now.h> // automatically installed for ESP32 boards, I think? #include <esp_now.h> // automatically installed for ESP32 boards, I think?
#include <Preferences.h> // automatically installed for ESP32 boards #include <Preferences.h> // automatically installed for ESP32 boards
#include <avdweb_Switch.h> // https://github.com/avdwebLibraries/avdweb_Switch #include <avdweb_Switch.h> // https://github.com/avdwebLibraries/avdweb_Switch
#include <CountDown.h> // https://github.com/RobTillaart/CountDown #include <CountDown.h> // https://github.com/RobTillaart/CountDown
#include <StopWatch.h> // https://github.com/RobTillaart/StopWatch_RT #include <StopWatch.h> // https://github.com/RobTillaart/StopWatch_RT
// set Arena, switches the MAC addresses out
// 1 = Arena A (Ant)
// 2 = Arena B (Beetle)
#define ARENA 1
// Hardware connections // Hardware connections
// Buttons: // Buttons:
#define START_BTN_PIN 1 #define START_BTN_PIN 1
@@ -25,7 +31,7 @@
// Relays: // Relays:
#define PIT_RELEASE_PIN 37 #define PIT_RELEASE_PIN 37
#define LIGHT_PIN 39 #define LIGHT_PIN 39
#define UNUSED_RELAY3_PIN 35 #define LIGHT_PIN2 35
#define UNUSED_RELAY4_PIN 33 #define UNUSED_RELAY4_PIN 33
const byte relayOnState = LOW; const byte relayOnState = LOW;
@@ -68,7 +74,7 @@ CountDown ReadyCountDown(CountDown::SECONDS);
// Rumble stopwatch // Rumble stopwatch
StopWatch rumbleTIME(StopWatch::SECONDS); StopWatch rumbleTIME(StopWatch::SECONDS);
int CLOCK_LED_BRIGHTNESS = 32; // 64 is okay int CLOCK_LED_BRIGHTNESS = 128; // 64 is okay
int BLINK_COUNTER_REDTEAM = 0; int BLINK_COUNTER_REDTEAM = 0;
int BLINK_COUNTER_BLUETEAM = 0; int BLINK_COUNTER_BLUETEAM = 0;
@@ -83,7 +89,16 @@ bool resumeFight = false;
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// ESP-NOW config // ESP-NOW config
// send config, Clock: // send config, Clock:
uint8_t broadcastAddressClock[] = {0x48, 0x27, 0xE2, 0x5D, 0xB6, 0x84}; #if ARENA == 1
// A Arena
uint8_t broadcastAddressClock1[] = {0x48, 0x27, 0xE2, 0x5D, 0xB6, 0x84};
uint8_t broadcastAddressClock2[] = {0xD8, 0x3B, 0xDA, 0xC9, 0x49, 0xC6};
#elif ARENA == 2
// B Arena
uint8_t broadcastAddressClock1[] = {0xD8, 0x3B, 0xDA, 0xC8, 0xFF, 0xFA};
uint8_t broadcastAddressClock2[] = {0xD8, 0x3B, 0xDA, 0xC8, 0x95, 0x42};
#endif
// struct for clock data // struct for clock data
typedef struct struct_message_Clock { typedef struct struct_message_Clock {
int sendMinutes; int sendMinutes;
@@ -98,8 +113,15 @@ typedef struct struct_message_Clock {
struct_message_Clock sendClockDATA; struct_message_Clock sendClockDATA;
// send config, pilot buttons: // send config, pilot buttons:
#if ARENA == 1
// A Arena
uint8_t broadcastAddressREDTEAMbutton[] = {0x84, 0xFC, 0xE6, 0xC7, 0x23, 0x14}; uint8_t broadcastAddressREDTEAMbutton[] = {0x84, 0xFC, 0xE6, 0xC7, 0x23, 0x14};
uint8_t broadcastAddressBLUETEAMbutton[] = {0x84, 0xFC, 0xE6, 0xC7, 0x1A, 0x02}; uint8_t broadcastAddressBLUETEAMbutton[] = {0x84, 0xFC, 0xE6, 0xC7, 0x1A, 0x02};
#elif ARENA == 2
// B Arena
uint8_t broadcastAddressREDTEAMbutton[] = {0xD8, 0x3B, 0xDA, 0xC8, 0x95, 0x58};
uint8_t broadcastAddressBLUETEAMbutton[] = {0xD8, 0x3B, 0xDA, 0xC8, 0x95, 0x1C};
#endif
// Structure for sending data // Structure for sending data
typedef struct struct_message_send { typedef struct struct_message_send {
@@ -111,7 +133,16 @@ struct_message_send sendToREDTEAMbutton;
struct_message_send sendToBLUETEAMbutton; struct_message_send sendToBLUETEAMbutton;
// ESP-Now stuff for the Pit controller // ESP-Now stuff for the Pit controller
#if ARENA == 1
// A Arena
uint8_t broadcastAddressPitController[] = {0x84, 0xFC, 0xE6, 0xC7, 0x19, 0xDE}; uint8_t broadcastAddressPitController[] = {0x84, 0xFC, 0xE6, 0xC7, 0x19, 0xDE};
#elif ARENA == 2
// B Arena
uint8_t broadcastAddressPitController[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xB0, 0x0B};
#endif
// Test Pit controller
//uint8_t broadcastAddressPitController[] = {0x94, 0xA9, 0x90, 0x0B, 0x21, 0x64};
// Structure for sending data // Structure for sending data
typedef struct struct_message_pit { typedef struct struct_message_pit {
bool PIT; // LED state bool PIT; // LED state
@@ -122,7 +153,7 @@ esp_now_peer_info_t peerInfo;
// callback when data is sent // callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { void OnDataSent(const wifi_tx_info_t *tx_info, esp_now_send_status_t status) {
Serial.print("\r\nLast Packet Send Status:\t"); Serial.print("\r\nLast Packet Send Status:\t");
Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail"); Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
} }
@@ -165,17 +196,29 @@ void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
// RED team button // RED team button
// ignore button input if in rumble mode // ignore button input if in rumble mode
if (!switchRUMBLE.on()) { if (!switchRUMBLE.on()) {
// ignore button press while in fight
if (!FightCountDown.isRunning()) {
buttonREDTEAMvar = receiveDATA.buttonREDTEAM; buttonREDTEAMvar = receiveDATA.buttonREDTEAM;
}
// ignore tapout while not in fight
if (FightCountDown.isRunning()) {
buttonREDTEAMtapout = receiveDATA.buttonREDTEAMtapout; buttonREDTEAMtapout = receiveDATA.buttonREDTEAMtapout;
} }
}
break; break;
case 2: case 2:
// BLUE team button // BLUE team button
// ignore button input if in rumble mode // ignore button input if in rumble mode
if (!switchRUMBLE.on()) { if (!switchRUMBLE.on()) {
// ignore button press while in fight
if (!FightCountDown.isRunning()) {
buttonBLUETEAMvar = receiveDATA.buttonBLUETEAM; buttonBLUETEAMvar = receiveDATA.buttonBLUETEAM;
}
// ignore tapout while not in fight
if (FightCountDown.isRunning()) {
buttonBLUETEAMtapout = receiveDATA.buttonBLUETEAMtapout; buttonBLUETEAMtapout = receiveDATA.buttonBLUETEAMtapout;
} }
}
break; break;
} }
} }
@@ -190,8 +233,10 @@ void sendTimeDisplay(int MINUTES, int SECONDS, int RED, int GREEN, int BLUE, int
sendClockDATA.sendGREENchannel = GREEN; sendClockDATA.sendGREENchannel = GREEN;
sendClockDATA.sendBLUEchannel = BLUE; sendClockDATA.sendBLUEchannel = BLUE;
sendClockDATA.sendBrightness = BRIGHTNESS; sendClockDATA.sendBrightness = BRIGHTNESS;
// actually send it // actually send it to first clock
esp_err_t result = esp_now_send(broadcastAddressClock, (uint8_t *) &sendClockDATA, sizeof(sendClockDATA)); esp_err_t result1 = esp_now_send(broadcastAddressClock1, (uint8_t *) &sendClockDATA, sizeof(sendClockDATA));
// actually send it to second clock
esp_err_t result2 = esp_now_send(broadcastAddressClock2, (uint8_t *) &sendClockDATA, sizeof(sendClockDATA));
} }
} }
@@ -222,13 +267,13 @@ void setup() {
digitalWrite(PIT_RELEASE_PIN, relayOffState); digitalWrite(PIT_RELEASE_PIN, relayOffState);
pinMode(LIGHT_PIN, OUTPUT); pinMode(LIGHT_PIN, OUTPUT);
digitalWrite(LIGHT_PIN, LOW); // have it by default on, to prevent flickering, needs a better fix tho digitalWrite(LIGHT_PIN, LOW); // have it by default on, to prevent flickering, needs a better fix tho
pinMode(UNUSED_RELAY3_PIN, OUTPUT); pinMode(LIGHT_PIN2, OUTPUT);
digitalWrite(UNUSED_RELAY3_PIN, relayOffState); digitalWrite(LIGHT_PIN2, LOW); // have it by default on, to prevent flickering, needs a better fix tho
pinMode(UNUSED_RELAY4_PIN, OUTPUT); pinMode(UNUSED_RELAY4_PIN, OUTPUT);
digitalWrite(UNUSED_RELAY4_PIN, relayOffState); digitalWrite(UNUSED_RELAY4_PIN, relayOffState);
// set status LED outputs: // set status LED outputs:
pinMode(LIGHT_STATUS_LED, OUTPUT); pinMode(LIGHT_STATUS_LED, OUTPUT);
digitalWrite(LIGHT_PIN, HIGH); // have it by default on, to prevent flickering, needs a better fix tho digitalWrite(LIGHT_STATUS_LED, HIGH);
pinMode(AUTOPIT_STATUS_LED, OUTPUT); pinMode(AUTOPIT_STATUS_LED, OUTPUT);
digitalWrite(AUTOPIT_STATUS_LED, LOW); digitalWrite(AUTOPIT_STATUS_LED, LOW);
pinMode(MODE_STATUS_LED, OUTPUT); pinMode(MODE_STATUS_LED, OUTPUT);
@@ -248,17 +293,27 @@ void setup() {
// get the status of Transmitted packet // get the status of Transmitted packet
esp_now_register_send_cb(OnDataSent); esp_now_register_send_cb(OnDataSent);
// Register clock peer // Register first clock peer
memcpy(peerInfo.peer_addr, broadcastAddressClock, 6); memcpy(peerInfo.peer_addr, broadcastAddressClock1, 6);
peerInfo.channel = 0; peerInfo.channel = 0;
peerInfo.encrypt = false; peerInfo.encrypt = false;
// Add peer // Add peer
if (esp_now_add_peer(&peerInfo) != ESP_OK){ if (esp_now_add_peer(&peerInfo) != ESP_OK){
Serial.println("Failed to add peer"); Serial.println("Failed to add Clock1 peer");
return; return;
} }
esp_err_t result = esp_now_send(broadcastAddressClock, (uint8_t *) &sendClockDATA, sizeof(sendClockDATA)); // 2) Register second clock peer
memcpy(peerInfo.peer_addr, broadcastAddressClock2, 6);
// peerInfo.channel & peerInfo.encrypt already set above
if (esp_now_add_peer(&peerInfo) != ESP_OK) {
Serial.println("Failed to add Clock2 peer");
return;
}
// send initial data (I think that doesn't work but meh...)
esp_err_t result1 = esp_now_send(broadcastAddressClock1, (uint8_t *) &sendClockDATA, sizeof(sendClockDATA));
esp_err_t result2 = esp_now_send(broadcastAddressClock2, (uint8_t *) &sendClockDATA, sizeof(sendClockDATA));
// Register Red Team Button peer // Register Red Team Button peer
memcpy(peerInfo.peer_addr, broadcastAddressREDTEAMbutton, 6); memcpy(peerInfo.peer_addr, broadcastAddressREDTEAMbutton, 6);
@@ -4,6 +4,7 @@
// 48:27:E2:50:86:84 // 48:27:E2:50:86:84
#include <WiFi.h> #include <WiFi.h>
#include <esp_wifi.h> // Required for wifi_tx_info_t
#include <esp_now.h> // automatically installed for ESP32 boards, I think? #include <esp_now.h> // automatically installed for ESP32 boards, I think?
#include <Preferences.h> // automatically installed for ESP32 boards #include <Preferences.h> // automatically installed for ESP32 boards
#include <FastLED.h> // https://fastled.io/ #include <FastLED.h> // https://fastled.io/
@@ -58,7 +59,7 @@ void setup() {
//FastLED.addLeds<WS2811Controller800Khz, LED_DATA_PIN_TIMER, GRB>(leds_TIMER, NUM_LEDS_TIMER); // GRB ordering is typical //FastLED.addLeds<WS2811Controller800Khz, LED_DATA_PIN_TIMER, GRB>(leds_TIMER, NUM_LEDS_TIMER); // GRB ordering is typical
FastLED.addLeds<WS2812B, LED_DATA_PIN_TIMER, GRB>(leds_TIMER, NUM_LEDS_TIMER); // GRB ordering is typical FastLED.addLeds<WS2812B, LED_DATA_PIN_TIMER, GRB>(leds_TIMER, NUM_LEDS_TIMER); // GRB ordering is typical
//FastLED.setMaxRefreshRate(10, true); //FastLED.setMaxRefreshRate(10, true);
FastLED.setMaxPowerInVoltsAndMilliamps(5,2000); // Limit to 10W of output power FastLED.setMaxPowerInVoltsAndMilliamps(5,4000); // Limit to 20W of output power
// set default values // set default values
// default screen, just to show it's up and running but hasn't received any data, should display 00:00 in blue // default screen, just to show it's up and running but hasn't received any data, should display 00:00 in blue
+37 -9
View File
@@ -1,6 +1,7 @@
// ROFLS+ Referee Remote // ROFLS+ Referee Remote
#include <WiFi.h> #include <WiFi.h>
#include <esp_wifi.h> // Required for wifi_tx_info_t
#include <esp_now.h> // automatically installed for ESP32 boards, I think? #include <esp_now.h> // automatically installed for ESP32 boards, I think?
#include <Preferences.h> // automatically installed for ESP32 boards #include <Preferences.h> // automatically installed for ESP32 boards
#include <avdweb_Switch.h> // https://github.com/avdwebLibraries/avdweb_Switch #include <avdweb_Switch.h> // https://github.com/avdwebLibraries/avdweb_Switch
@@ -14,6 +15,11 @@
const bool writeBoardID = false; const bool writeBoardID = false;
int boardID = 0; int boardID = 0;
// set Arena, switches the MAC addresses out
// 1 = Arena A (Ant)
// 2 = Arena B (Beetle)
#define ARENA 1
// Hardware connections // Hardware connections
#define START_BTN_PIN 10 #define START_BTN_PIN 10
#define PAUSE_BTN_PIN 8 #define PAUSE_BTN_PIN 8
@@ -45,7 +51,17 @@ bool sendDATAvar = false;
// ESP-NOW config // ESP-NOW config
// REPLACE WITH YOUR RECEIVER MAC Address // REPLACE WITH YOUR RECEIVER MAC Address
#if ARENA == 1
// A Arena Controller MAC Address
uint8_t broadcastAddress[] = {0x84, 0xFC, 0xE6, 0xC7, 0x1A, 0x8C}; uint8_t broadcastAddress[] = {0x84, 0xFC, 0xE6, 0xC7, 0x1A, 0x8C};
// Additional receiver MAC address
uint8_t extraReceiverMAC[] = { 0x78, 0x42, 0x1C, 0x6B, 0x2A, 0x4C }; // replace with real MAC
#elif ARENA == 2
// B Arena Controller MAC Address
uint8_t broadcastAddress[] = {0xD8, 0x3B, 0xDA, 0xC9, 0x0C, 0xEE};
// Additional receiver MAC address
uint8_t extraReceiverMAC[] = { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }; // replace with real MAC
#endif
// Structure example to send data // Structure example to send data
// Must match the receiver structure // Must match the receiver structure
@@ -74,12 +90,10 @@ typedef struct struct_message_receive {
// Create instance for receiving ESP-NOW data // Create instance for receiving ESP-NOW data
struct_message_receive receiveDATA; struct_message_receive receiveDATA;
esp_now_peer_info_t peerInfo;
Preferences preferences; Preferences preferences;
// callback when data is sent // callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { void OnDataSent(const wifi_tx_info_t *tx_info, esp_now_send_status_t status) {
Serial.print("\r\nLast Packet Send Status:\t"); Serial.print("\r\nLast Packet Send Status:\t");
Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail"); Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
} }
@@ -126,13 +140,25 @@ void setup() {
// get the status of Transmitted packet // get the status of Transmitted packet
esp_now_register_send_cb(OnDataSent); esp_now_register_send_cb(OnDataSent);
// Register peer // Register peer 1 (arena controller)
memcpy(peerInfo.peer_addr, broadcastAddress, 6); esp_now_peer_info_t peerInfo1 = {};
peerInfo.channel = 0; memcpy(peerInfo1.peer_addr, broadcastAddress, 6);
peerInfo.encrypt = false; peerInfo1.channel = 0;
peerInfo1.encrypt = false;
// Add peer // Add peer
if (esp_now_add_peer(&peerInfo) != ESP_OK){ if (esp_now_add_peer(&peerInfo1) != ESP_OK){
Serial.println("Failed to add peer"); Serial.println("Failed to add peer1");
return;
}
// Register peer 2 (extra receiver)
esp_now_peer_info_t peerInfo2 = {};
memcpy(peerInfo2.peer_addr, extraReceiverMAC, 6);
peerInfo2.channel = 0;
peerInfo2.encrypt = false;
// Add peer
if (esp_now_add_peer(&peerInfo2) != ESP_OK){
Serial.println("Failed to add peer2");
return; return;
} }
@@ -171,6 +197,8 @@ void sendDATA_to_controller() {
sendDATA.buttonBLUETEAMtapout = buttonBLUETEAMtapout; sendDATA.buttonBLUETEAMtapout = buttonBLUETEAMtapout;
// send data // send data
esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &sendDATA, sizeof(sendDATA)); esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &sendDATA, sizeof(sendDATA));
esp_now_send(extraReceiverMAC, (uint8_t *) &sendDATA, sizeof(sendDATA));
// check if transmission was successfull // check if transmission was successfull
// if (result == ESP_OK) { // if (result == ESP_OK) {
// Serial.println("Sent with success"); // Serial.println("Sent with success");
+80
View File
@@ -0,0 +1,80 @@
# ROFLS Arena Reference Manual
### Before a fight/tournament (omit step 1-6 after initial setup):
1. Check all connections
2. Power on the PSU
3. Turn on the Teambuttons and the Referee Remote
4. Wait for the 7 Segment Clock to turn on (~2s)
5. Turn on the lights (if off and needed)
6. Open the Controller Box and set the brightness with the potentiometer on the top right
7. Select the mode (explanations for the modes, see below):
8. Press and Hold Reset for >1s, can be on the Controller itself or the Referee Remote
9. Arena is ready
### Start a normal fight:
1. for good measure: Press and Hold Reset for >1s, can be on the Controller itself or the Referee Remote.
2. Team Red and Team Blue need to (short) press their respective Teambuttons, the Clock turns green to signal that both are ready.
3. Press Start, if the Teambuttons are not working, the Start button can be long pressed (>1s), to force a start.
4. To prevent the automatic pit opening, press and hold the Pit button, as soon as the pit button gets released, the pit will open.
### Start a rumble fight:
1. for good measure: Press and Hold Reset for >1s, can be on the Controller itself or the Referee Remote.
2. Press Start
3. To prevent the automatic pit opening, press and hold the Pit button, as soon as the pit button gets released, the pit will open.
### Pausing a running fight:
1. Press the Pause button
2. To resume:
- normal fight:
Both Teams need to press their respective Teambuttons to signal that they are both ready.
- Press start, after 3s, the timer resumes.
- rumble fight:
- Press start, after 3s, the stop watch resumes.
## Modes
* Normal Mode:
* Normal fight mode, 3 Minute Timer, Tapout enabled, after 90s (halftime) automatic pit opening (if enabled)
* Teambuttons are active and need to be pressed once to sign that they are ready. They light up if ready.
* Teambuttons LEDs turn of when the fight starts
* During a running fight, if one of the Teambuttons is pressed >1s, it counts as a tapout. The Timer stops and displays "TAPOUT", cycles between "TAPOUT" and the end time.
* Can be paused, resuming works the same as starting, Teams must be ready
* Rumble Mode:
* Rumble mode, stop watch counting up, Tapout disabled, after 90s automatic pit opening (if enabled)
* Match starts 3s after pressing start
* Teambuttons are inactive
* Teambutton LEDs Light up 3s before the match begins, and turn off when it starts.
* No Tapout available
* Can be paused, resuming works the same as starting, Teams must be ready
## Buttons explained
> The 4 buttons on the referee remote and the controller box have the same function and can both be used.
* Start
* Starts a fight, can be long pressed (>1s) to force start a fight
* Pause
* Pauses a fight
* Pit
* Short press (<1s) immediately opens the pit, Long press (>1s) prevents the pit from automatically open, but opens immediately after released
* Reset
* Resets the Arena Controller, needs to be long pressed (>1s)
* Lights Switch
* Turns on/off the Arena Lights, lights up if lights are on.
* Auto Pit Switch
* Turn on/off the automatic pit opening, lights up if enabled
* Mode Switch
* R = Rumble mode
* N = Normal mode
* T = Self Test (not implemented yet)
## Display Colours & Status explained
| Colour | Meaning |
|--------|---------|
| Blue with one purple segment, displaying 00:00 | not initialized (hit reset to fix) |
| Purple, displaying 03:00 | Normal Mode, Idle/Arena Ready |
| Green | Normal Mode, Both Teams Ready/Running Fight |
| Yellow | 3s Ready Countdown or Paused |
| Yellow, initally blinking 3 times | Timer run out / finish |
| Red, cycling between tap, out and time | Red Team has tapped out |
| Blue, cycling between tap, out and time | Blue Team has tapped out |
| Teal, displaying 00:00 | Rumble mode, Idle/Arena Ready |
| Teal, displaying timer | Rumble mode, active fight |