Skip to content

Commit b65bef6

Browse files
committed
Add binary data transmission with checksum and COBS packet framing
1 parent 50a5a1b commit b65bef6

File tree

1 file changed

+176
-0
lines changed

1 file changed

+176
-0
lines changed

src/TelemetryJet.cpp

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,78 @@ TelemetryJet::TelemetryJet(Stream *transport, unsigned long transmitRate)
2222
dimensions = (DataPoint**) malloc(sizeof(DataPoint*) * dimensionCacheLength);
2323
}
2424

25+
26+
/*
27+
* StuffData byte stuffs "length" bytes of data
28+
* at the location pointed to by "ptr", writing
29+
* the output to the location pointed to by "dst".
30+
*
31+
* Returns the length of the encoded data.
32+
* From https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
33+
*/
34+
size_t StuffData(const uint8_t *ptr, size_t length, uint8_t *dst) {
35+
uint8_t *start = dst;
36+
uint8_t *code_ptr = dst++;
37+
38+
*code_ptr = 1;
39+
while (length--) {
40+
if (*ptr) {
41+
*dst++ = *ptr++;
42+
*code_ptr += 1;
43+
} else {
44+
code_ptr = dst++;
45+
*code_ptr = 1;
46+
ptr++;
47+
}
48+
49+
if (*code_ptr == 0xFF && length > 0) {
50+
code_ptr = dst++;
51+
*code_ptr = 1;
52+
}
53+
}
54+
*dst++ = 0;
55+
56+
return dst - start;
57+
}
58+
59+
/*
60+
* UnStuffData decodes "length" bytes of data at
61+
* the location pointed to by "ptr", writing the
62+
* output to the location pointed to by "dst".
63+
*
64+
* Returns the length of the decoded data
65+
* (which is guaranteed to be <= length).
66+
* From https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
67+
*/
68+
size_t UnStuffData(const uint8_t *ptr, size_t length, uint8_t *dst) {
69+
const uint8_t *start = dst, *end = ptr + length;
70+
uint8_t code = 0xFF, copy = 0;
71+
bool flag = true;
72+
73+
for (; ptr < end; copy--) {
74+
if (copy != 0) {
75+
flag = false;
76+
*dst++ = *ptr++;
77+
} else {
78+
if (code != 0xFF) {
79+
flag = true;
80+
*dst++ = 0;
81+
}
82+
copy = code = *ptr++;
83+
if (code == 0) {
84+
break;
85+
}
86+
}
87+
}
88+
89+
if (flag) {
90+
--dst;
91+
}
92+
93+
return dst - start;
94+
}
95+
96+
2597
void TelemetryJet::update() {
2698
if (isTextMode) {
2799
// Text mode
@@ -127,6 +199,110 @@ void TelemetryJet::update() {
127199
while (transport->available() > 0) {
128200
uint8_t inByte = transport->read();
129201
}
202+
if (millis() - lastSent >= transmitRate && numDimensions > 0) {
203+
mpack_writer_t writer;
204+
size_t packetLength;
205+
for (uint16_t i = 0; i < numDimensions; i++) {
206+
if (dimensions[i]->hasValue && (dimensions[i]->hasNewValue || !isDeltaMode)) {
207+
dimensions[i]->hasNewValue = false;
208+
mpack_writer_init(&writer, messagePackBuffer, 32);
209+
210+
// Write key and type headers
211+
mpack_write_u16(&writer, (uint16_t)dimensions[i]->key);
212+
mpack_write_u8(&writer, (uint8_t)dimensions[i]->type);
213+
214+
// Write data
215+
switch (dimensions[i]->type) {
216+
case DataPointType::BOOLEAN: {
217+
mpack_write_bool(&writer, dimensions[i]->value.v_bool);
218+
break;
219+
}
220+
case DataPointType::UINT8: {
221+
mpack_write_u8(&writer, dimensions[i]->value.v_uint8);
222+
break;
223+
}
224+
case DataPointType::UINT16: {
225+
mpack_write_u16(&writer, dimensions[i]->value.v_uint16);
226+
break;
227+
}
228+
case DataPointType::UINT32: {
229+
mpack_write_u32(&writer, dimensions[i]->value.v_uint32);
230+
break;
231+
}
232+
case DataPointType::UINT64: {
233+
mpack_write_u64(&writer, dimensions[i]->value.v_uint64);
234+
break;
235+
}
236+
case DataPointType::INT8: {
237+
mpack_write_i8(&writer, dimensions[i]->value.v_int8);
238+
break;
239+
}
240+
case DataPointType::INT16: {
241+
mpack_write_i16(&writer, dimensions[i]->value.v_int16);
242+
break;
243+
}
244+
case DataPointType::INT32: {
245+
mpack_write_i32(&writer, dimensions[i]->value.v_int32);
246+
break;
247+
}
248+
case DataPointType::INT64: {
249+
mpack_write_i64(&writer, dimensions[i]->value.v_int64);
250+
break;
251+
}
252+
case DataPointType::FLOAT32: {
253+
mpack_write_float(&writer, dimensions[i]->value.v_float32);
254+
break;
255+
}
256+
case DataPointType::FLOAT64: {
257+
mpack_write_double(&writer, dimensions[i]->value.v_float64);
258+
break;
259+
}
260+
}
261+
262+
mpack_writer_destroy(&writer);
263+
packetLength = mpack_writer_buffer_used(&writer);
264+
265+
// Use COBS (Consistent Overhead Byte Stuffing)
266+
// https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
267+
// to replace all 0x0 bytes in the packet.
268+
// This way, we can use 0x0 as a packet frame marker.
269+
packetLength = StuffData(messagePackBuffer, packetLength, txBuffer);
270+
271+
// Compute checksum and add to front of the packet
272+
// We never want the checksum to == 0,
273+
// because that would complicate the COBS & packet frame marker logic.
274+
// If the checksum is going to be 0, add a single bit so that it won't be.
275+
uint8_t checksum = 0;
276+
for (uint16_t bufferIdx = 0; bufferIdx < packetLength; bufferIdx++) {
277+
checksum += (uint8_t)txBuffer[bufferIdx];
278+
}
279+
checksum = 0xFF - (checksum + 0x01);
280+
281+
uint8_t checksumCorrectionByte = 0x01;
282+
if (checksum == 0x0) {
283+
// Increment byte in the front of the packet to correct the checksum
284+
// If the checksum was previously 0x0 (0), it will now be 0xFF (255).
285+
checksumCorrectionByte += 1;
286+
checksum = 0xFF;
287+
}
288+
289+
// Write checksum and checksum correction byte
290+
transport->print((uint8_t)checksum);
291+
transport->print(' ');
292+
transport->print((uint8_t)checksumCorrectionByte);
293+
transport->print(' ');
294+
295+
// Write buffer
296+
for (uint16_t bufferIdx = 0; bufferIdx < packetLength; bufferIdx++) {
297+
transport->print((uint8_t)txBuffer[bufferIdx]);
298+
transport->print(' ');
299+
}
300+
transport->print((uint8_t)0x0);
301+
transport->println();
302+
}
303+
}
304+
lastSent = millis();
305+
}
130306
}
131307
}
132308

0 commit comments

Comments
 (0)