447 lines
14 KiB
C++
447 lines
14 KiB
C++
//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 <http://www.gnu.org/licenses/>.
|
|
|
|
//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 <SPI.h>
|
|
#include <RF24.h>
|
|
#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--;
|
|
};
|
|
*/
|
|
}
|