#include #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(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; }