2 ******************************************************************************
3 * @addtogroup OpenPilotSystem OpenPilot System
5 * @addtogroup OpenPilotLibraries OpenPilot System Libraries
9 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
10 * @brief UAVTalk library, implements to telemetry protocol. See the wiki for more details.
11 * This library should not be called directly by the application, it is only used by the
13 * @see The GNU Public License (GPL) Version 3
15 *****************************************************************************/
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 3 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
27 * You should have received a copy of the GNU General Public License along
28 * with this program; if not, write to the Free Software Foundation, Inc.,
29 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #include "openpilot.h"
33 #include "uavtalk_priv.h"
35 // #define UAV_DEBUGLOG 1
37 #if defined UAV_DEBUGLOG && defined FLASH_FREERTOS
38 #define UAVT_DEBUGLOG_PRINTF(...) PIOS_DEBUGLOG_Printf(__VA_ARGS__)
39 // uncomment and adapt the following lines to filter verbose logging to include specific object(s) only
40 // #include "flighttelemetrystats.h"
41 // #define UAVT_DEBUGLOG_CPRINTF(objId, ...) if (objId == FLIGHTTELEMETRYSTATS_OBJID) { UAVT_DEBUGLOG_PRINTF(__VA_ARGS__); }
43 #ifndef UAVT_DEBUGLOG_PRINTF
44 #define UAVT_DEBUGLOG_PRINTF(...)
46 #ifndef UAVT_DEBUGLOG_CPRINTF
47 #define UAVT_DEBUGLOG_CPRINTF(objId, ...)
51 static int32_t objectTransaction(UAVTalkConnectionData
*connection
, uint8_t type
, UAVObjHandle obj
, uint16_t instId
, int32_t timeout
);
52 static int32_t sendObject(UAVTalkConnectionData
*connection
, uint8_t type
, uint32_t objId
, uint16_t instId
, UAVObjHandle obj
);
53 static int32_t sendSingleObject(UAVTalkConnectionData
*connection
, uint8_t type
, uint32_t objId
, uint16_t instId
, UAVObjHandle obj
);
54 static int32_t receiveObject(UAVTalkConnectionData
*connection
, uint8_t type
, uint32_t objId
, uint16_t instId
, uint8_t *data
);
55 static void updateAck(UAVTalkConnectionData
*connection
, uint8_t type
, uint32_t objId
, uint16_t instId
);
58 * Initialize the UAVTalk library
59 * \param[in] connection UAVTalkConnection to be used
60 * \param[in] outputStream Function pointer that is called to send a data buffer
64 UAVTalkConnection
UAVTalkInitialize(UAVTalkOutputStream outputStream
)
67 UAVTalkConnectionData
*connection
= pios_malloc(sizeof(UAVTalkConnectionData
));
72 connection
->canari
= UAVTALK_CANARI
;
73 connection
->iproc
.rxPacketLength
= 0;
74 connection
->iproc
.state
= UAVTALK_STATE_SYNC
;
75 connection
->outStream
= outputStream
;
76 connection
->lock
= xSemaphoreCreateRecursiveMutex();
77 connection
->transLock
= xSemaphoreCreateRecursiveMutex();
79 connection
->rxBuffer
= pios_malloc(UAVTALK_MAX_PACKET_LENGTH
);
80 if (!connection
->rxBuffer
) {
83 connection
->txBuffer
= pios_malloc(UAVTALK_MAX_PACKET_LENGTH
);
84 if (!connection
->txBuffer
) {
87 vSemaphoreCreateBinary(connection
->respSema
);
88 xSemaphoreTake(connection
->respSema
, 0); // reset to zero
89 UAVTalkResetStats((UAVTalkConnection
)connection
);
90 return (UAVTalkConnection
)connection
;
94 * Set the communication output stream
95 * \param[in] connection UAVTalkConnection to be used
96 * \param[in] outputStream Function pointer that is called to send a data buffer
100 int32_t UAVTalkSetOutputStream(UAVTalkConnection connectionHandle
, UAVTalkOutputStream outputStream
)
102 UAVTalkConnectionData
*connection
;
104 CHECKCONHANDLE(connectionHandle
, connection
, return -1);
107 xSemaphoreTakeRecursive(connection
->lock
, portMAX_DELAY
);
110 connection
->outStream
= outputStream
;
113 xSemaphoreGiveRecursive(connection
->lock
);
119 * Get current output stream
120 * \param[in] connection UAVTalkConnection to be used
121 * @return UAVTarlkOutputStream the output stream used
123 UAVTalkOutputStream
UAVTalkGetOutputStream(UAVTalkConnection connectionHandle
)
125 UAVTalkConnectionData
*connection
;
127 CHECKCONHANDLE(connectionHandle
, connection
, return NULL
);
128 return connection
->outStream
;
132 * Get communication statistics counters
133 * \param[in] connection UAVTalkConnection to be used
134 * @param[out] statsOut Statistics counters
136 void UAVTalkGetStats(UAVTalkConnection connectionHandle
, UAVTalkStats
*statsOut
, bool reset
)
138 UAVTalkConnectionData
*connection
;
140 CHECKCONHANDLE(connectionHandle
, connection
, return );
143 xSemaphoreTakeRecursive(connection
->lock
, portMAX_DELAY
);
146 memcpy(statsOut
, &connection
->stats
, sizeof(UAVTalkStats
));
150 memset(&connection
->stats
, 0, sizeof(UAVTalkStats
));
154 xSemaphoreGiveRecursive(connection
->lock
);
158 * Get communication statistics counters
159 * \param[in] connection UAVTalkConnection to be used
160 * @param[out] statsOut Statistics counters
162 void UAVTalkAddStats(UAVTalkConnection connectionHandle
, UAVTalkStats
*statsOut
, bool reset
)
164 UAVTalkConnectionData
*connection
;
166 CHECKCONHANDLE(connectionHandle
, connection
, return );
169 xSemaphoreTakeRecursive(connection
->lock
, portMAX_DELAY
);
172 statsOut
->txBytes
+= connection
->stats
.txBytes
;
173 statsOut
->txObjectBytes
+= connection
->stats
.txObjectBytes
;
174 statsOut
->txObjects
+= connection
->stats
.txObjects
;
175 statsOut
->txErrors
+= connection
->stats
.txErrors
;
176 statsOut
->rxBytes
+= connection
->stats
.rxBytes
;
177 statsOut
->rxObjectBytes
+= connection
->stats
.rxObjectBytes
;
178 statsOut
->rxObjects
+= connection
->stats
.rxObjects
;
179 statsOut
->rxErrors
+= connection
->stats
.rxErrors
;
180 statsOut
->rxSyncErrors
+= connection
->stats
.rxSyncErrors
;
181 statsOut
->rxCrcErrors
+= connection
->stats
.rxCrcErrors
;
185 memset(&connection
->stats
, 0, sizeof(UAVTalkStats
));
189 xSemaphoreGiveRecursive(connection
->lock
);
193 * Reset the statistics counters.
194 * \param[in] connection UAVTalkConnection to be used
196 void UAVTalkResetStats(UAVTalkConnection connectionHandle
)
198 UAVTalkConnectionData
*connection
;
200 CHECKCONHANDLE(connectionHandle
, connection
, return );
203 xSemaphoreTakeRecursive(connection
->lock
, portMAX_DELAY
);
206 memset(&connection
->stats
, 0, sizeof(UAVTalkStats
));
209 xSemaphoreGiveRecursive(connection
->lock
);
213 * Accessor method to get the timestamp from the last UAVTalk message
215 void UAVTalkGetLastTimestamp(UAVTalkConnection connectionHandle
, uint16_t *timestamp
)
217 UAVTalkConnectionData
*connection
;
219 CHECKCONHANDLE(connectionHandle
, connection
, return );
221 UAVTalkInputProcessor
*iproc
= &connection
->iproc
;
222 *timestamp
= iproc
->timestamp
;
226 * Request an update for the specified object, on success the object data would have been
227 * updated by the GCS.
228 * \param[in] connection UAVTalkConnection to be used
229 * \param[in] obj Object to update
230 * \param[in] instId The instance ID or UAVOBJ_ALL_INSTANCES for all instances.
231 * \param[in] timeout Time to wait for the response, when zero it will return immediately
235 int32_t UAVTalkSendObjectRequest(UAVTalkConnection connectionHandle
, UAVObjHandle obj
, uint16_t instId
, int32_t timeout
)
237 UAVTalkConnectionData
*connection
;
239 CHECKCONHANDLE(connectionHandle
, connection
, return -1);
241 return objectTransaction(connection
, UAVTALK_TYPE_OBJ_REQ
, obj
, instId
, timeout
);
245 * Send the specified object through the telemetry link.
246 * \param[in] connection UAVTalkConnection to be used
247 * \param[in] obj Object to send
248 * \param[in] instId The instance ID or UAVOBJ_ALL_INSTANCES for all instances.
249 * \param[in] acked Selects if an ack is required (1:ack required, 0: ack not required)
250 * \param[in] timeoutMs Time to wait for the ack, when zero it will return immediately
254 int32_t UAVTalkSendObject(UAVTalkConnection connectionHandle
, UAVObjHandle obj
, uint16_t instId
, uint8_t acked
, int32_t timeoutMs
)
256 UAVTalkConnectionData
*connection
;
258 CHECKCONHANDLE(connectionHandle
, connection
, return -1);
262 return objectTransaction(connection
, UAVTALK_TYPE_OBJ_ACK
, obj
, instId
, timeoutMs
);
264 return objectTransaction(connection
, UAVTALK_TYPE_OBJ
, obj
, instId
, timeoutMs
);
269 * Send the specified object through the telemetry link with a timestamp.
270 * \param[in] connection UAVTalkConnection to be used
271 * \param[in] obj Object to send
272 * \param[in] instId The instance ID or UAVOBJ_ALL_INSTANCES for all instances.
273 * \param[in] acked Selects if an ack is required (1:ack required, 0: ack not required)
274 * \param[in] timeoutMs Time to wait for the ack, when zero it will return immediately
278 int32_t UAVTalkSendObjectTimestamped(UAVTalkConnection connectionHandle
, UAVObjHandle obj
, uint16_t instId
, uint8_t acked
, int32_t timeoutMs
)
280 UAVTalkConnectionData
*connection
;
282 CHECKCONHANDLE(connectionHandle
, connection
, return -1);
286 return objectTransaction(connection
, UAVTALK_TYPE_OBJ_ACK_TS
, obj
, instId
, timeoutMs
);
288 return objectTransaction(connection
, UAVTALK_TYPE_OBJ_TS
, obj
, instId
, timeoutMs
);
293 * Execute the requested transaction on an object.
294 * \param[in] connection UAVTalkConnection to be used
295 * \param[in] type Transaction type
296 * UAVTALK_TYPE_OBJ: send object,
297 * UAVTALK_TYPE_OBJ_REQ: request object update
298 * UAVTALK_TYPE_OBJ_ACK: send object with an ack
299 * \param[in] obj Object
300 * \param[in] instId The instance ID of UAVOBJ_ALL_INSTANCES for all instances.
301 * \param[in] timeoutMs Time to wait for the ack, when zero it will return immediately
305 static int32_t objectTransaction(UAVTalkConnectionData
*connection
, uint8_t type
, UAVObjHandle obj
, uint16_t instId
, int32_t timeoutMs
)
307 int32_t respReceived
;
310 // Send object depending on if a response is needed
311 if (type
== UAVTALK_TYPE_OBJ_ACK
|| type
== UAVTALK_TYPE_OBJ_ACK_TS
|| type
== UAVTALK_TYPE_OBJ_REQ
) {
312 // Get transaction lock (will block if a transaction is pending)
313 xSemaphoreTakeRecursive(connection
->transLock
, portMAX_DELAY
);
315 xSemaphoreTakeRecursive(connection
->lock
, portMAX_DELAY
);
316 // expected response type
317 connection
->respType
= (type
== UAVTALK_TYPE_OBJ_REQ
) ? UAVTALK_TYPE_OBJ
: UAVTALK_TYPE_ACK
;
318 connection
->respObjId
= UAVObjGetID(obj
);
319 connection
->respInstId
= instId
;
320 ret
= sendObject(connection
, type
, UAVObjGetID(obj
), instId
, obj
);
321 xSemaphoreGiveRecursive(connection
->lock
);
322 // Wait for response (or timeout) if sending the object succeeded
323 respReceived
= pdFALSE
;
325 respReceived
= xSemaphoreTake(connection
->respSema
, timeoutMs
/ portTICK_RATE_MS
);
327 // Check if a response was received
328 if (respReceived
== pdTRUE
) {
329 // We are done successfully
330 xSemaphoreGiveRecursive(connection
->transLock
);
333 // Cancel transaction
334 xSemaphoreTakeRecursive(connection
->lock
, portMAX_DELAY
);
335 // non blocking call to make sure the value is reset to zero (binary sema)
336 xSemaphoreTake(connection
->respSema
, 0);
337 connection
->respObjId
= 0;
338 xSemaphoreGiveRecursive(connection
->lock
);
339 xSemaphoreGiveRecursive(connection
->transLock
);
342 } else if (type
== UAVTALK_TYPE_OBJ
|| type
== UAVTALK_TYPE_OBJ_TS
) {
343 xSemaphoreTakeRecursive(connection
->lock
, portMAX_DELAY
);
344 ret
= sendObject(connection
, type
, UAVObjGetID(obj
), instId
, obj
);
345 xSemaphoreGiveRecursive(connection
->lock
);
351 * Process an byte from the telemetry stream.
352 * \param[in] connectionHandle UAVTalkConnection to be used
353 * \param[in] rxbyte Received byte
354 * \return UAVTalkRxState
356 UAVTalkRxState
UAVTalkProcessInputStreamQuiet(UAVTalkConnection connectionHandle
, uint8_t rxbyte
)
358 UAVTalkConnectionData
*connection
;
360 CHECKCONHANDLE(connectionHandle
, connection
, return -1);
362 UAVTalkInputProcessor
*iproc
= &connection
->iproc
;
364 ++connection
->stats
.rxBytes
;
366 if (iproc
->state
== UAVTALK_STATE_ERROR
|| iproc
->state
== UAVTALK_STATE_COMPLETE
) {
367 iproc
->state
= UAVTALK_STATE_SYNC
;
370 if (iproc
->rxPacketLength
< 0xffff) {
371 // update packet byte count
372 iproc
->rxPacketLength
++;
375 // Receive state machine
376 switch (iproc
->state
) {
377 case UAVTALK_STATE_SYNC
:
379 if (rxbyte
!= UAVTALK_SYNC_VAL
) {
380 connection
->stats
.rxSyncErrors
++;
384 // Initialize and update the CRC
385 iproc
->cs
= PIOS_CRC_updateByte(0, rxbyte
);
387 iproc
->rxPacketLength
= 1;
391 iproc
->state
= UAVTALK_STATE_TYPE
;
394 case UAVTALK_STATE_TYPE
:
396 if ((rxbyte
& UAVTALK_TYPE_MASK
) != UAVTALK_TYPE_VER
) {
397 connection
->stats
.rxErrors
++;
398 iproc
->state
= UAVTALK_STATE_SYNC
;
403 iproc
->cs
= PIOS_CRC_updateByte(iproc
->cs
, rxbyte
);
405 iproc
->type
= rxbyte
;
407 iproc
->packet_size
= 0;
408 iproc
->state
= UAVTALK_STATE_SIZE
;
411 case UAVTALK_STATE_SIZE
:
414 iproc
->cs
= PIOS_CRC_updateByte(iproc
->cs
, rxbyte
);
416 if (iproc
->rxCount
== 0) {
417 iproc
->packet_size
+= rxbyte
;
421 iproc
->packet_size
+= rxbyte
<< 8;
424 if (iproc
->packet_size
< UAVTALK_MIN_HEADER_LENGTH
|| iproc
->packet_size
> UAVTALK_MAX_HEADER_LENGTH
+ UAVTALK_MAX_PAYLOAD_LENGTH
) {
425 // incorrect packet size
426 connection
->stats
.rxErrors
++;
427 iproc
->state
= UAVTALK_STATE_ERROR
;
432 iproc
->state
= UAVTALK_STATE_OBJID
;
435 case UAVTALK_STATE_OBJID
:
438 iproc
->cs
= PIOS_CRC_updateByte(iproc
->cs
, rxbyte
);
440 iproc
->objId
+= rxbyte
<< (8 * (iproc
->rxCount
++));
441 if (iproc
->rxCount
< 4) {
447 iproc
->state
= UAVTALK_STATE_INSTID
;
450 case UAVTALK_STATE_INSTID
:
453 iproc
->cs
= PIOS_CRC_updateByte(iproc
->cs
, rxbyte
);
455 iproc
->instId
+= rxbyte
<< (8 * (iproc
->rxCount
++));
456 if (iproc
->rxCount
< 2) {
461 UAVObjHandle obj
= UAVObjGetByID(iproc
->objId
);
463 // Determine data length
464 if (iproc
->type
== UAVTALK_TYPE_OBJ_REQ
|| iproc
->type
== UAVTALK_TYPE_ACK
|| iproc
->type
== UAVTALK_TYPE_NACK
) {
466 iproc
->timestampLength
= 0;
468 iproc
->timestampLength
= (iproc
->type
& UAVTALK_TIMESTAMPED
) ? 2 : 0;
470 iproc
->length
= UAVObjGetNumBytes(obj
);
472 iproc
->length
= iproc
->packet_size
- iproc
->rxPacketLength
- iproc
->timestampLength
;
477 if (iproc
->length
>= UAVTALK_MAX_PAYLOAD_LENGTH
) {
478 // packet error - exceeded payload max length
479 connection
->stats
.rxErrors
++;
480 iproc
->state
= UAVTALK_STATE_ERROR
;
484 // Check the lengths match
485 if ((iproc
->rxPacketLength
+ iproc
->timestampLength
+ iproc
->length
) != iproc
->packet_size
) {
486 // packet error - mismatched packet size
487 connection
->stats
.rxErrors
++;
488 iproc
->state
= UAVTALK_STATE_ERROR
;
492 // Determine next state
493 if (iproc
->type
& UAVTALK_TIMESTAMPED
) {
494 // If there is a timestamp get it
495 iproc
->timestamp
= 0;
496 iproc
->state
= UAVTALK_STATE_TIMESTAMP
;
498 // If there is a payload get it, otherwise receive checksum
499 if (iproc
->length
> 0) {
500 iproc
->state
= UAVTALK_STATE_DATA
;
502 iproc
->state
= UAVTALK_STATE_CS
;
507 case UAVTALK_STATE_TIMESTAMP
:
510 iproc
->cs
= PIOS_CRC_updateByte(iproc
->cs
, rxbyte
);
512 iproc
->timestamp
+= rxbyte
<< (8 * (iproc
->rxCount
++));
513 if (iproc
->rxCount
< 2) {
518 // If there is a payload get it, otherwise receive checksum
519 if (iproc
->length
> 0) {
520 iproc
->state
= UAVTALK_STATE_DATA
;
522 iproc
->state
= UAVTALK_STATE_CS
;
526 case UAVTALK_STATE_DATA
:
529 iproc
->cs
= PIOS_CRC_updateByte(iproc
->cs
, rxbyte
);
531 connection
->rxBuffer
[iproc
->rxCount
++] = rxbyte
;
532 if (iproc
->rxCount
< iproc
->length
) {
537 iproc
->state
= UAVTALK_STATE_CS
;
540 case UAVTALK_STATE_CS
:
542 // Check the CRC byte
543 if (rxbyte
!= iproc
->cs
) {
544 // packet error - faulty CRC
545 UAVT_DEBUGLOG_PRINTF("BAD CRC");
546 connection
->stats
.rxCrcErrors
++;
547 connection
->stats
.rxErrors
++;
548 iproc
->state
= UAVTALK_STATE_ERROR
;
552 if (iproc
->rxPacketLength
!= (iproc
->packet_size
+ UAVTALK_CHECKSUM_LENGTH
)) {
553 // packet error - mismatched packet size
554 connection
->stats
.rxErrors
++;
555 iproc
->state
= UAVTALK_STATE_ERROR
;
559 connection
->stats
.rxObjects
++;
560 connection
->stats
.rxObjectBytes
+= iproc
->length
;
562 iproc
->state
= UAVTALK_STATE_COMPLETE
;
567 iproc
->state
= UAVTALK_STATE_ERROR
;
576 * Process an byte from the telemetry stream.
577 * \param[in] connection UAVTalkConnection to be used
578 * \param[in] rxbyte Received byte
579 * \return UAVTalkRxState
581 UAVTalkRxState
UAVTalkProcessInputStream(UAVTalkConnection connectionHandle
, uint8_t rxbyte
)
583 UAVTalkRxState state
= UAVTalkProcessInputStreamQuiet(connectionHandle
, rxbyte
);
585 if (state
== UAVTALK_STATE_COMPLETE
) {
586 UAVTalkReceiveObject(connectionHandle
);
593 * Send a parsed packet received on one connection handle out on a different connection handle.
594 * The packet must be in a complete state, meaning it is completed parsing.
595 * The packet is re-assembled from the component parts into a complete message and sent.
596 * This can be used to relay packets from one UAVTalk connection to another.
597 * \param[in] connection UAVTalkConnection to be used
598 * \param[in] rxbyte Received byte
602 int32_t UAVTalkRelayPacket(UAVTalkConnection inConnectionHandle
, UAVTalkConnection outConnectionHandle
)
604 UAVTalkConnectionData
*inConnection
;
606 CHECKCONHANDLE(inConnectionHandle
, inConnection
, return -1);
607 UAVTalkInputProcessor
*inIproc
= &inConnection
->iproc
;
609 // The input packet must be completely parsed.
610 if (inIproc
->state
!= UAVTALK_STATE_COMPLETE
) {
611 inConnection
->stats
.rxErrors
++;
616 UAVTalkConnectionData
*outConnection
;
617 CHECKCONHANDLE(outConnectionHandle
, outConnection
, return -1);
619 if (!outConnection
->outStream
) {
620 outConnection
->stats
.txErrors
++;
626 xSemaphoreTakeRecursive(outConnection
->lock
, portMAX_DELAY
);
628 outConnection
->txBuffer
[0] = UAVTALK_SYNC_VAL
;
630 outConnection
->txBuffer
[1] = inIproc
->type
;
631 // next 2 bytes are reserved for data length (inserted here later)
633 outConnection
->txBuffer
[4] = (uint8_t)(inIproc
->objId
& 0xFF);
634 outConnection
->txBuffer
[5] = (uint8_t)((inIproc
->objId
>> 8) & 0xFF);
635 outConnection
->txBuffer
[6] = (uint8_t)((inIproc
->objId
>> 16) & 0xFF);
636 outConnection
->txBuffer
[7] = (uint8_t)((inIproc
->objId
>> 24) & 0xFF);
638 outConnection
->txBuffer
[8] = (uint8_t)(inIproc
->instId
& 0xFF);
639 outConnection
->txBuffer
[9] = (uint8_t)((inIproc
->instId
>> 8) & 0xFF);
640 int32_t headerLength
= 10;
642 // Add timestamp when the transaction type is appropriate
643 if (inIproc
->type
& UAVTALK_TIMESTAMPED
) {
644 portTickType time
= xTaskGetTickCount();
645 outConnection
->txBuffer
[10] = (uint8_t)(time
& 0xFF);
646 outConnection
->txBuffer
[11] = (uint8_t)((time
>> 8) & 0xFF);
650 // Copy data (if any)
651 if (inIproc
->length
> 0) {
652 memcpy(&outConnection
->txBuffer
[headerLength
], inConnection
->rxBuffer
, inIproc
->length
);
655 // Store the packet length
656 outConnection
->txBuffer
[2] = (uint8_t)((headerLength
+ inIproc
->length
) & 0xFF);
657 outConnection
->txBuffer
[3] = (uint8_t)(((headerLength
+ inIproc
->length
) >> 8) & 0xFF);
660 outConnection
->txBuffer
[headerLength
+ inIproc
->length
] = inIproc
->cs
;
663 int32_t rc
= (*outConnection
->outStream
)(outConnection
->txBuffer
, headerLength
+ inIproc
->length
+ UAVTALK_CHECKSUM_LENGTH
);
666 outConnection
->stats
.txBytes
+= (rc
> 0) ? rc
: 0;
668 // evaluate return value before releasing the lock
670 if (rc
!= (int32_t)(headerLength
+ inIproc
->length
+ UAVTALK_CHECKSUM_LENGTH
)) {
671 outConnection
->stats
.txErrors
++;
676 xSemaphoreGiveRecursive(outConnection
->lock
);
683 * Complete receiving a UAVTalk packet. This will cause the packet to be unpacked, acked, etc.
684 * \param[in] connectionHandle UAVTalkConnection to be used
688 int32_t UAVTalkReceiveObject(UAVTalkConnection connectionHandle
)
690 UAVTalkConnectionData
*connection
;
692 CHECKCONHANDLE(connectionHandle
, connection
, return -1);
694 UAVTalkInputProcessor
*iproc
= &connection
->iproc
;
695 if (iproc
->state
!= UAVTALK_STATE_COMPLETE
) {
699 return receiveObject(connection
, iproc
->type
, iproc
->objId
, iproc
->instId
, connection
->rxBuffer
);
703 * Get the object ID of the current packet.
704 * \param[in] connectionHandle UAVTalkConnection to be used
705 * \return The object ID, or 0 on error.
707 uint32_t UAVTalkGetPacketObjId(UAVTalkConnection connectionHandle
)
709 UAVTalkConnectionData
*connection
;
711 CHECKCONHANDLE(connectionHandle
, connection
, return 0);
713 return connection
->iproc
.objId
;
717 * Receive an object. This function process objects received through the telemetry stream.
719 * Parser errors are considered as transmission errors and are not NACKed.
720 * Some senders (GCS) can timeout and retry if the message is not answered by an ack or nack.
722 * Object handling errors are considered as application errors and are NACked.
723 * In that case we want to nack as there is no point in the sender retrying to send invalid objects.
725 * \param[in] connection UAVTalkConnection to be used
726 * \param[in] type Type of received message (UAVTALK_TYPE_OBJ, UAVTALK_TYPE_OBJ_REQ, UAVTALK_TYPE_OBJ_ACK, UAVTALK_TYPE_ACK, UAVTALK_TYPE_NACK)
727 * \param[in] objId ID of the object to work on
728 * \param[in] instId The instance ID of UAVOBJ_ALL_INSTANCES for all instances.
729 * \param[in] data Data buffer
730 * \param[in] length Buffer length
734 static int32_t receiveObject(UAVTalkConnectionData
*connection
, uint8_t type
, uint32_t objId
, uint16_t instId
, uint8_t *data
)
740 xSemaphoreTakeRecursive(connection
->lock
, portMAX_DELAY
);
742 // Get the handle to the object. Will be null if object does not exist.
744 // Here we ask for instance ID 0 without taking into account the provided instId
745 // The provided instId will be used later when packing, unpacking, etc...
746 // TODO the above should be fixed as it is cumbersome and error prone
747 obj
= UAVObjGetByID(objId
);
749 // Process message type
751 case UAVTALK_TYPE_OBJ
:
752 case UAVTALK_TYPE_OBJ_TS
:
753 // All instances not allowed for OBJ messages
754 if (obj
&& (instId
!= UAVOBJ_ALL_INSTANCES
)) {
755 // Unpack object, if the instance does not exist it will be created!
756 if (UAVObjUnpack(obj
, instId
, data
) == 0) {
757 // Check if this object acks a pending OBJ_REQ message
758 // any OBJ message can ack a pending OBJ_REQ message
759 // even one that was not sent in response to the OBJ_REQ message
760 updateAck(connection
, type
, objId
, instId
);
769 case UAVTALK_TYPE_OBJ_ACK
:
770 case UAVTALK_TYPE_OBJ_ACK_TS
:
771 UAVT_DEBUGLOG_CPRINTF(objId
, "OBJ_ACK %X %d", objId
, instId
);
772 // All instances not allowed for OBJ_ACK messages
773 if (obj
&& (instId
!= UAVOBJ_ALL_INSTANCES
)) {
774 // Unpack object, if the instance does not exist it will be created!
775 if (UAVObjUnpack(obj
, instId
, data
) == 0) {
776 UAVT_DEBUGLOG_CPRINTF(objId
, "OBJ ACK %X %d", objId
, instId
);
777 // Object updated or created, transmit ACK
778 sendObject(connection
, UAVTALK_TYPE_ACK
, objId
, instId
, NULL
);
786 // failed to update object, transmit NACK
787 UAVT_DEBUGLOG_PRINTF("OBJ NACK %X %d", objId
, instId
);
788 sendObject(connection
, UAVTALK_TYPE_NACK
, objId
, instId
, NULL
);
792 case UAVTALK_TYPE_OBJ_REQ
:
793 // Check if requested object exists
794 UAVT_DEBUGLOG_CPRINTF(objId
, "REQ %X %d", objId
, instId
);
796 // Object found, transmit it
797 // The sent object will ack the object request on the receiver side
798 ret
= sendObject(connection
, UAVTALK_TYPE_OBJ
, objId
, instId
, obj
);
803 // failed to send object, transmit NACK
804 UAVT_DEBUGLOG_PRINTF("REQ NACK %X %d", objId
, instId
);
805 sendObject(connection
, UAVTALK_TYPE_NACK
, objId
, instId
, NULL
);
809 case UAVTALK_TYPE_NACK
:
810 // Do nothing on flight side, let it time out.
812 // The transaction takes the result code of the "semaphore taking operation" into account to determine success.
813 // If we give that semaphore in time, its "success" (ack received)
814 // If we do not give that semaphore before the timeout it will return failure.
815 // What would have to be done here is give the semaphore, but set a flag (for example connection->respFail=true)
816 // that indicates failure and then above where it checks for the result code, have it behave as if it failed
817 // if the explicit failure is set.
820 case UAVTALK_TYPE_ACK
:
821 // All instances not allowed for ACK messages
822 if (obj
&& (instId
!= UAVOBJ_ALL_INSTANCES
)) {
823 // Check if an ACK is pending
824 updateAck(connection
, type
, objId
, instId
);
835 xSemaphoreGiveRecursive(connection
->lock
);
842 * Check if an ack is pending on an object and give response semaphore
843 * \param[in] connection UAVTalkConnection to be used
844 * \param[in] obj Object
845 * \param[in] instId The instance ID of UAVOBJ_ALL_INSTANCES for all instances.
847 static void updateAck(UAVTalkConnectionData
*connection
, uint8_t type
, uint32_t objId
, uint16_t instId
)
849 if ((connection
->respObjId
== objId
) && (connection
->respType
== type
)) {
850 if ((connection
->respInstId
== UAVOBJ_ALL_INSTANCES
) && (instId
== 0)) {
851 // last instance received, complete transaction
852 xSemaphoreGive(connection
->respSema
);
853 connection
->respObjId
= 0;
854 } else if (connection
->respInstId
== instId
) {
855 xSemaphoreGive(connection
->respSema
);
856 connection
->respObjId
= 0;
862 * Send an object through the telemetry link.
863 * \param[in] connection UAVTalkConnection to be used
864 * \param[in] type Transaction type
865 * \param[in] objId The object ID
866 * \param[in] instId The instance ID or UAVOBJ_ALL_INSTANCES for all instances
867 * \param[in] obj Object handle to send (null when type is NACK)
871 static int32_t sendObject(UAVTalkConnectionData
*connection
, uint8_t type
, uint32_t objId
, uint16_t instId
, UAVObjHandle obj
)
877 // Important note : obj can be null (when type is NACK for example) so protect all obj dereferences.
879 // If all instances are requested and this is a single instance object, force instance ID to zero
880 if ((obj
!= NULL
) && (instId
== UAVOBJ_ALL_INSTANCES
) && UAVObjIsSingleInstance(obj
)) {
884 // Process message type
885 if (type
== UAVTALK_TYPE_OBJ
|| type
== UAVTALK_TYPE_OBJ_TS
|| type
== UAVTALK_TYPE_OBJ_ACK
|| type
== UAVTALK_TYPE_OBJ_ACK_TS
) {
886 if (instId
== UAVOBJ_ALL_INSTANCES
) {
887 // Get number of instances
888 numInst
= UAVObjGetNumInstances(obj
);
889 // Send all instances in reverse order
890 // This allows the receiver to detect when the last object has been received (i.e. when instance 0 is received)
892 for (n
= 0; n
< numInst
; ++n
) {
893 ret
= sendSingleObject(connection
, type
, objId
, numInst
- n
- 1, obj
);
899 ret
= sendSingleObject(connection
, type
, objId
, instId
, obj
);
901 } else if (type
== UAVTALK_TYPE_OBJ_REQ
) {
902 ret
= sendSingleObject(connection
, type
, objId
, instId
, obj
);
903 } else if (type
== UAVTALK_TYPE_ACK
|| type
== UAVTALK_TYPE_NACK
) {
904 if (instId
!= UAVOBJ_ALL_INSTANCES
) {
905 ret
= sendSingleObject(connection
, type
, objId
, instId
, obj
);
913 * Send an object through the telemetry link.
914 * \param[in] connection UAVTalkConnection to be used
915 * \param[in] type Transaction type
916 * \param[in] objId The object ID
917 * \param[in] instId The instance ID (can NOT be UAVOBJ_ALL_INSTANCES, use () instead)
918 * \param[in] obj Object handle to send (null when type is NACK)
922 static int32_t sendSingleObject(UAVTalkConnectionData
*connection
, uint8_t type
, uint32_t objId
, uint16_t instId
, UAVObjHandle obj
)
924 // IMPORTANT : obj can be null (when type is NACK for example)
926 if (!connection
->outStream
) {
927 connection
->stats
.txErrors
++;
932 connection
->txBuffer
[0] = UAVTALK_SYNC_VAL
;
934 connection
->txBuffer
[1] = type
;
935 // next 2 bytes are reserved for data length (inserted here later)
937 connection
->txBuffer
[4] = (uint8_t)(objId
& 0xFF);
938 connection
->txBuffer
[5] = (uint8_t)((objId
>> 8) & 0xFF);
939 connection
->txBuffer
[6] = (uint8_t)((objId
>> 16) & 0xFF);
940 connection
->txBuffer
[7] = (uint8_t)((objId
>> 24) & 0xFF);
942 connection
->txBuffer
[8] = (uint8_t)(instId
& 0xFF);
943 connection
->txBuffer
[9] = (uint8_t)((instId
>> 8) & 0xFF);
944 int32_t headerLength
= 10;
946 // Add timestamp when the transaction type is appropriate
947 if (type
& UAVTALK_TIMESTAMPED
) {
948 portTickType time
= xTaskGetTickCount();
949 connection
->txBuffer
[10] = (uint8_t)(time
& 0xFF);
950 connection
->txBuffer
[11] = (uint8_t)((time
>> 8) & 0xFF);
954 // Determine data length
956 if (type
== UAVTALK_TYPE_OBJ_REQ
|| type
== UAVTALK_TYPE_ACK
|| type
== UAVTALK_TYPE_NACK
) {
959 length
= UAVObjGetNumBytes(obj
);
963 if (length
> UAVOBJECTS_LARGEST
) {
964 connection
->stats
.txErrors
++;
968 // Copy data (if any)
970 if (UAVObjPack(obj
, instId
, &connection
->txBuffer
[headerLength
]) == -1) {
971 connection
->stats
.txErrors
++;
976 // Store the packet length
977 connection
->txBuffer
[2] = (uint8_t)((headerLength
+ length
) & 0xFF);
978 connection
->txBuffer
[3] = (uint8_t)(((headerLength
+ length
) >> 8) & 0xFF);
980 // Calculate and store checksum
981 connection
->txBuffer
[headerLength
+ length
] = PIOS_CRC_updateCRC(0, connection
->txBuffer
, headerLength
+ length
);
984 uint16_t tx_msg_len
= headerLength
+ length
+ UAVTALK_CHECKSUM_LENGTH
;
985 int32_t rc
= (*connection
->outStream
)(connection
->txBuffer
, tx_msg_len
);
988 if (rc
== tx_msg_len
) {
989 ++connection
->stats
.txObjects
;
990 connection
->stats
.txObjectBytes
+= length
;
991 connection
->stats
.txBytes
+= tx_msg_len
;
993 connection
->stats
.txErrors
++;
994 // TODO rc == -1 connection not open, -2 buffer full should retry
995 connection
->stats
.txBytes
+= (rc
> 0) ? rc
: 0;