-
Notifications
You must be signed in to change notification settings - Fork 0
/
DigiHz_EasyTransfer.cpp
404 lines (385 loc) · 16 KB
/
DigiHz_EasyTransfer.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
//Version 1.0.7
#include "DigiHz_EasyTransfer.h"
DigiHz_EasyTransfer::DigiHz_EasyTransfer(bool debug, uint32_t rxBufferSize) {
_debug = debug;
_rxBufferSize = rxBufferSize;
}
uint8_t DigiHz_EasyTransfer::getTxMaxSize(){
return txMaxSize;
}
uint8_t DigiHz_EasyTransfer::getRxBufferSize(){
return _rxBufferSize;
}
uint8_t DigiHz_EasyTransfer::getTxStatus(){
if (_txStatus < 255){//Prevent end user from using TX_STATUS_UNKNOWN
return _txStatus;
} else {
return NULL;
}
}
uint8_t DigiHz_EasyTransfer::getRxStatus(){
if (_rxStatus < 254){//Prevent end user from using RX_STATUS_ACK_RECIEVED and RX_STATUS_UNKNOWN
return _rxStatus;
} else {
return NULL;
}
}
uint8_t DigiHz_EasyTransfer::getTxAttemptsDone(){
return _txAttemptsDone;
}
bool DigiHz_EasyTransfer::begin(Stream *theStream, uint8_t flushRxPortBufferWaitTime, Stream *debugPort) {
_stream = theStream;
_debugPort = debugPort;
if (_debugPort == NULL){//No debug port specified.
_debug = false;
}
if (_debug) {
_debugPort->println(F("DBG:DigiHz_EasyTransfer begin method instantiated (created) successfully."));
_debugPort->print(F("DBG:Version "));_debugPort->println(_version);
_debugPort->print(F("DBG:tx max size is:"));_debugPort->print(txMaxSize);_debugPort->println(F(" bytes."));
}
if (_rxBufferSize < rxBufferMinSize){
if (_debug) {
_debugPort->print(F("DBG:ERROR! rx buffer size was set to:"));_debugPort->print(_rxBufferSize);_debugPort->print(F(" bytes. (MINIMUM is "));_debugPort->print(rxBufferMinSize);_debugPort->println(F(" bytes)."));
_debugPort->print(F("DBG:OVERIDE! rx buffer size is set to:"));_debugPort->print(rxBufferMinSize);_debugPort->println(F(" byte."));
}
_rxBufferSize = rxBufferMinSize;
} else if (_rxBufferSize > rxBufferMaxSize){
if (_debug) {
_debugPort->print(F("DBG:ERROR! rx buffer size was set to:"));_debugPort->print(_rxBufferSize);_debugPort->print(F(" bytes. (MAXIMUM is "));_debugPort->print(rxBufferMaxSize);_debugPort->println(F(" bytes)."));
_debugPort->print(F("DBG:OVERIDE! rx buffer size is set to:"));_debugPort->print(rxBufferMaxSize);_debugPort->println(F(" bytes."));
}
_rxBufferSize = rxBufferMaxSize;//Overide user's setting of rx buffer size.
} else {
if (_debug) {
_debugPort->print(F("DBG:rx buffer size is set to:"));_debugPort->print(_rxBufferSize);_debugPort->println(F(" bytes."));
}
}
rxBuffer = (uint8_t*) malloc(_rxBufferSize + 1);//Dynamic creation of rx buffer in RAM.
_flushRxPortBufferWaitTime = flushRxPortBufferWaitTime;
if (_debug) {
_debugPort->print(F("DBG:Flush rx port buffer wait time set to:"));_debugPort->print(_flushRxPortBufferWaitTime);_debugPort->println(F(" milli seconds."));
}
if (_flushRxPortBufferWaitTime > 0){
flushRxPortBuffer(flushRxPortBufferWaitTime);//Flush the rx port buffer.
}
return true;
}
void DigiHz_EasyTransfer::setHeaders(byte header1, byte header2){
_header1 = header1;
_header2 = header2;
if (_debug) {
_debugPort->print(F("DBG:header 1 set to:0x"));_debugPort->println(_header1, HEX);
_debugPort->print(F("DBG:header 2 set to:0x"));_debugPort->println(_header2, HEX);
}
}
void DigiHz_EasyTransfer::clearRxBufferOnRecieve(bool clearRxBufferOnRecieve){
_clearRxBufferOnRecieve = clearRxBufferOnRecieve;
if (_debug) {
_debugPort->print(F("DBG:clearRxBufferOnRecieve set to:"));
if (_clearRxBufferOnRecieve){
_debugPort->println(F("true"));
} else {
_debugPort->println(F("false"));
}
}
}
void DigiHz_EasyTransfer::setTxRetries(uint8_t txRetries){
if (txRetries == 0){
txRetries = 1;//Overide user's setting of tx retries.
}
_txRetries = txRetries;
if (_debug) {
_debugPort->print(F("DBG:tx retries set to:"));_debugPort->println(_txRetries);
}
}
void DigiHz_EasyTransfer::setTxAckTimeout(uint16_t txAckTimeout){
_txAckTimeout = txAckTimeout;
if (_debug) {
_debugPort->print(F("DBG:tx ack timeout set to:"));_debugPort->print(_txAckTimeout);_debugPort->println(F(" milli seconds."));
}
}
void DigiHz_EasyTransfer::setFlushRxPortBufferWaitTime(uint8_t flushRxPortBufferWaitTime){
_flushRxPortBufferWaitTime = flushRxPortBufferWaitTime;
if (_debug) {
_debugPort->print(F("DBG:Flush rx port buffer wait time set to:"));_debugPort->print(_flushRxPortBufferWaitTime);_debugPort->println(F(" milli seconds."));
}
}
void DigiHz_EasyTransfer::clearRxBuffer(){
memset(rxBuffer, 0, _rxBufferSize);//Clear the buffer.
}
void DigiHz_EasyTransfer::flushRxPortBuffer(uint8_t delayTime){
if (_debug) {
_debugPort->println(F("DBG:Flushing rx port buffer..."));
}
while (true){
delay (delayTime);//Give data a chance to arrive.
if (_stream->available()){//We received something, get all of it and discard it from the buffer.
while (_stream->available()){
_stream->read();
}
continue;//Stay in the main while loop.
} else {
break;//Nothing arrived for delayTime ms
}
}
if (_debug) {
_debugPort->println(F("DBG:Flushing of rx port buffer done."));
}
}
bool DigiHz_EasyTransfer::sendData(uint8_t * ptr, uint32_t txLength, uint8_t txIdentifier, bool txRequestAck){
if (txLength > txMaxSize){
_txStatus = TX_STATUS_DATA_TO_BIG;
return false;
}
if (txIdentifier == 0){
_txIdentifier = 0;//Set identifier to 0.
_txRequestAck = false;//Reset flag.
_txStatus = TX_INVALID_IDENTIFIER;
return false;
} else {
_txIdentifier = txIdentifier;
_txRequestAck = txRequestAck;
}
uint8_t _numTxRetries;
if (txRequestAck){//If we request ack, then we try this many times to send.
_numTxRetries = _txRetries;
} else {//We only try to send once.
_numTxRetries = 1;
}
_txAttemptsDone = 0;//Reset counter.
for (uint8_t i = 0; i < _numTxRetries; i++) {
if (_debug) {
_debugPort->println();
}
_txAttemptsDone++;//Increase counter.
if (_numTxRetries > 1){
if (_debug) {
_debugPort->print(F("DBG:Send atttempt "));_debugPort->print(i + 1);_debugPort->print(F("/"));_debugPort->println(_numTxRetries);
}
}
//Captures address and size of struct or var or array start.
_txAddress = ptr;
_txSize = txLength;//sizeOfPayload
//Captures address and size of struct or var or array end.
//Sends out struct or var or array in binary, with header(s), identifier, requestAck, sizeOfPayload, payload and checksum start.
uint8_t txChecksum = _txSize;
if (_debug) {
_txRxStartMeasure = micros();
}
_stream->write(_header1);
_stream->write(_header2);
_stream->write(_txIdentifier);
_stream->write(_txRequestAck);
_stream->write(_txSize);
for (int i = 0; i < _txSize; i++){
txChecksum^=*(_txAddress + i);
_stream->write(*(_txAddress + i));
}
_stream->write(txChecksum);
//Sends out struct or var or array in binary, with header(s), identifier, requestAck, sizeOfPayload, payload and checksum end.
_txChecksum = txChecksum;
if (_debug) {
_txRxEndMeasure = micros();
_debugPort->print(F("DBG:Time to send data packet:"));_debugPort->print(_txRxEndMeasure - _txRxStartMeasure);_debugPort->println(F(" micro seconds."));
_debugPort->print(F("DBG:tx data packet identifier:"));_debugPort->println(_txIdentifier);
_debugPort->print(F("DBG:tx data packet length:"));_debugPort->print(_txSize);_debugPort->println(F(" bytes."));
_debugPort->print(F("DBG:tx data packet checksum:"));_debugPort->println(_txChecksum);
}
if (_txRequestAck){//If we request ack.
if (_debug) {
_txRxAckStartMeasure = micros();
_debugPort->println(F("DBG:Waiting for ack..."));
}
_txWaitForAckStart = millis();
_txAckRecieved = false;//Set flag.
while (_txWaitForAckStart + _txAckTimeout > millis()){
delay(1);//Slow things down a bit, we dont need to check continiously. //NOTE:Not sure we are going to use this delay here.
receiveData();
if (_txAckRecieved){//Break out of while loop if we recieved the ack.
break;
}
yield();
}
if (_txAckRecieved){//We got the ack.
if (_debug) {
_txRxAckEndMeasure = micros();
_debugPort->print(F("DBG:Got the ack in:"));_debugPort->print(_txRxAckEndMeasure - _txRxAckStartMeasure);_debugPort->println(F(" micro seconds."));
_debugPort->println();
}
_txAckRecieved = false;//Reset flag.
_txStatus = TX_STATUS_OK;
return true;
} else {//We did not get the ack back within the timeout.
if (_debug) {
_txRxAckEndMeasure = micros();
_debugPort->print(F("DBG:Gave up waiting for ack after:"));_debugPort->print(_txRxAckEndMeasure - _txRxAckStartMeasure);_debugPort->println(F(" micro seconds."));
_debugPort->println();
}
if (_numTxRetries == i + 1){
_txStatus = TX_STATUS_ACK_TIMEOUT;
return false;
}
}
} else {//Not requesting ack.
if (_debug) {
_debugPort->println();
}
if (_numTxRetries == 1){
_txStatus = TX_STATUS_OK;
return true;
}
}
}
_txStatus = TX_STATUS_UNKNOWN;
return false;
}
// Private method for this class
bool DigiHz_EasyTransfer::_rxSendTheAck(uint8_t * ptr, uint8_t txLength){
_txIdentifier = 0;//Set identifier to 0.
_txRequestAck = false;//Reset flag.
//Captures address and size of var start.
_txAddress = ptr;
_txSize = txLength;//sizeOfPayload
//Captures address and size of var end.
//Sends out the var in binary, with header(s), identifier, requestAck, sizeOfPayload, payload and checksum start.
uint8_t txChecksum = _txSize;
if (_debug) {
_txRxStartMeasure = micros();
}
_stream->write(_header1);
_stream->write(_header2);
_stream->write(_txIdentifier);
_stream->write(_txRequestAck);
_stream->write(_txSize);
txChecksum^=*(_txAddress);
_stream->write(*(_txAddress));
_stream->write(txChecksum);
//Sends out the var in binary, with header(s), identifier, requestAck, sizeOfPayload, payload and checksum end.
if (_debug) {
_txRxEndMeasure = micros();
_debugPort->print(F("DBG:Time to send ack packet:"));_debugPort->print(_txRxEndMeasure - _txRxStartMeasure);_debugPort->println(F(" micro seconds."));
}
return true;
}
bool DigiHz_EasyTransfer::receiveData(){
//Start off by looking for the header bytes. If they were already found in a previous call, skip it.
if (_rxLength == 0){
//This size check may be redundant due to the size check below, but for now I'll leave it the way it is.
if (_stream->available() >= 5){//header1, header2, identifier, requestAck, size
//This will block until a header1 is found or buffer size becomes less then 5.
while (_stream->read() != _header1) {
//This will trash any preamble junk in the serial buffer.
//But we need to make sure there is enough in the buffer to process while we trash the rest.
//If the buffer becomes too empty, we will escape and try again on the next call.
if (_stream->available() < 5) {
_rxStatus = RX_STATUS_UNKNOWN;
return false;
}
}
if (_stream->read() == _header2){
_rxIdentifier = _stream->read();
_rxSendAck = _stream->read();
_rxLength = _stream->read();
}
}
}
//We get here if we already found the header bytes, the identifier, the requestAck and the size matched what we know, and now we are byte aligned.
if (_rxLength != 0){
if (_clearRxBufferOnRecieve){
clearRxBuffer();//Ensure that the rx buffer is empty! (Optional)
}
if (_rxLength > _rxBufferSize){//Catch buffer size under dimensioned!
if (_debug) {
_debugPort->println(F("DBG:ERROR, rx buffer size is set to small! Increase the rx buffer size."));
}
_rxLength = 0;//Reset so we can recieve next packet.
_rxArrayInx = 0;
_rxStatus = RX_STATUS_BUFFER_TO_SMALL;
return false;
}
rxBufferLength = _rxLength;
rxIdentifier = _rxIdentifier;
while(_stream->available() && _rxArrayInx <= _rxLength){
rxBuffer[_rxArrayInx++] = _stream->read();
}
if (_rxLength == (_rxArrayInx - 1)){//Got the whole packet.
if (_debug) {
_debugPort->println(F("DBG:Seem to have gotten the whole packet."));
}
//Last uint8_t is the checksum.
_rxCalculatedChecksum = _rxLength;
for (int i = 0; i < _rxLength; i++){
_rxCalculatedChecksum^=rxBuffer[i];
}
if (_rxCalculatedChecksum == rxBuffer[_rxArrayInx - 1]){//Checksum is good.
if (_rxIdentifier != 0){//If we recieved a data packet.
if (_debug) {
_debugPort->println(F("DBG:Recieved data packet."));
_debugPort->print(F("DBG:rx identifier:"));_debugPort->println(_rxIdentifier);
_debugPort->print(F("DBG:rx data packet checksum:"));_debugPort->println(rxBuffer[_rxArrayInx - 1]);
_debugPort->println(F("DBG:rx data packet checksum OK."));
}
if (_rxSendAck){//If sender requested an ack.
if (_debug) {
_debugPort->println(F("DBG:Sending ack back."));
}
if (_flushRxPortBufferWaitTime > 0){
flushRxPortBuffer(_flushRxPortBufferWaitTime);//Flush the rx port buffer so we not get garbage.
}
_rxSendTheAck(details(rxBuffer[_rxArrayInx - 1]));//Send the ack back.
_rxSendAck = false;
}
_rxLength = 0;//Reset so we can recieve next packet.
_rxArrayInx = 0;
_rxStatus = RX_STATUS_OK;
return true;
} else {//We recieved an ack packet.
if (_debug) {
_debugPort->println(F("DBG:Recieved ack packet."));
_debugPort->print(F("DBG:rx ack packet checksum:"));_debugPort->println(rxBuffer[0]);
_debugPort->println(F("DBG:rx ack packet checksum OK."));
}
if (rxBuffer[0] == _txChecksum){//If tx data packet checksum and rx ack packet checksum matches.
if (_debug) {
_debugPort->println(F("DBG:tx data packet checksum and rx ack packet checksum matches."));
}
_txAckRecieved = true;
_rxLength = 0;//Reset so we can recieve next packet.
_rxArrayInx = 0;
_rxStatus = RX_STATUS_ACK_RECIEVED;//NOTE:We will not get this back, but we set a status anyway.
return true;
} else {//rx ack packet checksum did not match tx data packet checksum.
if (_debug) {
_debugPort->println(F("DBG:ERROR, rx ack packet checksum did not match tx data packet checksum!"));
}
_rxLength = 0;//Reset so we can recieve next packet.
_rxArrayInx = 0;
_rxStatus = RX_STATUS_ACK_CHECKSUM_FAILED;
return false;
}
}
} else {//Failed checksum, need to clear this out anyway.
if (_debug) {
if (_rxIdentifier != 0){//If we recieved a data packet.
_debugPort->print(F("DBG:rx data packet checksum:"));_debugPort->println(rxBuffer[0]);
_debugPort->println(F("DBG:ERROR, rx data packet checksum FAILED."));
_rxStatus = RX_STATUS_DATA_CHECKSUM_FAILED;
} else {//We recieved an ack packet.
_debugPort->print(F("DBG:rx ack packet checksum:"));_debugPort->println(rxBuffer[0]);
_debugPort->println(F("DBG:ERROR, rx ack packet checksum FAILED."));
_rxStatus = RX_STATUS_ACK_CHECKSUM_FAILED;
}
}
_rxLength = 0;//Reset so we can recieve next packet.
_rxArrayInx = 0;
return false;
}
} else {//Did not get the whole packet.
_rxStatus = RX_STATUS_UNKNOWN;
return false;
}
}
_rxStatus = RX_STATUS_UNKNOWN;
return false;
}