SportIduino/FWsrc/sportiduino-3.11.0/firmware/MasterStation/sportidentprotocol.cpp

190 lines
5.2 KiB
C++
Executable File

#include "sportidentprotocol.h"
void SiTimestamp::fromUnixtime(uint32_t timestamp, int8_t timezone) {
if(!timestamp) {
return;
}
uint32_t localtime = timestamp + (int16_t)timezone*900;
uint32_t daysFromEpoch = localtime/86400;
uint32_t secsFromMidnight = localtime%86400;
uint8_t weekday = (daysFromEpoch + 4)%7;
ptd = weekday << 1;
ptd |= secsFromMidnight/43200;
uint16_t pt = secsFromMidnight%43200;
pth = pt >> 8;
ptl = pt & 0xff;
}
uint16_t SportidentProtocol::crc16(uint8_t *data, uint16_t len) {
if(len < 2) {
return 0; // response value is "0" for none or one data byte
}
uint8_t *p = data;
uint16_t crc = *p++;
crc = (crc << 8) + *p++;
if(len == 2) {
return crc; // response value is CRC for two data bytes
}
uint16_t val;
for(uint16_t i = (int)(len >> 1); i > 0; --i) {
if(i > 1) {
val = *p++;
val = (val << 8) + *p++;
} else {
if(len & 1) { // odd number of data bytes, complete with "0"
val = *p;
val = (val << 8);
} else {
val = 0;
}
}
for(uint8_t j = 0; j<16; ++j) {
if(crc & 0x8000) {
crc <<= 1;
if(val & 0x8000) {
++crc;
}
crc ^= 0x8005;
} else {
crc <<= 1;
if(val & 0x8000) {
++crc;
}
}
val <<= 1;
}
}
return crc;
}
void SportidentProtocol::start(uint8_t code) {
serialBuffer[0] = STX;
serialBuffer[1] = code;
const uint16_t station_code = 0x0001;
// Legacy protocol
if(code < 0x80) {
baseCmd = true;
baseCmdChecksum = 0;
serialDataPos = 2;
add(station_code & 0xff);
} else {
baseCmd = false;
serialDataPos = 3;
add(station_code >> 8);
add(station_code & 0xff);
}
}
void SportidentProtocol::add(uint8_t dataByte) {
if(baseCmd) {
if(dataByte < 0x20) {
serialBuffer[serialDataPos++] = DLE;
}
baseCmdChecksum += dataByte;
} else if (serialDataPos >= SPORTIDENT_MAX_PACKET_SIZE - 1) {
return;
}
serialBuffer[serialDataPos++] = dataByte;
}
void SportidentProtocol::add(const uint8_t *data, uint8_t size) {
for(uint8_t i = 0; i < size; ++i) {
add(data[i]);
}
}
void SportidentProtocol::send() {
if(baseCmd) {
if(serialBuffer[1] == BCMD_READ_SI6) {
add(baseCmdChecksum & 0xFF);
}
serialBuffer[serialDataPos++] = ETX;
} else {
uint8_t dataSize = serialDataPos - 3; // minus start, resp code, datalen
serialBuffer[2] = dataSize;
crc.value = SportidentProtocol::crc16(&serialBuffer[1], 2 + dataSize);
serialBuffer[serialDataPos++] = crc.b[1];
serialBuffer[serialDataPos++] = crc.b[0];
serialBuffer[serialDataPos++] = ETX;
}
for(uint8_t i = 0; i < serialDataPos; i++) {
Serial.write(serialBuffer[i]);
}
}
void SportidentProtocol::error() {
Serial.write(NAK);
}
uint8_t *SportidentProtocol::read(bool *error, uint8_t *code, uint8_t *dataSize) {
*error = false;
if(Serial.available() > 0) {
uint8_t b = Serial.peek();
if(b == ACK) {
Serial.read(); // drop byte
*code = ACK;
*dataSize = 0;
return code;
} else if(b == STX) {
Serial.readBytes(serialBuffer, 3);
if(serialBuffer[1] == STX) {
serialBuffer[1] = serialBuffer[2];
Serial.readBytes(&serialBuffer[2], 1);
}
uint8_t cmd = serialBuffer[1];
// Legacy protocol
if(cmd < 0x80) {
legacyMode = true;
uint8_t length = 0;
for(uint8_t i = 2; i < SPORTIDENT_MAX_PACKET_SIZE; ++i) {
Serial.readBytes(&serialBuffer[i], 1);
++length;
if(serialBuffer[i] == DLE) {
Serial.readBytes(&serialBuffer[i], 1);
} else if(serialBuffer[i] == ETX) {
--length;
break;
}
}
*code = cmd;
*dataSize = length;
return &serialBuffer[2];
}
legacyMode = false;
uint8_t length = serialBuffer[2];
if(length > SPORTIDENT_MAX_PACKET_SIZE - 6) {
*error = true;
return nullptr;
}
memset(&serialBuffer[3], 0, length + 3);
Serial.readBytes(&serialBuffer[3], length + 3);
crc.b[1] = serialBuffer[length + 3];
crc.b[0] = serialBuffer[length + 4];
if(crc.value != SportidentProtocol::crc16(&serialBuffer[1], 2 + length)) {
*error = true;
return nullptr;
}
*code = cmd;
*dataSize = length;
return &serialBuffer[3];
}
}
return nullptr;
}