190 lines
5.2 KiB
C++
Executable File
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;
|
|
}
|
|
|