SportIduino/FWsrc/sportiduino-3.11.0/firmware/MasterStation/MasterStation.ino

1195 lines
33 KiB
C++
Executable File

#include <sportiduino.h>
#include "sportidentprotocol.h"
#ifndef HW_VERS
#define HW_VERS 1
#endif
#define FW_MAJOR_VERS 11
// If FW_MINOR_VERS more than MAX_FW_MINOR_VERS this is beta version HW_VERS.FW_MINOR_VERS.0-beta.X
// where X is (FW_MINOR_VERS - MAX_FW_MINOR_VERS)
#define FW_MINOR_VERS 0
//-----------------------------------------------------------
// HARDWARE
#define BUZZ_PIN 3
#define LED_PIN 4
#define RC522_RST_PIN 9
#define RC522_SS_PIN 10
// Set BUZZER_FREQUENCY by running "make buzzfreq=2500"
#ifndef BUZZER_FREQUENCY
// or change here
#define BUZZER_FREQUENCY 4000 // 0 for buzzer with generator
#endif
//-----------------------------------------------------------
// CONST
#define SERIAL_START_BYTE 0xFE
enum Error {
ERROR_SERIAL = 0x01,
ERROR_CARD_WRITE = 0x02,
ERROR_CARD_READ = 0x03,
ERROR_EEPROM_READ = 0x04,
ERROR_CARD_NOT_FOUND = 0x05,
ERROR_UNKNOWN_CMD = 0x06,
ERROR_BAD_DATASIZE = 0x07,
ERROR_BAD_SETTINGS = 0x08
};
enum Resp {
RESP_FUNC_BACKUP = 0x61,
RESP_FUNC_MARKS = 0x63,
RESP_FUNC_RAW_DATA = 0x65,
RESP_FUNC_VERSION = 0x66,
RESP_FUNC_SETTINGS = 0x67,
RESP_FUNC_MODE = 0x69,
RESP_FUNC_CARD_TYPE = 0x70,
RESP_FUNC_ERROR = 0x78,
RESP_FUNC_OK = 0x79
};
#define EEPROM_CONFIG_ADDR 0x3EE
const uint8_t NTAG213_PAGE4_FACTORY_DATA[] = {0x01, 0x03, 0xa0, 0x0c};
const uint8_t NTAG215_216_PAGE4_FACTORY_DATA[] = {0x03, 0x00, 0xfe, 0x00};
//-----------------------------------------------------------
using SiProto = SportidentProtocol;
struct __attribute__((packed)) Configuration {
uint8_t antennaGain;
int8_t timezone; // timezone in 1/4 hours
// v1.11 and later
uint32_t ntagAuthPassword;
};
//-----------------------------------------------------------
// FUNCTIONS
inline void beep(uint16_t ms, uint8_t n) { beep_w(LED_PIN, BUZZ_PIN, BUZZER_FREQUENCY, ms, n); }
inline void beepTimeCardOk() { beep_w(LED_PIN, BUZZ_PIN, BUZZER_FREQUENCY, 500, 3, 500); delay(500); beep(1000, 1); }
inline void beepError() { beep(100, 3); }
inline void beepOk() { beep(500, 1); }
// Declatarions for building by Arduino-Makefile
void signalError(uint8_t error);
void handleCmd(uint8_t cmdCode, uint8_t *data, uint8_t dataSize);
void handleSiCmd(uint8_t cmdCode, uint8_t *data, uint8_t dataSize);
void sieDetectCard();
void sieCardRemoved();
void sieCardReadError();
bool sieSendDataBlock(uint8_t blockNumber);
bool sieSendAllDataBlocks(bool shortFormat);
//-----------------------------------------------------------
// VARIABLES
static Configuration config;
static Rfid rfid;
static uint8_t password[3] = {0, 0, 0};
static SerialProtocol serialProto;
static SiProto siProto;
static bool sieMode = true; // Sportident emulation mode (continuos readout)
void setup() {
pinMode(LED_PIN, OUTPUT);
pinMode(BUZZ_PIN, OUTPUT);
pinMode(RC522_RST_PIN, OUTPUT);
pinMode(RC522_SS_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
digitalWrite(BUZZ_PIN, LOW);
digitalWrite(RC522_RST_PIN, LOW);
readConfig((uint8_t*)&config, sizeof(Configuration), EEPROM_CONFIG_ADDR);
if(config.antennaGain > MAX_ANTENNA_GAIN || config.antennaGain < MIN_ANTENNA_GAIN) {
config.antennaGain = DEFAULT_ANTENNA_GAIN;
config.timezone = 0;
config.ntagAuthPassword = 0xFFFFFFFF;
}
rfid.init(RC522_SS_PIN, RC522_RST_PIN, config.antennaGain);
rfid.setAuthPassword((uint8_t*)&config.ntagAuthPassword);
serialProto.init(SERIAL_START_BYTE, 38400);
digitalWrite(LED_PIN, HIGH);
delay(50);
digitalWrite(LED_PIN, LOW);
}
void loop() {
if(sieMode) {
rfid.begin(config.antennaGain);
sieDetectCard();
rfid.end();
delay(50);
}
}
void serialEvent() {
bool error = false;
uint8_t cmdCode = 0;
uint8_t dataSize = 0;
uint8_t *data = serialProto.read(&error, &cmdCode, &dataSize);
if(error) {
signalError(ERROR_SERIAL);
return;
}
if(data) {
sieMode = false;
handleCmd(cmdCode, data, dataSize);
return;
}
data = siProto.read(&error, &cmdCode, &dataSize);
if(error) {
siProto.error();
return;
}
if(data) {
sieMode = true;
handleSiCmd(cmdCode, data, dataSize);
return;
}
serialProto.dropByte();
}
void signalError(uint8_t error) {
serialProto.start(RESP_FUNC_ERROR);
serialProto.add(error);
serialProto.add((uint8_t)rfid.getCardType());
serialProto.send();
beepError();
}
void signalOK(bool beep = true) {
serialProto.start(RESP_FUNC_OK);
serialProto.add((uint8_t)rfid.getCardType());
serialProto.send();
if(beep) {
beepOk();
}
}
uint8_t writeMasterCard(uint8_t masterCode, byte *data = NULL, uint16_t size = 0) {
if(!rfid.isCardDetected()) {
return ERROR_CARD_NOT_FOUND;
}
byte head[] = {
0, masterCode, 255, FW_MAJOR_VERS,
password[0], password[1], password[2], 0
};
if(!rfid.cardWrite(CARD_PAGE_INIT, head, sizeof(head))) {
return ERROR_CARD_WRITE;
}
if(data && size > 0) {
if(!rfid.cardWrite(CARD_PAGE_INIT + 2, data, size)) {
return ERROR_CARD_WRITE;
}
}
return 0;
}
void funcWriteMasterTime(uint8_t *serialData, uint8_t dataSize) {
if(dataSize < 6) {
signalError(ERROR_BAD_DATASIZE);
return;
}
byte data[] = {
serialData[1], serialData[0], serialData[2], 0, // month, year, day, 0
serialData[3], serialData[4], serialData[5], 0 // hour, minute, second, 0
};
uint8_t error = writeMasterCard(MASTER_CARD_SET_TIME, data, sizeof(data));
if(error) {
signalError(error);
} else {
signalOK(false);
beepTimeCardOk();
}
}
void funcWriteMasterNum(uint8_t *serialData, uint8_t dataSize) {
if(dataSize < 1) {
signalError(ERROR_BAD_DATASIZE);
return;
}
byte data[] = {serialData[0], 0, 0, 0}; // station num
uint8_t error = writeMasterCard(MASTER_CARD_SET_NUMBER, data, sizeof(data));
if(error) {
signalError(error);
} else {
signalOK();
}
}
void funcWriteMasterConfig(uint8_t *serialData, uint8_t dataSize) {
if(dataSize < 6) {
signalError(ERROR_BAD_DATASIZE);
return;
}
uint8_t error = writeMasterCard(MASTER_CARD_CONFIG, serialData, dataSize);
if(error) {
signalError(error);
} else {
signalOK();
}
}
void funcWriteMasterPassword(uint8_t *serialData, uint8_t dataSize) {
if(dataSize < 3) {
signalError(ERROR_BAD_DATASIZE);
return;
}
uint8_t error = writeMasterCard(MASTER_CARD_PASSWORD, serialData, dataSize);
if(error) {
signalError(error);
} else {
signalOK();
}
}
void funcApplyPassword(uint8_t *serialData, uint8_t dataSize) {
if(dataSize < 3) {
signalError(ERROR_BAD_DATASIZE);
return;
}
memcpy(password, serialData, 3);
signalOK();
}
void funcReadSettings(uint8_t *, uint8_t ) {
serialProto.start(RESP_FUNC_SETTINGS);
// Send first 2 bytes of configuration (without ntagAuthPassword)
serialProto.add((uint8_t*)&config, 2);
serialProto.send();
}
void funcWriteMasterAuthPassword(uint8_t *serialData, uint8_t dataSize) {
if(dataSize < 4) {
signalError(ERROR_BAD_DATASIZE);
return;
}
uint8_t error = writeMasterCard(MASTER_CARD_AUTH_PASSWORD, serialData, dataSize);
if(error) {
signalError(error);
} else {
signalOK();
}
}
void funcWriteSettings(uint8_t *serialData, uint8_t dataSize) {
if(dataSize != sizeof(Configuration) && dataSize != 2 /*old config format*/) {
signalError(ERROR_BAD_DATASIZE);
return;
}
Configuration *newConfig = (Configuration*)serialData;
if(newConfig->antennaGain < MIN_ANTENNA_GAIN || newConfig->antennaGain > MAX_ANTENNA_GAIN
|| newConfig->timezone < -12*4 || newConfig->timezone > 14*4) {
signalError(ERROR_BAD_SETTINGS);
return;
}
memcpy(&config, newConfig, dataSize);
writeConfig((uint8_t*)&config, sizeof(Configuration), EEPROM_CONFIG_ADDR);
rfid.setAntennaGain(config.antennaGain);
rfid.setAuthPassword((uint8_t*)&config.ntagAuthPassword);
signalOK();
}
bool clearCard() {
uint8_t maxPage = rfid.getCardMaxPage();
// Clear card from last page
uint8_t c = 0;
for(uint8_t page = maxPage - 3; page > CARD_PAGE_INIT_TIME; page -= 4) {
if(c % 10 == 0) {
digitalWrite(LED_PIN, HIGH);
} else if(c % 5 == 0) {
digitalWrite(LED_PIN, LOW);
}
++c;
if(!rfid.cardErase4Pages(page)) {
return false;
}
}
uint8_t tail = (maxPage - CARD_PAGE_INIT_TIME)%4;
// Erase the tail if necessary
if (tail > 0) {
// Erase the remaining pages individually
for (uint8_t i = tail; i > 0; --i) {
uint8_t pageToErase = CARD_PAGE_INIT_TIME + i;
// Attempt to erase a single page
if (!rfid.cardPageErase(pageToErase)) {
return false;
}
}
}
return true;
}
void funcInitParticipantCard(uint8_t *serialData, uint8_t dataSize) {
if(dataSize < 14) {
signalError(ERROR_BAD_DATASIZE);
return;
}
if(!rfid.isCardDetected()) {
signalError(ERROR_CARD_NOT_FOUND);
return;
}
bool writeProtection = false;
bool readProtection = false;
if(dataSize > 14) {
uint8_t flags = serialData[14];
writeProtection = (flags & 1);
readProtection = (flags & 2);
}
if(!rfid.cardEnableDisableAuthentication(writeProtection, readProtection)) {
signalError(ERROR_CARD_WRITE);
return;
}
digitalWrite(LED_PIN, HIGH);
if(!clearCard()) {
signalError(ERROR_CARD_WRITE);
digitalWrite(LED_PIN, LOW);
return;
}
digitalWrite(LED_PIN, LOW);
byte data[] = {
serialData[0], serialData[1], 0, FW_MAJOR_VERS, // card num, 0, fw version
serialData[2], serialData[3], serialData[4], serialData[5], // unixtime
serialData[6], serialData[7], serialData[8], serialData[9], // page6
serialData[10], serialData[11], serialData[12], serialData[13] // page7
};
if(!rfid.cardWrite(CARD_PAGE_INIT, data, sizeof(data))) {
signalError(ERROR_CARD_WRITE);
return;
}
signalOK();
}
void funcWritePages6_7(uint8_t *serialData, uint8_t dataSize) {
if(dataSize < 8) {
signalError(ERROR_BAD_DATASIZE);
return;
}
if(!rfid.isCardDetected()) {
signalError(ERROR_CARD_NOT_FOUND);
return;
}
if(!rfid.cardWrite(CARD_PAGE_INFO1, serialData, 8)) {
signalError(ERROR_CARD_WRITE);
return;
}
signalOK();
}
void funcWriteMasterBackup(uint8_t*, uint8_t) {
uint8_t error = writeMasterCard(MASTER_CARD_READ_BACKUP);
if(error) {
signalError(error);
} else {
signalOK();
}
}
void funcWriteGetInfoCard(uint8_t*, uint8_t) {
uint8_t error = writeMasterCard(MASTER_CARD_STATE);
if(error) {
signalError(error);
} else {
signalOK();
}
}
void funcWriteMasterSleep(uint8_t *serialData, uint8_t dataSize) {
if(dataSize < 6) {
signalError(ERROR_BAD_DATASIZE);
return;
}
// wakeup time
byte data[] = {
serialData[1], serialData[0], serialData[2], 0,
serialData[3], serialData[4], serialData[5], 0
};
uint8_t error = writeMasterCard(MASTER_CARD_SLEEP, data, sizeof(data));
if(error) {
signalError(error);
} else {
signalOK();
}
}
void funcReadBackup(uint8_t*, uint8_t) {
if(!rfid.isCardDetected()) {
signalError(ERROR_CARD_NOT_FOUND);
return;
}
byte pageData[] = {0,0,0,0};
if(!rfid.cardPageRead(CARD_PAGE_INIT, pageData)) {
signalError(ERROR_CARD_READ);
return;
}
serialProto.start(RESP_FUNC_BACKUP);
serialProto.add(pageData[0]); // add station number
uint8_t maxPage = rfid.getCardMaxPage();
if(pageData[3] == 1) { // old format with timestamps
serialProto.add(0xff); // flag: have timestamps
uint16_t timeHigh12bits = 0;
uint32_t initTime = 0;
for(uint8_t page = CARD_PAGE_INFO1; page <= maxPage; ++page) {
if(!rfid.cardPageRead(page, pageData)) {
signalError(ERROR_CARD_READ);
return;
}
if(timeHigh12bits == 0) {
timeHigh12bits = pageData[0] << 8;
timeHigh12bits |= pageData[1] & 0xf0;
initTime = ((uint32_t)pageData[1] & 0x0f) << 16;
initTime |= (uint32_t)pageData[2] << 8;
initTime |= pageData[3];
continue;
}
uint16_t cardNum = pageData[0] << 8;
cardNum |= pageData[1] & 0xff;
cardNum >>= 4;
if(cardNum == 0) {
continue;
}
serialProto.add(pageData[0] >> 4); // card number first byte
serialProto.add(pageData[0] << 4 | pageData[1] >> 4); // card number second byte
uint32_t punchTime = ((uint32_t)pageData[1] & 0x0f) << 16;
punchTime |= (uint32_t)pageData[2] << 8;
punchTime |= pageData[3];
uint16_t currentTimeHigh12bits = timeHigh12bits;
if(punchTime < initTime) {
currentTimeHigh12bits += 0x10;
}
serialProto.add(currentTimeHigh12bits >> 8);
serialProto.add((currentTimeHigh12bits&0xf0) | (pageData[1]&0x0f));
serialProto.add(pageData[2]);
serialProto.add(pageData[3]);
}
} else if(pageData[3] >= 10) { // new format (FW version 10 or greater)
serialProto.add(0xff); // flag: have timestamps
uint16_t lastTimeHigh16bits = 0;
for(uint8_t page = CARD_PAGE_INFO1; page <= maxPage; ++page) {
if(!rfid.cardPageRead(page, pageData)) {
signalError(ERROR_CARD_READ);
return;
}
if(pageData[0] == 0 && pageData[1] == 0) {
uint16_t timeHigh16bits = byteArrayToUint32(pageData) & 0xffff;
if(timeHigh16bits > 0 && timeHigh16bits != lastTimeHigh16bits) {
lastTimeHigh16bits = timeHigh16bits;
}
continue;
}
serialProto.add(pageData[0]); // card number first byte
serialProto.add(pageData[1]); // card number second byte
serialProto.add(lastTimeHigh16bits >> 8);
serialProto.add(lastTimeHigh16bits & 0xff);
serialProto.add(pageData[2]); // timestamps
serialProto.add(pageData[3]); // timestamps
}
}
serialProto.send();
beepOk();
}
void funcReadCard(uint8_t*, uint8_t) {
// Don't signal error to prevent discontiniuos beep in the poll mode
if(!rfid.isCardDetected()) {
return;
}
byte pageData[] = {0,0,0,0};
if(!rfid.cardPageRead(CARD_PAGE_INIT, pageData)) {
signalError(ERROR_CARD_READ);
return;
}
if(pageData[2] == 0xff) {
return;
}
uint8_t *cardNumPointer = pageData;
if(memcmp(pageData, NTAG213_PAGE4_FACTORY_DATA, 4) == 0
|| memcmp(pageData, NTAG215_216_PAGE4_FACTORY_DATA, 4) == 0) {
// Reset card number
cardNumPointer[0] = 0;
cardNumPointer[1] = 0;
}
serialProto.start(RESP_FUNC_MARKS);
// Output the card number
serialProto.add(cardNumPointer[0]);
serialProto.add(cardNumPointer[1]);
if(!rfid.cardPageRead(CARD_PAGE_INIT_TIME, pageData)) {
signalError(ERROR_CARD_READ);
return;
}
uint8_t timeHighByte = pageData[0];
uint32_t initTime = pageData[1];
initTime <<= 8;
initTime |= pageData[2];
initTime <<= 8;
initTime |= pageData[3];
if(!rfid.cardPageRead(CARD_PAGE_INFO1, pageData)) {
signalError(ERROR_CARD_READ);
return;
}
// Output page 6
for(uint8_t i = 0; i < 4; i++) {
serialProto.add(pageData[i]);
}
if(!rfid.cardPageRead(CARD_PAGE_INFO2, pageData)) {
signalError(ERROR_CARD_READ);
return;
}
// Output page 7
for(uint8_t i = 0; i < 4; i++) {
serialProto.add(pageData[i]);
}
uint8_t maxPage = rfid.getCardMaxPage();
for(uint8_t page = CARD_PAGE_START; page <= maxPage; ++page) {
if(!rfid.cardPageRead(page, pageData)) {
signalError(ERROR_CARD_READ);
return;
}
if(pageIsEmpty(pageData)) { // no new punches
break;
}
// Output station number
serialProto.add(pageData[0]);
uint32_t punchTime = pageData[1];
punchTime <<= 8;
punchTime |= pageData[2];
punchTime <<= 8;
punchTime |= pageData[3];
// for example, we have init time 0x00FFFFFF
// all mark time will be 0x01xxxxxx
// in this case we have to add 1 to timeHighByte
if(punchTime < initTime) {
serialProto.add(timeHighByte + 1);
} else {
serialProto.add(timeHighByte);
}
// Output time
serialProto.add(pageData[1]);
serialProto.add(pageData[2]);
serialProto.add(pageData[3]);
}
serialProto.send();
}
void funcReadRawCard(uint8_t* serialData, uint8_t dataSize) {
uint8_t error = ERROR_CARD_NOT_FOUND;
byte pageData[] = {0,0,0,0};
if(rfid.isCardDetected()) {
error = ERROR_CARD_READ;
uint8_t firstPage = CARD_PAGE_INIT;
uint8_t lastPage = rfid.getCardMaxPage();
if (dataSize > 1) {
firstPage = serialData[0];
uint8_t numPages = serialData[1];
numPages = max(numPages, 1);
lastPage = min(firstPage + numPages - 1, lastPage);
}
serialProto.start(RESP_FUNC_RAW_DATA);
for(uint8_t page = firstPage; page <= lastPage; page++) {
if(!rfid.cardPageRead(page, pageData)) {
error = ERROR_CARD_READ;
break;
}
error = 0;
serialProto.add(page);
for(uint8_t i = 0; i < 4; i++) {
serialProto.add(pageData[i]);
}
}
}
if(error) {
signalError(error);
} else {
serialProto.send();
beepOk();
}
}
void funcReadCardType(uint8_t*, uint8_t) {
serialProto.start(RESP_FUNC_CARD_TYPE);
serialProto.add((uint8_t)rfid.getCardType());
serialProto.send();
}
void funcGetVersion(uint8_t*, uint8_t) {
serialProto.start(RESP_FUNC_VERSION);
serialProto.add(HW_VERS);
serialProto.add(FW_MAJOR_VERS);
serialProto.add(FW_MINOR_VERS);
serialProto.send();
}
void callRfidFunction(void (*func)(uint8_t*, uint8_t), uint8_t *data, uint8_t dataSize) {
rfid.begin();
func(data, dataSize);
rfid.end();
}
void handleCmd(uint8_t cmdCode, uint8_t *data, uint8_t dataSize) {
switch(cmdCode) {
case 0x41:
callRfidFunction(funcWriteMasterTime, data, dataSize);
break;
case 0x42:
callRfidFunction(funcWriteMasterNum, data, dataSize);
break;
case 0x43:
callRfidFunction(funcWriteMasterPassword, data, dataSize);
break;
case 0x5A:
callRfidFunction(funcWriteMasterConfig, data, dataSize);
break;
case 0x5B:
callRfidFunction(funcWriteMasterAuthPassword, data, dataSize);
break;
case 0x44:
callRfidFunction(funcInitParticipantCard, data, dataSize);
break;
case 0x45:
callRfidFunction(funcWritePages6_7, data, dataSize);
break;
case 0x46:
funcGetVersion(data, dataSize);
break;
case 0x47:
callRfidFunction(funcWriteMasterBackup, data, dataSize);
break;
case 0x48:
callRfidFunction(funcReadBackup, data, dataSize);
break;
case 0x4A:
funcWriteSettings(data, dataSize);
break;
case 0x4B:
callRfidFunction(funcReadCard, data, dataSize);
break;
case 0x4C:
callRfidFunction(funcReadRawCard, data, dataSize);
break;
case 0x4D:
funcReadSettings(data, dataSize);
break;
case 0x4E:
callRfidFunction(funcWriteMasterSleep, data, dataSize);
break;
case 0x4F:
funcApplyPassword(data, dataSize);
break;
case 0x50:
callRfidFunction(funcWriteGetInfoCard, data, dataSize);
break;
case 0x51:
callRfidFunction(funcReadCardType, data, dataSize);
break;
case 0x58:
beepError();
break;
case 0x59:
beepOk();
break;
default:
signalError(ERROR_UNKNOWN_CMD);
break;
}
}
const uint8_t fakeStationConfig[] = {
0x00, 0x00, 0x00, 0x01, // serial number
0xF7, // SRR-dongle configuration?
0x36, 0x32, 0x33, // firmware (623)
0x0A, 0x01, 0x19, // buid date
0x91, 0x97, // model ID (BSM7-RS232, BSM7-USB)
0x80, // memory size in kB
0x20, 0x0D,
0x4B, 0x08, 0x4E, 0xFA, 0x28,
0x0A, 0x01, 0x19, // battery date
0x00,
0x6D, 0xDD, // battery capacity in Ah (as multiples of 14062.5)
0x00,
0x00, 0x00, // backup ptr 1
0x18, 0x04, 0xFF,
0x03, 0x80, // backup ptr 2
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x4D, 0x70, 0xFF, 0xFF, 0xFF, 0x00, 0xC3,
0xFF, // read all SI6 card 8 blocks
0x00, // SRR-dongle frequency band: 0x00="red", 0x01="blue"
0x00, 0x00, 0x0A, 0x00, 0x00,
0x00, 0x00, 0xFF,
0x00, // memory overflow if != 0x00
0xEF, 0xFF, 0x00, 0x24, 0xFE, 0xC0, 0xFF, 0xFF, 0x19, 0x99,
0x05, 0x1E, 0x7F, 0xF8, 0x85, 0x0C, 0x01, 0x01, 0xA6, 0xE0,
0x6F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0x30, 0x30, 0x30, 0x35, 0x7D, 0x20,
0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x30, // program
0x05, // readout mode
0x01, // station code
0x35, // feedback
0x05, // extended protocol with handshake
0x10, 0x08, 0x01, // wakeup date
0x00, 0x00, 0x00, // wakeup time
0x00, 0x1C, 0x20, // sleep time
0x00, 0x78
};
void handleSiCmd(uint8_t cmdCode, uint8_t *data, uint8_t dataSize) {
switch(cmdCode) {
case SiProto::BCMD_GET_SYS_VAL:
{
siProto.start(cmdCode);
siProto.add(0);
siProto.add(fakeStationConfig, 14);
siProto.send();
}
break;
case SiProto::BCMD_SET_MS:
case SiProto::CMD_SET_MS:
{
siProto.start(cmdCode);
siProto.add(0x4d);
siProto.send();
}
break;
case SiProto::CMD_GET_SYS_VAL:
{
uint8_t offset = data[0];
if(offset > sizeof(fakeStationConfig)) {
return;
}
uint8_t len = data[1];
uint8_t maxDataLen = sizeof(fakeStationConfig) - offset;
if(len > 0x7F) {
len = maxDataLen;
}
siProto.start(cmdCode);
siProto.add(offset);
siProto.add(&fakeStationConfig[offset], len);
siProto.send();
}
break;
case SiProto::CMD_GET_TIME:
{
const uint8_t emptyData[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
siProto.start(cmdCode);
siProto.add(emptyData, sizeof(emptyData));
siProto.send();
}
break;
case SiProto::BCMD_READ_SI6:
case SiProto::CMD_READ_SI6:
{
uint8_t blockNumber = data[0];
rfid.begin();
bool autosend = false; // not implemented
if(blockNumber == 0x00 && autosend) {
if(!sieSendAllDataBlocks(true)) {
sieCardReadError();
}
} else if(blockNumber == 0x08) {
if(!sieSendAllDataBlocks(false)) {
sieCardReadError();
}
} else {
if(!sieSendDataBlock(blockNumber)) {
sieCardReadError();
}
}
rfid.end();
}
break;
case SiProto::ACK:
{
sieCardRemoved();
beepOk();
}
break;
default:
siProto.error();
break;
}
}
uint8_t *readCardNumber() {
if(!rfid.isNewCardDetected()) {
return nullptr;
}
byte pageData[] = {0,0,0,0};
if(!rfid.cardPageRead(CARD_PAGE_INIT, pageData)) {
return nullptr;
}
if(pageData[2] == MASTER_CARD_SIGN) {
return nullptr;
}
static uint8_t cardNum[2];
cardNum[0] = pageData[0];
cardNum[1] = pageData[1];
return cardNum;
}
uint32_t readInitTime() {
byte pageData[4];
if(!rfid.cardPageRead(CARD_PAGE_INIT_TIME, pageData)) {
return 0;
}
return byteArrayToUint32(pageData);
}
uint32_t getPunchTime(const byte *pageData, uint32_t initTime) {
uint32_t punchTime = (byteArrayToUint32(pageData)&0x00FFFFFF) | (initTime&0xFF000000);
if(punchTime < initTime) {
punchTime += (uint32_t)1 << 24;
}
return punchTime;
}
bool readStart(uint32_t initTime, uint32_t *startTime, uint8_t *pageStartPunch) {
*startTime = 0;
*pageStartPunch = CARD_PAGE_START;
const uint8_t beginPage = CARD_PAGE_START;
const uint8_t endPage = beginPage + 10; // read only first 10 punches
byte pageData[4];
for(uint8_t page = beginPage; page < endPage; ++page) {
if(!rfid.cardPageRead(page, pageData)) {
return false;
}
uint8_t cp = pageData[0];
if(cp == START_STATION_NUM) {
*startTime = getPunchTime(pageData, initTime);
*pageStartPunch = page;
return true;
}
}
// no start punch found
return true;
}
bool readFinish(uint32_t initTime, uint32_t *finishTime, uint8_t *pageFinishPunch) {
*finishTime = 0;
*pageFinishPunch = 0;
uint8_t newPage = 0;
uint8_t lastNum;
if(!findNewPage(&rfid, &newPage, &lastNum)) {
return false;
}
*pageFinishPunch = newPage;
uint8_t endPage = newPage;
uint8_t beginPage = max(CARD_PAGE_START, endPage - 10);
byte pageData[4];
for(uint8_t page = endPage - 1; page >= beginPage; --page) {
if(!rfid.cardPageRead(page, pageData)) {
return false;
}
uint8_t cp = pageData[0];
if(cp == FINISH_STATION_NUM) {
*finishTime = getPunchTime(pageData, initTime);
*pageFinishPunch = page;
return true;
}
}
// no finish punch found
return true;
}
uint8_t *currentCardNumber = nullptr;
uint32_t currentCardInitTime = 0;
uint8_t currentCpCount = 0;
void sieDetectCard() {
uint8_t *cardNum= readCardNumber();
if(!cardNum) {
return;
}
uint32_t initTime = readInitTime();
if(!initTime) {
return;
}
currentCardNumber = cardNum;
currentCardInitTime = initTime;
if(siProto.isLegacyMode()) {
siProto.start(SiProto::BCMD_SI6_DETECTED);
siProto.add(0x55);
siProto.add(0xAA);
siProto.add(0x00);
siProto.add(0x00);
siProto.add(currentCardNumber[0]);
siProto.add(currentCardNumber[1]);
siProto.send();
return;
}
siProto.start(SiProto::CMD_SI6_DETECTED);
siProto.add(0);
siProto.add(0);
siProto.add(currentCardNumber[0]);
siProto.add(currentCardNumber[1]);
siProto.send();
}
void sieCardRemoved() {
if(!currentCardNumber) {
return;
}
if(siProto.isLegacyMode()) {
siProto.start(SiProto::BCMD_SI5_DETECTED);
siProto.add(0x4F);
} else {
siProto.start(SiProto::CMD_SI_REMOVED);
siProto.add(0);
siProto.add(0);
siProto.add(currentCardNumber[0]);
siProto.add(currentCardNumber[1]);
}
siProto.send();
}
void sieCardReadError() {
sieCardRemoved();
beepError();
}
bool sieSendDataBlock(uint8_t blockNumber) {
if(!currentCardNumber || !rfid.isCardDetected()) {
//siProto.error();
return false;
}
if(siProto.isLegacyMode()) {
siProto.start(SiProto::BCMD_READ_SI6);
} else {
siProto.start(SiProto::CMD_READ_SI6);
}
siProto.add(blockNumber);
static uint8_t blockOffset = 0;
if(blockNumber == 0) {
// TODO: read number of last CP
uint8_t lastCpNum = 0;
currentCpCount = 0;
SiTimestamp clear;
SiTimestamp check;
SiTimestamp start;
SiTimestamp finish;
SiTimestamp lastCp;
clear.fromUnixtime(currentCardInitTime, config.timezone);
clear.cn = 0;
uint8_t finishPunchOrEmptyPage = 0;
uint32_t finishTime = 0;
if(!readFinish(currentCardInitTime, &finishTime, &finishPunchOrEmptyPage)) {
return false;
}
if(finishTime) {
finish.fromUnixtime(finishTime, config.timezone);
finish.cn = 0;
}
if(finishPunchOrEmptyPage) {
currentCpCount = finishPunchOrEmptyPage - CARD_PAGE_START;
}
uint8_t pageStartPunch = 0;
uint32_t startTime = 0;
if(!readStart(currentCardInitTime, &startTime, &pageStartPunch)) {
return false;
}
if(startTime) {
start.fromUnixtime(startTime, config.timezone);
start.cn = 0;
blockOffset = pageStartPunch - CARD_PAGE_START + 1;
currentCpCount -= blockOffset;
} else {
blockOffset = 0;
}
uint8_t cti[] = {
0x55, // card type (CTI)
0xAA, // punches pointer (PP)
0x00, 0x00, currentCardNumber[0], currentCardNumber[1]
};
Crc crc;
crc.value = SiProto::crc16(cti, sizeof(cti));
uint8_t data[40] = {
0x01, 0x01, 0x01, 0x01, // structure of data
0xED, 0xED, 0xED, 0xED, // SI6 ID
cti[0], cti[1], cti[2], cti[3], cti[4], cti[5],
crc.b[1], crc.b[0],
0, lastCpNum,
currentCpCount, static_cast<uint8_t>(currentCpCount + 1),
finish.ptd, finish.cn, finish.pth, finish.ptl,
start.ptd, start.cn, start.pth, start.ptl,
check.ptd, check.cn, check.pth, check.ptl,
clear.ptd, clear.cn, clear.pth, clear.ptl,
lastCp.ptd, lastCp.cn, lastCp.pth, lastCp.ptl
};
siProto.add(data, sizeof(data));
// Start number
for(uint8_t i = 0; i < 4; ++i) {
siProto.add(0xFF);
}
memset(data, ' ', sizeof(data));
// Class
siProto.add(data, 4);
// Surname
siProto.add(data, 20);
// Name
siProto.add(data, 20);
// Country
siProto.add(data, 4);
// Club
siProto.add(data, 36);
} else if(blockNumber == 1) {
uint8_t data[36];
memset(data, ' ', sizeof(data));
// User ID
siProto.add(data, 16);
// Mobile phone number
siProto.add(data, 16);
// E-mail
siProto.add(data, 36);
// Street
siProto.add(data, 20);
// City
siProto.add(data, 16);
// ZIP code
siProto.add(data, 8);
// Sex
siProto.add(data, 4);
// Day of birth
siProto.add(data, 8);
// Date of production
for(uint8_t i = 0; i < 4; ++i) {
siProto.add(0xff);
}
} else {
uint8_t maxPage = rfid.getCardMaxPage();
const uint8_t blockOrder[] = {
0, 1, 4, 5, 6, 7, 2, 3
};
uint8_t blockAddress = CARD_PAGE_START + (blockOrder[blockNumber] - 2)*32 + blockOffset;
byte pageData[4];
for(uint8_t page = blockAddress; page < blockAddress + 32; ++page) {
SiTimestamp siTimestamp;
if(page <= maxPage) {
if(!rfid.cardPageRead(page, pageData)) {
return false;
}
uint8_t cp = pageData[0];
if(cp == START_STATION_NUM || cp == FINISH_STATION_NUM) {
siTimestamp.cn = 0;
} else if(cp != 0) {
siTimestamp.cn = cp;
uint32_t punchTime = getPunchTime(pageData, currentCardInitTime);
siTimestamp.fromUnixtime(punchTime, config.timezone);
}
}
siProto.add(siTimestamp.ptd);
siProto.add(siTimestamp.cn);
siProto.add(siTimestamp.pth);
siProto.add(siTimestamp.ptl);
}
}
siProto.send();
return true;
}
bool sieSendAllDataBlocks(bool shortFormat) {
for(uint8_t blockNumber = 0; blockNumber < 8; ++blockNumber) {
if(!sieSendDataBlock(blockNumber)) {
return false;
}
if(blockNumber == 0 && (currentCpCount <= 64 || shortFormat)) {
blockNumber = 5;
}
}
return true;
}