//Copyright (C) 2010-2014 Henk Jegers //Copyright (C) 2019 giuliof (GOLEM) //This program is free software: you can redistribute it and/or modify //it under the terms of the GNU General Public License as published by //the Free Software Foundation, either version 3 of the License, or //(at your option) any later version. //This program is distributed in the hope that it will be useful, //but WITHOUT ANY WARRANTY; without even the implied warranty of //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //GNU General Public License for more details. //You should have received a copy of the GNU General Public License //along with this program. If not, see . //for questions email to info@archeryclock.com // version MEGA01 Initial version // version MEGA01_1 Adding robustness for receiving ABCD detailes which is implemented in AC 232. Using the ABCD details information is not yet implemented in MEGA01_1 Needs archeryclock version 2320 or newer. // version MEGA01_2 Added end number information. Needs Archeryclock2401 or newer. (older archeryclock versions don't sent the end number information to arduine) // version MEGA02_1 Changes because of a change in protocoll for AC 241 (nr of archers and E and F shooters) and added functionality: emergency stop, add archers E and F. Changes for signal robustness. (0 (0000) is sent as 12 (1100) to prevent decoding issues when 3 times 0 is sent (000000000000) // #define GOLEM_PANEL // Uncomment to enable nRF24 module // #define HAS_WIFI #include "utils.h" #ifdef HAS_WIFI #include #include #endif /* * * * * * * * * * NRF24 Radio Module * * * * * * * * * */ #ifdef HAS_WIFI uint64_t device_address = 0x0110104334LL; //Hardware configuration: Set up nRF24L01 radio on SPI bus plus pins 40 (CE) & 41 (CSN) RF24 radio(40, 41); #endif /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // Commands data structures enum PanelSide_t { LEFT = 0, RIGHT = 1 }; enum BuzzerStatus_t { BUZZER_OFF, BUZZER_ON }; struct cfg_t { PanelSide_t PanelSide : 1; BuzzerStatus_t BuzzerStatus : 1; uint8_t abcd : 3; // unclear meaning uint8_t competition : 3; // unclear meaning uint8_t GroupsNumber : 6; uint8_t : 0; } cfg; struct trafficvalue_t { uint8_t cmd : 3; // < 0b111 BuzzerStatus_t buzzer : 1; uint8_t TrafficLightLeft : 3; uint8_t TrafficLightRight : 3; uint8_t ArcherGroups : 6; //uint8_t : 0; }; struct statevalue_t { uint8_t cmd : 4; // = 0b1111 uint8_t abcd : 3; // unclear meaning uint8_t competition : 3; // unclear meaning uint8_t : 3; uint8_t GroupsNumber : 6; //uint8_t : 0; }; struct digitsvalue_t { uint8_t cmd : 1; PanelSide_t side : 1; uint8_t sideOverride : 1; uint8_t minutes : 1; uint8_t rDigit : 4; uint8_t mDigit : 4; uint8_t lDigit : 4; }; union ReceivedCommand_t { int raw; trafficvalue_t t; statevalue_t s; digitsvalue_t d; } ReceivedCommand; #ifndef GOLEM_PANEL #define DOTS_bm _BV(7) #else #define DOTS_PIN 23 #endif #define ABCD_PORT A #define LEFTDIGIT_PORT K #define MIDDIGIT_PORT L #define RIGHTDIGIT_PORT C #define TRAFLIGHT_PORT F #define HORNP_PIN 7 #define REDP_PIN 6 #define ORANGEP_PIN 5 #define GREENP_PIN 4 #ifndef GOLEM_PANEL #define A_PIN 25 #define B_PIN 24 #define C_PIN 23 #define D_PIN 22 #define notA_PIN 29 #define notB_PIN 28 #define notC_PIN 27 #define notD_PIN 26 #else #define A_PIN 22 #define B_PIN 24 #define C_PIN 26 #define D_PIN 27 #define AC_PIN 25 #define BD_PIN 28 #endif #define LR_PIN 10 #define MUTE_PIN 9 #define BRIGHT_PIN 8 // "translation" for seven segment #define CHAR_P 15 #define CHAR_DASH 10 #ifndef GOLEM_PANEL const uint8_t segment[16] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x40, 0x00, 0x3f, 0x00, 0x00, 0x73}; //0,1,2,3,4,5,6,7,8,9,-, , , , ,P #else const uint8_t segment[16] = {0xbe, 0x82, 0x6e, 0xea, 0xd2, 0xf8, 0xfc, 0x8a, 0xfe, 0xfa, 0x40, 0x0, 0xbe, 0x0, 0x0, 0x5e}; //0,1,2,3,4,5,6,7,8,9,-, , , , ,P #endif boolean dataAvailable(char &digit) { if (Serial1.available()) { digit = Serial1.read(); // Read serial buffer return 1; } else if (Serial.available()) { digit = Serial.read(); // Read serial buffer return 1; } return 0; } boolean parseCommand(ReceivedCommand_t &ReceivedCommand) { // A command is received char digit; #ifdef HAS_WIFI // Parse command from nRF packets if (radio.available()) { char inputString[6] = {0}; // Variable for the received timestamp radio.read(&inputString, 5); ReceivedCommand.raw = atoi(inputString); return 1; } #endif if (dataAvailable(digit)) { /* ** Buffer to store received char ** * Data is an integer string terminated with newline '\n' * */ static String inputString = ""; // Check if string terminator if (digit == '\r') { // Make string conversion ReceivedCommand.raw = inputString.toInt(); inputString = ""; // Now decode command and apply to the panel // Refer to documentation for command structure } else // if (char) digit != '\n' { inputString += digit; } return 1; } return 0; } void setup() { pinMode(LR_PIN, INPUT_PULLUP); pinMode(MUTE_PIN, INPUT_PULLUP); pinMode(BRIGHT_PIN, INPUT_PULLUP); CATDDR(ABCD_PORT) = 0xFF; CATDDR(LEFTDIGIT_PORT) = 0xFF; CATDDR(MIDDIGIT_PORT) = 0xFF; CATDDR(RIGHTDIGIT_PORT) = 0xFF; CATDDR(TRAFLIGHT_PORT) = 0xFF; pinMode(REDP_PIN, OUTPUT); pinMode(ORANGEP_PIN, OUTPUT); pinMode(GREENP_PIN, OUTPUT); pinMode(HORNP_PIN, OUTPUT); // Load system configuration cfg.BuzzerStatus = digitalRead(MUTE_PIN) ? BUZZER_ON : BUZZER_OFF; cfg.PanelSide = digitalRead(LR_PIN) ? LEFT : RIGHT; // Communication w/PC for debug or other puroposes Serial.begin(9600); Serial.println("Arduino Serial Link"); Serial.println("connected!"); // Communication w/XBEE Serial1.begin(9600); #ifdef HAS_WIFI // Communication with nRF24 radio.begin(); // Set the PA Level low to prevent power supply related issues since this is a // getting_started sketch, and the likelihood of close proximity of the devices. RF24_PA_MAX is default. radio.setPALevel(RF24_PA_LOW); // Setup for the USB module radio.setDataRate(RF24_2MBPS); radio.setCRCLength(RF24_CRC_16); radio.setRetries(15, 15); radio.setChannel(40); radio.setPayloadSize(32); radio.openReadingPipe(1, device_address); // Start the radio listening for data radio.startListening(); #endif } void loop() { if (parseCommand(ReceivedCommand)) { // Parsing of DIGITS command if (ReceivedCommand.d.cmd == 0b0) { // Parse command only if is same side as this panel (or comamnd says to override) if (ReceivedCommand.d.sideOverride || (ReceivedCommand.d.side == cfg.PanelSide)) { CATPORT(MIDDIGIT_PORT) = segment[ReceivedCommand.d.mDigit]; CATPORT(RIGHTDIGIT_PORT) = segment[ReceivedCommand.d.rDigit]; // Turn on dot between minutes and seconds only if count is in minutes and its digit is a representable value (not dash) #ifndef GOLEM_PANEL if (ReceivedCommand.d.lDigit != CHAR_DASH && ReceivedCommand.d.minutes) CATPORT(LEFTDIGIT_PORT) = segment[ReceivedCommand.d.lDigit] | DOTS_bm; else CATPORT(LEFTDIGIT_PORT) = segment[ReceivedCommand.d.lDigit]; #else CATPORT(LEFTDIGIT_PORT) = segment[ReceivedCommand.d.lDigit]; #endif #ifdef GOLEM_PANEL if (ReceivedCommand.d.lDigit != CHAR_DASH && ReceivedCommand.d.minutes) digitalWrite(DOTS_PIN, HIGH); else digitalWrite(DOTS_PIN, LOW); #endif } } // Parsing of TRAFFIC command else if (ReceivedCommand.t.cmd == 0b001) { // Set buzzer depending on mute switch if (cfg.BuzzerStatus == BUZZER_ON) { digitalWrite(HORNP_PIN, ReceivedCommand.t.buzzer); } // Set Traffic lights depending on side switch if (cfg.PanelSide == RIGHT) { digitalWrite(GREENP_PIN, ReceivedCommand.t.TrafficLightRight & _BV(0)); // green right side digitalWrite(ORANGEP_PIN, ReceivedCommand.t.TrafficLightRight & _BV(1)); // orange right side digitalWrite(REDP_PIN, ReceivedCommand.t.TrafficLightRight & _BV(2)); // red right side #ifndef GOLEM_PANEL CATPORT(TRAFLIGHT_PORT) = (ReceivedCommand.t.TrafficLightRight & _BV(0) ? 0b1001 << 2 : 0) | (ReceivedCommand.t.TrafficLightRight & _BV(1) ? 0b1001 << 1 : 0) | (ReceivedCommand.t.TrafficLightRight & _BV(2) ? 0b1001 << 0 : 0); #else CATPORT(TRAFLIGHT_PORT) = (ReceivedCommand.t.TrafficLightRight & _BV(0) ? 0b11 << 6 : 0) | (ReceivedCommand.t.TrafficLightRight & _BV(1) ? 0b1001 << 2 : 0) | (ReceivedCommand.t.TrafficLightRight & _BV(2) ? 0b11 << 3 : 0); #endif } else { digitalWrite(GREENP_PIN, ReceivedCommand.t.TrafficLightLeft & _BV(0)); // green left side digitalWrite(ORANGEP_PIN, ReceivedCommand.t.TrafficLightLeft & _BV(1)); // orange left side digitalWrite(REDP_PIN, ReceivedCommand.t.TrafficLightLeft & _BV(2)); // red left side #ifndef GOLEM_PANEL CATPORT(TRAFLIGHT_PORT) = (ReceivedCommand.t.TrafficLightLeft & _BV(0) ? 0b1001 << 2 : 0) | (ReceivedCommand.t.TrafficLightLeft & _BV(1) ? 0b1001 << 1 : 0) | (ReceivedCommand.t.TrafficLightLeft & _BV(2) ? 0b1001 << 0 : 0); #else CATPORT(TRAFLIGHT_PORT) = (ReceivedCommand.t.TrafficLightLeft & _BV(0) ? 0b11 << 6 : 0) | (ReceivedCommand.t.TrafficLightLeft & _BV(1) ? 0b1001 << 2 : 0) | (ReceivedCommand.t.TrafficLightLeft & _BV(2) ? 0b11 << 3 : 0); #endif } // Set ABCD(EF) digitalWrite(A_PIN, ReceivedCommand.t.ArcherGroups & _BV(0)); //A digitalWrite(B_PIN, ReceivedCommand.t.ArcherGroups & _BV(1)); //B digitalWrite(C_PIN, ReceivedCommand.t.ArcherGroups & _BV(2)); //C digitalWrite(D_PIN, ReceivedCommand.t.ArcherGroups & _BV(3)); //D #ifdef GOLEM_PANEL digitalWrite(AC_PIN, ReceivedCommand.t.ArcherGroups & (_BV(0) | _BV(2))); //AC digitalWrite(BD_PIN, ReceivedCommand.t.ArcherGroups & (_BV(1) | _BV(3))); //BD #endif // EF not implemented in this hardware /* digitalWrite(6, (((statevalue >> 7) & B00000111) != 2) and (trafficvalue & 0x4000)); //E digitalWrite(7, (((statevalue >> 7) & B00000111) != 2) and (trafficvalue & 0x8000)); //F digitalWrite(A15, (((statevalue >> 7) & B00000111) == 2) and (trafficvalue & 0x4000)); //green right arrow for fita finals digitalWrite(39, (((statevalue >> 7) & B00000111) == 2) and (trafficvalue & 0x8000)); //green left arrow for fita finals */ // Set not{A,B,C,D,(E,F)}; EF not implemented in this hardware // Note: GOLEM panel does not have not{A,B,C,D} #ifndef GOLEM_PANEL if (cfg.abcd != 6) { // not A is on if archer group A is not selected digitalWrite(notA_PIN, !(ReceivedCommand.t.ArcherGroups & _BV(0))); // not B ~ not D are on if groups are effectively present and are not selected if (cfg.competition != 2 && cfg.competition != 4) { digitalWrite(notB_PIN, cfg.GroupsNumber > 0 && !(ReceivedCommand.t.ArcherGroups & _BV(1))); digitalWrite(notC_PIN, cfg.GroupsNumber > 1 && !(ReceivedCommand.t.ArcherGroups & _BV(2))); digitalWrite(notD_PIN, cfg.GroupsNumber > 2 && !(ReceivedCommand.t.ArcherGroups & _BV(3))); } else { digitalWrite(notB_PIN, LOW); digitalWrite(notC_PIN, LOW); digitalWrite(notD_PIN, LOW); } } else { digitalWrite(notA_PIN, ReceivedCommand.t.buzzer == BUZZER_ON); digitalWrite(notB_PIN, ReceivedCommand.t.buzzer == BUZZER_ON); digitalWrite(notC_PIN, ReceivedCommand.t.buzzer == BUZZER_ON); digitalWrite(notD_PIN, ReceivedCommand.t.buzzer == BUZZER_ON); } #endif } // Parsing of STATE command else if (ReceivedCommand.s.cmd == 0b1111) { cfg.abcd = ReceivedCommand.s.abcd; cfg.competition = ReceivedCommand.s.competition; cfg.GroupsNumber = ReceivedCommand.s.GroupsNumber; } } // This feature is not implemented in this hardware /* lefend = ((endnrvalue >> 4) & B00001111); midend = ((endnrvalue >> 8) & B00001111); rigend = ((endnrvalue >> 12) & B00001111); if (lefend == 15) { comend = lefend; } else { comend = midend; }; digitalWrite(24, ((segment[comend] & 0x001)) ? HIGH : LOW); //left combined end digit segment A digitalWrite(26, ((segment[comend] & 0x002)) ? HIGH : LOW); //left combined end digit segment B digitalWrite(28, ((segment[comend] & 0x004)) ? HIGH : LOW); //left combined end digit segment C digitalWrite(30, ((segment[comend] & 0x008)) ? HIGH : LOW); //left combined end digit segment D digitalWrite(32, ((segment[comend] & 0x010)) ? HIGH : LOW); //left combined end digit segment E digitalWrite(34, ((segment[comend] & 0x020)) ? HIGH : LOW); //left combined end digit segment F digitalWrite(36, ((segment[comend] & 0x040)) ? HIGH : LOW); //left combined end digit segment G digitalWrite(40, ((segment[rigend] & 0x001)) ? HIGH : LOW); //right end digit segment A digitalWrite(42, ((segment[rigend] & 0x002)) ? HIGH : LOW); //right end digit segment B digitalWrite(44, ((segment[rigend] & 0x004)) ? HIGH : LOW); //right end digit segment C digitalWrite(46, ((segment[rigend] & 0x008)) ? HIGH : LOW); //right end digit segment D digitalWrite(48, ((segment[rigend] & 0x010)) ? HIGH : LOW); //right end digit segment E digitalWrite(50, ((segment[rigend] & 0x020)) ? HIGH : LOW); //right end digit segment F digitalWrite(52, ((segment[rigend] & 0x040)) ? HIGH : LOW); //right end digit segment G */ // This feature is not implemented in this hardware /* //Read buttons buttonvalue = 0; // if (digitalRead(A4)){buttonvalue=5;}; // if (digitalRead(A3)){buttonvalue=4;}; // if (digitalRead(A0)){buttonvalue=1;}; //next // if (digitalRead(A1)){buttonvalue=2;}; // if (digitalRead(A2)){buttonvalue=3;}; if (loop1 == 0) { if (buttonvalue != remember1) { Serial.print(buttonvalue); delay(3); remember1 = buttonvalue; loop1 = 15; //digitalWrite(2, 1); }; } else { delay(10); //if (loop1>7){blinkr=1;}; loop1--; }; */ }