Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 48384f87c2 | |||
| bba3a5d50b | |||
| 3d9a1a778c | |||
| 4d7a43c9d1 | |||
| c450cfb7c9 | |||
| 7c3dbfab63 | |||
| fc472600c4 | |||
| 649cbe63b1 | |||
| 79c734bd34 | |||
| 3e29ab76e2 | |||
| d81ab7da89 | |||
| 5e5ff83506 | |||
| 606219b8bd | |||
| d93b283d2c | |||
| c9c4b1c1c5 |
@@ -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() {
|
||||
if (switchLIGHT.on()) {
|
||||
digitalWrite(LIGHT_PIN, LOW);
|
||||
digitalWrite(LIGHT_PIN2, LOW);
|
||||
}
|
||||
else {
|
||||
digitalWrite(LIGHT_PIN, HIGH);
|
||||
digitalWrite(LIGHT_PIN2, HIGH);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
// ROFLS+ Arena Controller
|
||||
|
||||
#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 <Preferences.h> // automatically installed for ESP32 boards
|
||||
#include <FastLED.h> // https://fastled.io/
|
||||
#include <avdweb_Switch.h> // https://github.com/avdwebLibraries/avdweb_Switch
|
||||
#include <CountDown.h> // https://github.com/RobTillaart/CountDown
|
||||
#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
|
||||
// Buttons:
|
||||
#define START_BTN_PIN 1
|
||||
@@ -26,7 +31,7 @@
|
||||
// Relays:
|
||||
#define PIT_RELEASE_PIN 37
|
||||
#define LIGHT_PIN 39
|
||||
#define UNUSED_RELAY3_PIN 35
|
||||
#define LIGHT_PIN2 35
|
||||
#define UNUSED_RELAY4_PIN 33
|
||||
|
||||
const byte relayOnState = LOW;
|
||||
@@ -58,10 +63,10 @@ bool buttonBLUETEAMtapout = false;
|
||||
unsigned long PITopenTimestamp = 0;
|
||||
bool PITreleased = false;
|
||||
|
||||
const long PITopenTime = 500; // activate solenoid for 500ms
|
||||
const int countdownTIME = 180; // countdown timer length in seconds, actual countdown for the fight
|
||||
const int countdownToFightTIME = 3; // countdown timer length in seconds, "ready" countdown
|
||||
const int PITreleaseTime = 90; // automatic pit release time in seconds until end of countdown
|
||||
const long PITopenTime = 500; // default: 500 activate solenoid for 500ms
|
||||
const int countdownTIME = 180; // default: 180 countdown timer length in seconds, actual countdown for the fight
|
||||
const int countdownToFightTIME = 3; // default: 3 countdown timer length in seconds, "ready" countdown
|
||||
const int PITreleaseTime = 90; // default: 90 automatic pit release time in seconds until end of countdown
|
||||
bool countdownPAUSED = false;
|
||||
CountDown FightCountDown(CountDown::SECONDS);
|
||||
CountDown ReadyCountDown(CountDown::SECONDS);
|
||||
@@ -69,7 +74,7 @@ CountDown ReadyCountDown(CountDown::SECONDS);
|
||||
// Rumble stopwatch
|
||||
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_BLUETEAM = 0;
|
||||
@@ -79,11 +84,21 @@ int BLINK_INTERVAL = 200;
|
||||
bool ARENA_READY = false;
|
||||
bool REDTEAM_READY = false;
|
||||
bool BLUETEAM_READY = false;
|
||||
bool resumeFight = false;
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// ESP-NOW config
|
||||
// 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
|
||||
typedef struct struct_message_Clock {
|
||||
int sendMinutes;
|
||||
@@ -98,8 +113,15 @@ typedef struct struct_message_Clock {
|
||||
struct_message_Clock sendClockDATA;
|
||||
|
||||
// send config, pilot buttons:
|
||||
uint8_t broadcastAddressREDTEAMbutton[] = {0x84, 0xFC, 0xE6, 0xC7, 0x23, 0x14};
|
||||
uint8_t broadcastAddressBLUETEAMbutton[] = {0x84, 0xFC, 0xE6, 0xC7, 0x1A, 0x02};
|
||||
#if ARENA == 1
|
||||
// A Arena
|
||||
uint8_t broadcastAddressREDTEAMbutton[] = {0x84, 0xFC, 0xE6, 0xC7, 0x23, 0x14};
|
||||
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
|
||||
typedef struct struct_message_send {
|
||||
@@ -111,7 +133,16 @@ struct_message_send sendToREDTEAMbutton;
|
||||
struct_message_send sendToBLUETEAMbutton;
|
||||
|
||||
// ESP-Now stuff for the Pit controller
|
||||
uint8_t broadcastAddressPitController[] = {0x84, 0xFC, 0xE6, 0xC7, 0x19, 0xDE};
|
||||
#if ARENA == 1
|
||||
// A Arena
|
||||
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
|
||||
typedef struct struct_message_pit {
|
||||
bool PIT; // LED state
|
||||
@@ -122,7 +153,7 @@ esp_now_peer_info_t peerInfo;
|
||||
|
||||
// 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.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
|
||||
}
|
||||
@@ -163,13 +194,31 @@ void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
|
||||
break;
|
||||
case 1:
|
||||
// RED team button
|
||||
buttonREDTEAMvar = receiveDATA.buttonREDTEAM;
|
||||
buttonREDTEAMtapout = receiveDATA.buttonREDTEAMtapout;
|
||||
// ignore button input if in rumble mode
|
||||
if (!switchRUMBLE.on()) {
|
||||
// ignore button press while in fight
|
||||
if (!FightCountDown.isRunning()) {
|
||||
buttonREDTEAMvar = receiveDATA.buttonREDTEAM;
|
||||
}
|
||||
// ignore tapout while not in fight
|
||||
if (FightCountDown.isRunning()) {
|
||||
buttonREDTEAMtapout = receiveDATA.buttonREDTEAMtapout;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
// BLUE team button
|
||||
buttonBLUETEAMvar = receiveDATA.buttonBLUETEAM;
|
||||
buttonBLUETEAMtapout = receiveDATA.buttonBLUETEAMtapout;
|
||||
// ignore button input if in rumble mode
|
||||
if (!switchRUMBLE.on()) {
|
||||
// ignore button press while in fight
|
||||
if (!FightCountDown.isRunning()) {
|
||||
buttonBLUETEAMvar = receiveDATA.buttonBLUETEAM;
|
||||
}
|
||||
// ignore tapout while not in fight
|
||||
if (FightCountDown.isRunning()) {
|
||||
buttonBLUETEAMtapout = receiveDATA.buttonBLUETEAMtapout;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -184,11 +233,30 @@ void sendTimeDisplay(int MINUTES, int SECONDS, int RED, int GREEN, int BLUE, int
|
||||
sendClockDATA.sendGREENchannel = GREEN;
|
||||
sendClockDATA.sendBLUEchannel = BLUE;
|
||||
sendClockDATA.sendBrightness = BRIGHTNESS;
|
||||
// actually send it
|
||||
esp_err_t result = esp_now_send(broadcastAddressClock, (uint8_t *) &sendClockDATA, sizeof(sendClockDATA));
|
||||
// actually send it to first clock
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
// Global Vars for tap out states
|
||||
bool redTapOutActive = false;
|
||||
unsigned long redTapOutStartTime = 0;
|
||||
int redTapOutStage = 0;
|
||||
|
||||
bool blueTapOutActive = false;
|
||||
unsigned long blueTapOutStartTime = 0;
|
||||
int blueTapOutStage = 0;
|
||||
|
||||
// Global Vars for Fight end
|
||||
bool fightStarted = false;
|
||||
bool fightEnded = false;
|
||||
const int END_BLINK_COUNT = 3;
|
||||
const unsigned long END_BLINK_INTERVAL = 500; // in milliseconds
|
||||
int endBlinkTransitions = 0;
|
||||
bool endBlinkState = false;
|
||||
unsigned long lastEndBlinkTime = 0;
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
@@ -199,13 +267,13 @@ void setup() {
|
||||
digitalWrite(PIT_RELEASE_PIN, relayOffState);
|
||||
pinMode(LIGHT_PIN, OUTPUT);
|
||||
digitalWrite(LIGHT_PIN, LOW); // have it by default on, to prevent flickering, needs a better fix tho
|
||||
pinMode(UNUSED_RELAY3_PIN, OUTPUT);
|
||||
digitalWrite(UNUSED_RELAY3_PIN, relayOffState);
|
||||
pinMode(LIGHT_PIN2, OUTPUT);
|
||||
digitalWrite(LIGHT_PIN2, LOW); // have it by default on, to prevent flickering, needs a better fix tho
|
||||
pinMode(UNUSED_RELAY4_PIN, OUTPUT);
|
||||
digitalWrite(UNUSED_RELAY4_PIN, relayOffState);
|
||||
// set status LED outputs:
|
||||
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);
|
||||
digitalWrite(AUTOPIT_STATUS_LED, LOW);
|
||||
pinMode(MODE_STATUS_LED, OUTPUT);
|
||||
@@ -225,17 +293,27 @@ void setup() {
|
||||
// get the status of Transmitted packet
|
||||
esp_now_register_send_cb(OnDataSent);
|
||||
|
||||
// Register clock peer
|
||||
memcpy(peerInfo.peer_addr, broadcastAddressClock, 6);
|
||||
// Register first clock peer
|
||||
memcpy(peerInfo.peer_addr, broadcastAddressClock1, 6);
|
||||
peerInfo.channel = 0;
|
||||
peerInfo.encrypt = false;
|
||||
// Add peer
|
||||
if (esp_now_add_peer(&peerInfo) != ESP_OK){
|
||||
Serial.println("Failed to add peer");
|
||||
Serial.println("Failed to add Clock1 peer");
|
||||
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
|
||||
memcpy(peerInfo.peer_addr, broadcastAddressREDTEAMbutton, 6);
|
||||
@@ -280,81 +358,85 @@ void setup() {
|
||||
esp_now_register_recv_cb(esp_now_recv_cb_t(OnDataRecv));
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// poll all the switch/button inputs
|
||||
pollInput();
|
||||
// deactivate solenoids if needed
|
||||
checkPIT();
|
||||
// Arena Light
|
||||
arenaLIGHT();
|
||||
// update status LEDs
|
||||
statusLEDs();
|
||||
// update Teambutton LEDs
|
||||
updateTEAMLEDs();
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
// start button logic
|
||||
// Handler for the START button logic.
|
||||
void handleStartButton() {
|
||||
if (buttonSTARTvar) {
|
||||
buttonSTARTvar = false;
|
||||
// start logic
|
||||
// Fresh start: no countdown is running, and the fight hasn't been paused.
|
||||
if (!FightCountDown.isRunning() && !rumbleTIME.isRunning() && countdownPAUSED == false) {
|
||||
if (switchRUMBLE.on()) {
|
||||
buttonREDTEAMvar = false;
|
||||
buttonBLUETEAMvar = false;
|
||||
//rumbleTIME.start();
|
||||
ARENA_READY = true;
|
||||
REDTEAM_LED(true);
|
||||
BLUETEAM_LED(true);
|
||||
ReadyCountDown.start(countdownToFightTIME);
|
||||
} else if (buttonREDTEAMvar && buttonBLUETEAMvar && !switchRUMBLE.on()) {
|
||||
}
|
||||
else if (buttonREDTEAMvar && buttonBLUETEAMvar && !switchRUMBLE.on()) {
|
||||
buttonREDTEAMvar = false;
|
||||
buttonBLUETEAMvar = false;
|
||||
//FightCountDown.start(countdownTIME);
|
||||
ARENA_READY = true;
|
||||
REDTEAM_LED(true);
|
||||
BLUETEAM_LED(true);
|
||||
ReadyCountDown.start(countdownToFightTIME);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// resume logic
|
||||
// Resume (unpause) branch: instead of calling FightCountDown.resume() immediately,
|
||||
// we want to display ReadyCountDown first.
|
||||
if (buttonREDTEAMvar && buttonBLUETEAMvar && !switchRUMBLE.on()) {
|
||||
buttonREDTEAMvar = false;
|
||||
buttonBLUETEAMvar = false;
|
||||
resumeFight = true; // Mark that we want to resume later.
|
||||
countdownPAUSED = false;
|
||||
FightCountDown.resume();
|
||||
} else if (switchRUMBLE.on()) {
|
||||
ARENA_READY = true;
|
||||
REDTEAM_LED(true);
|
||||
BLUETEAM_LED(true);
|
||||
ReadyCountDown.start(countdownToFightTIME);
|
||||
}
|
||||
else if (switchRUMBLE.on()) {
|
||||
buttonREDTEAMvar = false;
|
||||
buttonBLUETEAMvar = false;
|
||||
resumeFight = true;
|
||||
countdownPAUSED = false;
|
||||
rumbleTIME.start();
|
||||
ARENA_READY = true;
|
||||
REDTEAM_LED(true);
|
||||
BLUETEAM_LED(true);
|
||||
ReadyCountDown.start(countdownToFightTIME);
|
||||
}
|
||||
}
|
||||
}
|
||||
// forced start of countdown
|
||||
}
|
||||
|
||||
// Handler for the forced start button logic.
|
||||
void handleForcedStartButton() {
|
||||
if (buttonSTARTforced) {
|
||||
buttonSTARTforced = false;
|
||||
if (!FightCountDown.isRunning() && !rumbleTIME.isRunning() && countdownPAUSED == false) {
|
||||
buttonREDTEAMvar = false;
|
||||
buttonBLUETEAMvar = false;
|
||||
if (!switchRUMBLE.on()) {
|
||||
//FightCountDown.start(countdownTIME);
|
||||
ARENA_READY = true;
|
||||
ReadyCountDown.start(countdownToFightTIME);
|
||||
} else if (switchRUMBLE.on()) {
|
||||
//rumbleTIME.start();
|
||||
ARENA_READY = true;
|
||||
ReadyCountDown.start(countdownToFightTIME);
|
||||
}
|
||||
ARENA_READY = true;
|
||||
REDTEAM_LED(true);
|
||||
BLUETEAM_LED(true);
|
||||
ReadyCountDown.start(countdownToFightTIME);
|
||||
}
|
||||
else {
|
||||
// forced resume logic
|
||||
buttonREDTEAMvar = false;
|
||||
buttonBLUETEAMvar = false;
|
||||
resumeFight = true;
|
||||
countdownPAUSED = false;
|
||||
if (!switchRUMBLE.on()) {
|
||||
FightCountDown.resume();
|
||||
} else if (switchRUMBLE.on()) {
|
||||
rumbleTIME.start();
|
||||
}
|
||||
ARENA_READY = true;
|
||||
REDTEAM_LED(true);
|
||||
BLUETEAM_LED(true);
|
||||
ReadyCountDown.start(countdownToFightTIME);
|
||||
}
|
||||
}
|
||||
// pause button logic
|
||||
}
|
||||
|
||||
// Handler for the pause button logic.
|
||||
void handlePauseButton() {
|
||||
if (buttonPAUSEvar) {
|
||||
buttonPAUSEvar = false;
|
||||
if (FightCountDown.isRunning()) {
|
||||
@@ -366,90 +448,230 @@ void loop() {
|
||||
rumbleTIME.stop();
|
||||
}
|
||||
}
|
||||
// pit button logic
|
||||
}
|
||||
|
||||
// Handler for the pit button logic.
|
||||
void handlePitButton() {
|
||||
if (buttonPITvar) {
|
||||
buttonPITvar = false;
|
||||
buttonPIThold = false;
|
||||
openPITmanually();
|
||||
}
|
||||
// automatic pit release
|
||||
if (FightCountDown.remaining() <= PITreleaseTime && FightCountDown.remaining() != 0 && !switchRUMBLE.on() && switchPIT.on() && buttonPIThold == false) {
|
||||
openPIT();
|
||||
}
|
||||
if (rumbleTIME.elapsed() >= PITreleaseTime && switchRUMBLE.on() && switchPIT.on() && buttonPIThold == false) {
|
||||
openPIT();
|
||||
}
|
||||
}
|
||||
|
||||
// tap out logic
|
||||
if (buttonREDTEAMtapout && !switchRUMBLE.on()) {
|
||||
// check for automatic pit release.
|
||||
void handleAutoPitRelease() {
|
||||
// Only release the pit if it hasn't already been released.
|
||||
if (!PITreleased) {
|
||||
if (FightCountDown.remaining() <= PITreleaseTime && FightCountDown.remaining() != 0 &&
|
||||
!switchRUMBLE.on() && switchPIT.on() && buttonPIThold == false) {
|
||||
openPIT();
|
||||
}
|
||||
else if (rumbleTIME.elapsed() >= PITreleaseTime && switchRUMBLE.on() &&
|
||||
switchPIT.on() && buttonPIThold == false) {
|
||||
openPIT();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handler for transitioning from ReadyCountDown to the fight (or rumble) countdown.
|
||||
void handleCountdownTransition() {
|
||||
if (ReadyCountDown.remaining() == 0 && ARENA_READY) {
|
||||
ARENA_READY = false;
|
||||
REDTEAM_LED(false);
|
||||
BLUETEAM_LED(false);
|
||||
if (!switchRUMBLE.on()) {
|
||||
if (resumeFight) {
|
||||
FightCountDown.resume(); // Resume the paused fight countdown.
|
||||
resumeFight = false;
|
||||
}
|
||||
else {
|
||||
FightCountDown.start(countdownTIME); // Fresh start.
|
||||
fightStarted = true;
|
||||
}
|
||||
}
|
||||
else if (switchRUMBLE.on()) {
|
||||
// For the rumble branch, you may decide if a resume is relevant.
|
||||
// Here we simply start the rumble timer as before.
|
||||
rumbleTIME.start();
|
||||
resumeFight = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The display update logic now considers all conditions in order.
|
||||
void updateDisplay() {
|
||||
if (ReadyCountDown.isRunning()) {
|
||||
// Display Ready Countdown in Yellow
|
||||
sendTimeDisplay((ReadyCountDown.remaining()/60 % 60), (ReadyCountDown.remaining()%60), 255, 165, 0, CLOCK_LED_BRIGHTNESS);
|
||||
} else if (switchRUMBLE.on()) {
|
||||
// Display the Rumble Timer
|
||||
sendTimeDisplay((rumbleTIME.elapsed()/60 % 60), (rumbleTIME.elapsed()%60), 0, 255, 255, CLOCK_LED_BRIGHTNESS);
|
||||
} else if (!FightCountDown.isRunning() && !countdownPAUSED) {
|
||||
// Choose green if both team buttons are active; otherwise, choose magenta.
|
||||
if (buttonREDTEAMvar && buttonBLUETEAMvar) {
|
||||
sendTimeDisplay((countdownTIME/60 % 60), (countdownTIME%60), 0, 255, 0, CLOCK_LED_BRIGHTNESS);
|
||||
} else {
|
||||
sendTimeDisplay((countdownTIME/60 % 60), (countdownTIME%60), 255, 0, 255, CLOCK_LED_BRIGHTNESS);
|
||||
}
|
||||
} else {
|
||||
// choose yellow if countdown is paused and both teams aren't ready
|
||||
if (countdownPAUSED && (!buttonREDTEAMvar || !buttonBLUETEAMvar)) {
|
||||
sendTimeDisplay((FightCountDown.remaining()/60 % 60), (FightCountDown.remaining()%60), 255, 165, 0, CLOCK_LED_BRIGHTNESS);
|
||||
} else {
|
||||
// Display the countdown in green
|
||||
sendTimeDisplay((FightCountDown.remaining()/60 % 60), (FightCountDown.remaining()%60), 0, 255, 0, CLOCK_LED_BRIGHTNESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endless cycling for Red Team tap-out.
|
||||
void handleRedTapOut() {
|
||||
// When the red tap-out is first triggered.
|
||||
if (!redTapOutActive && buttonREDTEAMtapout && !switchRUMBLE.on() && !blueTapOutActive) {
|
||||
buttonREDTEAMtapout = false;
|
||||
countdownPAUSED = true;
|
||||
FightCountDown.stop();
|
||||
REDTEAM_LED(true);
|
||||
sendTimeDisplay(99, 0, 255, 0, 0, CLOCK_LED_BRIGHTNESS);
|
||||
delay(1500);
|
||||
sendTimeDisplay(0, 99, 255, 0, 0, CLOCK_LED_BRIGHTNESS);
|
||||
delay(1500);
|
||||
sendTimeDisplay((FightCountDown.remaining()/60%60), (FightCountDown.remaining()%60), 255, 0, 0, CLOCK_LED_BRIGHTNESS);
|
||||
delay(7000);
|
||||
redTapOutActive = true;
|
||||
redTapOutStartTime = millis();
|
||||
}
|
||||
if (buttonBLUETEAMtapout && !switchRUMBLE.on()) {
|
||||
|
||||
// If tap-out is active, continuously update the display in a cycle.
|
||||
if (redTapOutActive) {
|
||||
// Define a full cycle period of 8'000 ms.
|
||||
const unsigned long cycleDuration = 8000;
|
||||
// Compute how far into the current cycle we are.
|
||||
unsigned long cycleTime = (millis() - redTapOutStartTime) % cycleDuration;
|
||||
|
||||
if (cycleTime < 1500) {
|
||||
// Stage 1 (0 - 1500ms): Display first tap-out message.
|
||||
sendTimeDisplay(99, 0, 255, 0, 0, CLOCK_LED_BRIGHTNESS);
|
||||
}
|
||||
else if (cycleTime < 3000) {
|
||||
// Stage 2 (1500 - 3000ms): Display second tap-out message.
|
||||
sendTimeDisplay(0, 99, 255, 0, 0, CLOCK_LED_BRIGHTNESS);
|
||||
}
|
||||
else {
|
||||
// Stage 3 (3000 - 10'000ms): Show the remaining fight countdown.
|
||||
sendTimeDisplay((FightCountDown.remaining() / 60 % 60), (FightCountDown.remaining() % 60), 255, 0, 0, CLOCK_LED_BRIGHTNESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endless cycling for Blue Team tap-out.
|
||||
void handleBlueTapOut() {
|
||||
// When the blue tap-out is first triggered.
|
||||
if (!blueTapOutActive && buttonBLUETEAMtapout && !switchRUMBLE.on() && !redTapOutActive) {
|
||||
buttonBLUETEAMtapout = false;
|
||||
countdownPAUSED = true;
|
||||
FightCountDown.stop();
|
||||
BLUETEAM_LED(true);
|
||||
sendTimeDisplay(99, 0, 0, 0, 255, CLOCK_LED_BRIGHTNESS);
|
||||
delay(1500);
|
||||
sendTimeDisplay(0, 99, 0, 0, 255, CLOCK_LED_BRIGHTNESS);
|
||||
delay(1500);
|
||||
sendTimeDisplay((FightCountDown.remaining()/60%60), (FightCountDown.remaining()%60), 0, 0, 255, CLOCK_LED_BRIGHTNESS);
|
||||
delay(1500);
|
||||
blueTapOutActive = true;
|
||||
blueTapOutStartTime = millis();
|
||||
}
|
||||
|
||||
// reset button logic
|
||||
// If tap-out is active, continuously update the display in a cycle.
|
||||
if (blueTapOutActive) {
|
||||
// Define a full cycle period of 8'000 ms.
|
||||
const unsigned long cycleDuration = 8000;
|
||||
// Compute the cycle progress.
|
||||
unsigned long cycleTime = (millis() - blueTapOutStartTime) % cycleDuration;
|
||||
|
||||
if (cycleTime < 1500) {
|
||||
// Stage 1 (0 - 1500ms): Display first tap-out message.
|
||||
sendTimeDisplay(99, 0, 0, 0, 255, CLOCK_LED_BRIGHTNESS);
|
||||
}
|
||||
else if (cycleTime < 3000) {
|
||||
// Stage 2 (1500 - 3000ms): Display second tap-out message.
|
||||
sendTimeDisplay(0, 99, 0, 0, 255, CLOCK_LED_BRIGHTNESS);
|
||||
}
|
||||
else {
|
||||
// Stage 3 (3000 - 10'000ms): Show the remaining fight countdown.
|
||||
sendTimeDisplay((FightCountDown.remaining() / 60 % 60), (FightCountDown.remaining() % 60), 0, 0, 255, CLOCK_LED_BRIGHTNESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleFightEnd() {
|
||||
// When the countdown hits 0 and the fight had started, mark it as ended.
|
||||
if ((FightCountDown.remaining() == 0) && fightStarted) {
|
||||
fightEnded = true;
|
||||
}
|
||||
|
||||
// Only do blinking if the fight has ended.
|
||||
if (!fightEnded) {
|
||||
return; // Skip the rest until fightEnded becomes true.
|
||||
}
|
||||
|
||||
// Each full blink cycle includes an "on" and "off" state.
|
||||
// Therefore, we count transitions: total transitions = END_BLINK_COUNT * 2.
|
||||
const int totalTransitions = END_BLINK_COUNT * 2 + 1;
|
||||
|
||||
// If we haven't completed our full blink sequence, manage timing:
|
||||
if (endBlinkTransitions < totalTransitions) {
|
||||
unsigned long currentMillis = millis();
|
||||
if (currentMillis - lastEndBlinkTime >= END_BLINK_INTERVAL) {
|
||||
endBlinkState = !endBlinkState; // Toggle between on and off states.
|
||||
endBlinkTransitions++; // Count this toggle.
|
||||
lastEndBlinkTime = currentMillis; // Reset the timer.
|
||||
}
|
||||
|
||||
// Depending on the blink state, update the display.
|
||||
// When endBlinkState is true, use the normal brightness.
|
||||
// When false, replace CLOCK_LED_BRIGHTNESS with 0 to blank the display.
|
||||
if (endBlinkState) {
|
||||
sendTimeDisplay(0, 0, 255, 165, 0, CLOCK_LED_BRIGHTNESS);
|
||||
} else {
|
||||
sendTimeDisplay(0, 0, 255, 165, 0, 0);
|
||||
}
|
||||
} else {
|
||||
// After completing the blink sequence, ensure that the display remains on.
|
||||
sendTimeDisplay(0, 0, 255, 165, 0, CLOCK_LED_BRIGHTNESS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
void loop() {
|
||||
// Poll and update low-level I/O and LEDs.
|
||||
pollInput();
|
||||
checkPIT();
|
||||
arenaLIGHT();
|
||||
statusLEDs();
|
||||
updateTEAMLEDs();
|
||||
|
||||
|
||||
// Process button events.
|
||||
handleStartButton();
|
||||
handleForcedStartButton();
|
||||
handlePauseButton();
|
||||
handlePitButton();
|
||||
handleAutoPitRelease();
|
||||
|
||||
|
||||
// Process tap-out sequences.
|
||||
if (!switchRUMBLE.on()) {
|
||||
handleRedTapOut();
|
||||
handleBlueTapOut();
|
||||
}
|
||||
|
||||
// Handle reset
|
||||
if (buttonRESETvar) {
|
||||
buttonRESETvar = false;
|
||||
PITreleased = false;
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
// Ready Countdown to Fight Countdown transition
|
||||
if (ReadyCountDown.remaining() == 0 && ARENA_READY) {
|
||||
ARENA_READY = false;
|
||||
REDTEAM_LED(false);
|
||||
BLUETEAM_LED(false);
|
||||
if (!switchRUMBLE.on()) {
|
||||
FightCountDown.start(countdownTIME);
|
||||
} else if (switchRUMBLE.on()) {
|
||||
rumbleTIME.start();
|
||||
}
|
||||
}
|
||||
// Transition from ReadyCountDown to the appropriate fight countdown.
|
||||
handleCountdownTransition();
|
||||
|
||||
// If either team has tapped out, skip updating the display.
|
||||
if (buttonREDTEAMtapout || buttonBLUETEAMtapout) {
|
||||
return;
|
||||
}
|
||||
// End display
|
||||
handleFightEnd();
|
||||
|
||||
// Display the Ready CountDown timer if it is running.
|
||||
if (ReadyCountDown.isRunning()) {
|
||||
sendTimeDisplay((ReadyCountDown.remaining()/60%60), (ReadyCountDown.remaining()%60), 255, 165, 0, CLOCK_LED_BRIGHTNESS);
|
||||
return;
|
||||
}
|
||||
|
||||
// When the RUMBLE switch is active, always show the rumble timer.
|
||||
if (switchRUMBLE.on()) {
|
||||
sendTimeDisplay((rumbleTIME.elapsed()/60%60), (rumbleTIME.elapsed()%60), 0, 255, 255, CLOCK_LED_BRIGHTNESS);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the fight countdown isn't running and isn't paused, determine which countdown color to use.
|
||||
if (!FightCountDown.isRunning() && !countdownPAUSED) {
|
||||
// Choose green if both buttons are active; otherwise, choose magenta.
|
||||
if (buttonREDTEAMvar && buttonBLUETEAMvar) {
|
||||
sendTimeDisplay((countdownTIME/60%60), (countdownTIME%60), 0, 255, 0, CLOCK_LED_BRIGHTNESS);
|
||||
} else {
|
||||
sendTimeDisplay((countdownTIME/60%60), (countdownTIME%60), 255, 0, 255, CLOCK_LED_BRIGHTNESS);
|
||||
}
|
||||
} else {
|
||||
// Otherwise, show the fight countdown timer.
|
||||
sendTimeDisplay((FightCountDown.remaining()/60%60), (FightCountDown.remaining()%60), 0, 255, 0, CLOCK_LED_BRIGHTNESS);
|
||||
// When no team is in a tap‐out sequence and fight hasn't ended, update the display.
|
||||
if (!redTapOutActive && !blueTapOutActive && !fightEnded) {
|
||||
updateDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,9 @@ const int COLONArray [] = {224,225,226,227,228,229,230,231};
|
||||
|
||||
// tap out
|
||||
const int LitArrayTap [] = {0,1,2,3,4,5,6,7,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151};
|
||||
const int LitArrayOut [] = {0,1,2,3,4,5,6,7,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159};
|
||||
//const int LitArrayOut [] = {0,1,2,3,4,5,6,7,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159};
|
||||
const int LitArrayOut [] = {56,57,58,59,60,61,62,63,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215};
|
||||
|
||||
|
||||
// set digits of the timer/clock, use: setDIGIT(<digit ID>, <Number>, <red channel intensity 0-255>, <green channel intensity 0-255>, <blue channel intensity 0-255>)
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// 48:27:E2:50:86:84
|
||||
|
||||
#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 <Preferences.h> // automatically installed for ESP32 boards
|
||||
#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<WS2812B, LED_DATA_PIN_TIMER, GRB>(leds_TIMER, NUM_LEDS_TIMER); // GRB ordering is typical
|
||||
//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
|
||||
// default screen, just to show it's up and running but hasn't received any data, should display 00:00 in blue
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#define ACTUATED_PULSE 1000 // Actuated position
|
||||
|
||||
// Define the reset time in milliseconds
|
||||
#define RESET_TIME_MS 500
|
||||
#define RESET_TIME_MS 1000
|
||||
|
||||
// Create two Servo objects for the two servos
|
||||
Servo servo1;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// ROFLS+ Referee Remote
|
||||
|
||||
#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 <Preferences.h> // automatically installed for ESP32 boards
|
||||
#include <avdweb_Switch.h> // https://github.com/avdwebLibraries/avdweb_Switch
|
||||
@@ -14,6 +15,11 @@
|
||||
const bool writeBoardID = false;
|
||||
int boardID = 0;
|
||||
|
||||
// set Arena, switches the MAC addresses out
|
||||
// 1 = Arena A (Ant)
|
||||
// 2 = Arena B (Beetle)
|
||||
#define ARENA 1
|
||||
|
||||
// Hardware connections
|
||||
#define START_BTN_PIN 10
|
||||
#define PAUSE_BTN_PIN 8
|
||||
@@ -45,7 +51,17 @@ bool sendDATAvar = false;
|
||||
// ESP-NOW config
|
||||
|
||||
// REPLACE WITH YOUR RECEIVER MAC Address
|
||||
uint8_t broadcastAddress[] = {0x84, 0xFC, 0xE6, 0xC7, 0x1A, 0x8C};
|
||||
#if ARENA == 1
|
||||
// A Arena Controller MAC Address
|
||||
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
|
||||
// Must match the receiver structure
|
||||
@@ -74,12 +90,10 @@ typedef struct struct_message_receive {
|
||||
// Create instance for receiving ESP-NOW data
|
||||
struct_message_receive receiveDATA;
|
||||
|
||||
esp_now_peer_info_t peerInfo;
|
||||
|
||||
Preferences preferences;
|
||||
|
||||
// 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.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
|
||||
}
|
||||
@@ -126,13 +140,25 @@ void setup() {
|
||||
// get the status of Transmitted packet
|
||||
esp_now_register_send_cb(OnDataSent);
|
||||
|
||||
// Register peer
|
||||
memcpy(peerInfo.peer_addr, broadcastAddress, 6);
|
||||
peerInfo.channel = 0;
|
||||
peerInfo.encrypt = false;
|
||||
// Register peer 1 (arena controller)
|
||||
esp_now_peer_info_t peerInfo1 = {};
|
||||
memcpy(peerInfo1.peer_addr, broadcastAddress, 6);
|
||||
peerInfo1.channel = 0;
|
||||
peerInfo1.encrypt = false;
|
||||
// Add peer
|
||||
if (esp_now_add_peer(&peerInfo) != ESP_OK){
|
||||
Serial.println("Failed to add peer");
|
||||
if (esp_now_add_peer(&peerInfo1) != ESP_OK){
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -171,6 +197,8 @@ void sendDATA_to_controller() {
|
||||
sendDATA.buttonBLUETEAMtapout = buttonBLUETEAMtapout;
|
||||
// send data
|
||||
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
|
||||
// if (result == ESP_OK) {
|
||||
// Serial.println("Sent with success");
|
||||
|
||||
@@ -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 |
|
||||
Reference in New Issue
Block a user