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
);
56 // UavTalk Process FSM functions
57 static bool UAVTalkProcess_SYNC(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
);
58 static bool UAVTalkProcess_TYPE(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
);
59 static bool UAVTalkProcess_OBJID(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
);
60 static bool UAVTalkProcess_INSTID(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
);
61 static bool UAVTalkProcess_SIZE(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
);
62 static bool UAVTalkProcess_TIMESTAMP(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
);
63 static bool UAVTalkProcess_DATA(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
);
64 static bool UAVTalkProcess_CS(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
);
66 * Initialize the UAVTalk library
67 * \param[in] connection UAVTalkConnection to be used
68 * \param[in] outputStream Function pointer that is called to send a data buffer
72 UAVTalkConnection
UAVTalkInitialize(UAVTalkOutputStream outputStream
)
75 UAVTalkConnectionData
*connection
= pios_malloc(sizeof(UAVTalkConnectionData
));
80 connection
->canari
= UAVTALK_CANARI
;
81 connection
->iproc
.rxPacketLength
= 0;
82 connection
->iproc
.state
= UAVTALK_STATE_SYNC
;
83 connection
->outStream
= outputStream
;
84 connection
->lock
= xSemaphoreCreateRecursiveMutex();
85 connection
->transLock
= xSemaphoreCreateRecursiveMutex();
87 connection
->rxBuffer
= pios_malloc(UAVTALK_MAX_PACKET_LENGTH
);
88 if (!connection
->rxBuffer
) {
91 connection
->txBuffer
= pios_malloc(UAVTALK_MAX_PACKET_LENGTH
);
92 if (!connection
->txBuffer
) {
95 vSemaphoreCreateBinary(connection
->respSema
);
96 xSemaphoreTake(connection
->respSema
, 0); // reset to zero
97 UAVTalkResetStats((UAVTalkConnection
)connection
);
98 return (UAVTalkConnection
)connection
;
102 * Set the communication output stream
103 * \param[in] connection UAVTalkConnection to be used
104 * \param[in] outputStream Function pointer that is called to send a data buffer
108 int32_t UAVTalkSetOutputStream(UAVTalkConnection connectionHandle
, UAVTalkOutputStream outputStream
)
110 UAVTalkConnectionData
*connection
;
112 CHECKCONHANDLE(connectionHandle
, connection
, return -1);
115 xSemaphoreTakeRecursive(connection
->lock
, portMAX_DELAY
);
118 connection
->outStream
= outputStream
;
121 xSemaphoreGiveRecursive(connection
->lock
);
127 * Get current output stream
128 * \param[in] connection UAVTalkConnection to be used
129 * @return UAVTarlkOutputStream the output stream used
131 UAVTalkOutputStream
UAVTalkGetOutputStream(UAVTalkConnection connectionHandle
)
133 UAVTalkConnectionData
*connection
;
135 CHECKCONHANDLE(connectionHandle
, connection
, return NULL
);
136 return connection
->outStream
;
140 * Get communication statistics counters
141 * \param[in] connection UAVTalkConnection to be used
142 * @param[out] statsOut Statistics counters
144 void UAVTalkGetStats(UAVTalkConnection connectionHandle
, UAVTalkStats
*statsOut
, bool reset
)
146 UAVTalkConnectionData
*connection
;
148 CHECKCONHANDLE(connectionHandle
, connection
, return );
151 xSemaphoreTakeRecursive(connection
->lock
, portMAX_DELAY
);
154 memcpy(statsOut
, &connection
->stats
, sizeof(UAVTalkStats
));
158 memset(&connection
->stats
, 0, sizeof(UAVTalkStats
));
162 xSemaphoreGiveRecursive(connection
->lock
);
166 * Get communication statistics counters
167 * \param[in] connection UAVTalkConnection to be used
168 * @param[out] statsOut Statistics counters
170 void UAVTalkAddStats(UAVTalkConnection connectionHandle
, UAVTalkStats
*statsOut
, bool reset
)
172 UAVTalkConnectionData
*connection
;
174 CHECKCONHANDLE(connectionHandle
, connection
, return );
177 xSemaphoreTakeRecursive(connection
->lock
, portMAX_DELAY
);
180 statsOut
->txBytes
+= connection
->stats
.txBytes
;
181 statsOut
->txObjectBytes
+= connection
->stats
.txObjectBytes
;
182 statsOut
->txObjects
+= connection
->stats
.txObjects
;
183 statsOut
->txErrors
+= connection
->stats
.txErrors
;
184 statsOut
->rxBytes
+= connection
->stats
.rxBytes
;
185 statsOut
->rxObjectBytes
+= connection
->stats
.rxObjectBytes
;
186 statsOut
->rxObjects
+= connection
->stats
.rxObjects
;
187 statsOut
->rxErrors
+= connection
->stats
.rxErrors
;
188 statsOut
->rxSyncErrors
+= connection
->stats
.rxSyncErrors
;
189 statsOut
->rxCrcErrors
+= connection
->stats
.rxCrcErrors
;
193 memset(&connection
->stats
, 0, sizeof(UAVTalkStats
));
197 xSemaphoreGiveRecursive(connection
->lock
);
201 * Reset the statistics counters.
202 * \param[in] connection UAVTalkConnection to be used
204 void UAVTalkResetStats(UAVTalkConnection connectionHandle
)
206 UAVTalkConnectionData
*connection
;
208 CHECKCONHANDLE(connectionHandle
, connection
, return );
211 xSemaphoreTakeRecursive(connection
->lock
, portMAX_DELAY
);
214 memset(&connection
->stats
, 0, sizeof(UAVTalkStats
));
217 xSemaphoreGiveRecursive(connection
->lock
);
221 * Accessor method to get the timestamp from the last UAVTalk message
223 void UAVTalkGetLastTimestamp(UAVTalkConnection connectionHandle
, uint16_t *timestamp
)
225 UAVTalkConnectionData
*connection
;
227 CHECKCONHANDLE(connectionHandle
, connection
, return );
229 UAVTalkInputProcessor
*iproc
= &connection
->iproc
;
230 *timestamp
= iproc
->timestamp
;
234 * Request an update for the specified object, on success the object data would have been
235 * updated by the GCS.
236 * \param[in] connection UAVTalkConnection to be used
237 * \param[in] obj Object to update
238 * \param[in] instId The instance ID or UAVOBJ_ALL_INSTANCES for all instances.
239 * \param[in] timeout Time to wait for the response, when zero it will return immediately
243 int32_t UAVTalkSendObjectRequest(UAVTalkConnection connectionHandle
, UAVObjHandle obj
, uint16_t instId
, int32_t timeout
)
245 UAVTalkConnectionData
*connection
;
247 CHECKCONHANDLE(connectionHandle
, connection
, return -1);
249 return objectTransaction(connection
, UAVTALK_TYPE_OBJ_REQ
, obj
, instId
, timeout
);
253 * Send the specified object through the telemetry link.
254 * \param[in] connection UAVTalkConnection to be used
255 * \param[in] obj Object to send
256 * \param[in] instId The instance ID or UAVOBJ_ALL_INSTANCES for all instances.
257 * \param[in] acked Selects if an ack is required (1:ack required, 0: ack not required)
258 * \param[in] timeoutMs Time to wait for the ack, when zero it will return immediately
262 int32_t UAVTalkSendObject(UAVTalkConnection connectionHandle
, UAVObjHandle obj
, uint16_t instId
, uint8_t acked
, int32_t timeoutMs
)
264 UAVTalkConnectionData
*connection
;
266 CHECKCONHANDLE(connectionHandle
, connection
, return -1);
270 return objectTransaction(connection
, UAVTALK_TYPE_OBJ_ACK
, obj
, instId
, timeoutMs
);
272 return objectTransaction(connection
, UAVTALK_TYPE_OBJ
, obj
, instId
, timeoutMs
);
277 * Send the specified object through the telemetry link with a timestamp.
278 * \param[in] connection UAVTalkConnection to be used
279 * \param[in] obj Object to send
280 * \param[in] instId The instance ID or UAVOBJ_ALL_INSTANCES for all instances.
281 * \param[in] acked Selects if an ack is required (1:ack required, 0: ack not required)
282 * \param[in] timeoutMs Time to wait for the ack, when zero it will return immediately
286 int32_t UAVTalkSendObjectTimestamped(UAVTalkConnection connectionHandle
, UAVObjHandle obj
, uint16_t instId
, uint8_t acked
, int32_t timeoutMs
)
288 UAVTalkConnectionData
*connection
;
290 CHECKCONHANDLE(connectionHandle
, connection
, return -1);
294 return objectTransaction(connection
, UAVTALK_TYPE_OBJ_ACK_TS
, obj
, instId
, timeoutMs
);
296 return objectTransaction(connection
, UAVTALK_TYPE_OBJ_TS
, obj
, instId
, timeoutMs
);
301 * Execute the requested transaction on an object.
302 * \param[in] connection UAVTalkConnection to be used
303 * \param[in] type Transaction type
304 * UAVTALK_TYPE_OBJ: send object,
305 * UAVTALK_TYPE_OBJ_REQ: request object update
306 * UAVTALK_TYPE_OBJ_ACK: send object with an ack
307 * \param[in] obj Object
308 * \param[in] instId The instance ID of UAVOBJ_ALL_INSTANCES for all instances.
309 * \param[in] timeoutMs Time to wait for the ack, when zero it will return immediately
313 static int32_t objectTransaction(UAVTalkConnectionData
*connection
, uint8_t type
, UAVObjHandle obj
, uint16_t instId
, int32_t timeoutMs
)
315 int32_t respReceived
;
318 // Send object depending on if a response is needed
319 if (type
== UAVTALK_TYPE_OBJ_ACK
|| type
== UAVTALK_TYPE_OBJ_ACK_TS
|| type
== UAVTALK_TYPE_OBJ_REQ
) {
320 // Get transaction lock (will block if a transaction is pending)
321 xSemaphoreTakeRecursive(connection
->transLock
, portMAX_DELAY
);
323 xSemaphoreTakeRecursive(connection
->lock
, portMAX_DELAY
);
324 // expected response type
325 connection
->respType
= (type
== UAVTALK_TYPE_OBJ_REQ
) ? UAVTALK_TYPE_OBJ
: UAVTALK_TYPE_ACK
;
326 connection
->respObjId
= UAVObjGetID(obj
);
327 connection
->respInstId
= instId
;
328 ret
= sendObject(connection
, type
, UAVObjGetID(obj
), instId
, obj
);
329 xSemaphoreGiveRecursive(connection
->lock
);
330 // Wait for response (or timeout) if sending the object succeeded
331 respReceived
= pdFALSE
;
333 respReceived
= xSemaphoreTake(connection
->respSema
, timeoutMs
/ portTICK_RATE_MS
);
335 // Check if a response was received
336 if (respReceived
== pdTRUE
) {
337 // We are done successfully
338 xSemaphoreGiveRecursive(connection
->transLock
);
341 // Cancel transaction
342 xSemaphoreTakeRecursive(connection
->lock
, portMAX_DELAY
);
343 // non blocking call to make sure the value is reset to zero (binary sema)
344 xSemaphoreTake(connection
->respSema
, 0);
345 connection
->respObjId
= 0;
346 xSemaphoreGiveRecursive(connection
->lock
);
347 xSemaphoreGiveRecursive(connection
->transLock
);
350 } else if (type
== UAVTALK_TYPE_OBJ
|| type
== UAVTALK_TYPE_OBJ_TS
) {
351 xSemaphoreTakeRecursive(connection
->lock
, portMAX_DELAY
);
352 ret
= sendObject(connection
, type
, UAVObjGetID(obj
), instId
, obj
);
353 xSemaphoreGiveRecursive(connection
->lock
);
359 * Process an byte from the telemetry stream.
360 * \param[in] connectionHandle UAVTalkConnection to be used
361 * \param[in] rxbuffer Received buffer
362 * \param[in/out] Length in bytes of received buffer
363 * \param[in/out] position Next item to be read inside rxbuffer
364 * \return UAVTalkRxState
366 UAVTalkRxState
UAVTalkProcessInputStreamQuiet(UAVTalkConnection connectionHandle
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
)
368 UAVTalkConnectionData
*connection
;
370 CHECKCONHANDLE(connectionHandle
, connection
, return -1);
372 UAVTalkInputProcessor
*iproc
= &connection
->iproc
;
374 if (iproc
->state
== UAVTALK_STATE_ERROR
|| iproc
->state
== UAVTALK_STATE_COMPLETE
) {
375 iproc
->state
= UAVTALK_STATE_SYNC
;
378 uint8_t processedBytes
= (*position
);
380 // stop processing as soon as a complete packet is received, error is encountered or buffer is processed entirely
381 while ((length
> (*position
))
382 && iproc
->state
!= UAVTALK_STATE_COMPLETE
383 && iproc
->state
!= UAVTALK_STATE_ERROR
) {
384 // Receive state machine
385 if ((length
> (*position
)) && iproc
->state
== UAVTALK_STATE_SYNC
&&
386 !UAVTalkProcess_SYNC(connection
, iproc
, rxbuffer
, length
, position
)) {
390 if ((length
> (*position
)) && iproc
->state
== UAVTALK_STATE_TYPE
&&
391 !UAVTalkProcess_TYPE(connection
, iproc
, rxbuffer
, length
, position
)) {
395 if ((length
> (*position
)) && iproc
->state
== UAVTALK_STATE_SIZE
&&
396 !UAVTalkProcess_SIZE(connection
, iproc
, rxbuffer
, length
, position
)) {
400 if ((length
> (*position
)) && iproc
->state
== UAVTALK_STATE_OBJID
&&
401 !UAVTalkProcess_OBJID(connection
, iproc
, rxbuffer
, length
, position
)) {
405 if ((length
> (*position
)) && iproc
->state
== UAVTALK_STATE_INSTID
&&
406 !UAVTalkProcess_INSTID(connection
, iproc
, rxbuffer
, length
, position
)) {
410 if ((length
> (*position
)) && iproc
->state
== UAVTALK_STATE_TIMESTAMP
&&
411 !UAVTalkProcess_TIMESTAMP(connection
, iproc
, rxbuffer
, length
, position
)) {
415 if ((length
> (*position
)) && iproc
->state
== UAVTALK_STATE_DATA
&&
416 !UAVTalkProcess_DATA(connection
, iproc
, rxbuffer
, length
, position
)) {
420 if ((length
> (*position
)) && iproc
->state
== UAVTALK_STATE_CS
&&
421 !UAVTalkProcess_CS(connection
, iproc
, rxbuffer
, length
, position
)) {
427 processedBytes
= (*position
) - processedBytes
;
428 connection
->stats
.rxBytes
+= processedBytes
;
433 * Process a buffer from the telemetry stream.
434 * \param[in] connection UAVTalkConnection to be used
435 * \param[in] rxbuffer Received buffer
436 * \param[in] count bytes inside rxbuffer
437 * \return UAVTalkRxState
439 UAVTalkRxState
UAVTalkProcessInputStream(UAVTalkConnection connectionHandle
, uint8_t *rxbuffer
, uint8_t length
)
441 uint8_t position
= 0;
442 UAVTalkRxState state
= UAVTALK_STATE_ERROR
;
444 while (position
< length
) {
445 state
= UAVTalkProcessInputStreamQuiet(connectionHandle
, rxbuffer
, length
, &position
);
446 if (state
== UAVTALK_STATE_COMPLETE
) {
447 UAVTalkReceiveObject(connectionHandle
);
454 * Send a parsed packet received on one connection handle out on a different connection handle.
455 * The packet must be in a complete state, meaning it is completed parsing.
456 * The packet is re-assembled from the component parts into a complete message and sent.
457 * This can be used to relay packets from one UAVTalk connection to another.
458 * \param[in] connection UAVTalkConnection to be used
459 * \param[in] rxbyte Received byte
463 int32_t UAVTalkRelayPacket(UAVTalkConnection inConnectionHandle
, UAVTalkConnection outConnectionHandle
)
465 UAVTalkConnectionData
*inConnection
;
467 CHECKCONHANDLE(inConnectionHandle
, inConnection
, return -1);
468 UAVTalkInputProcessor
*inIproc
= &inConnection
->iproc
;
470 // The input packet must be completely parsed.
471 if (inIproc
->state
!= UAVTALK_STATE_COMPLETE
) {
472 inConnection
->stats
.rxErrors
++;
477 UAVTalkConnectionData
*outConnection
;
478 CHECKCONHANDLE(outConnectionHandle
, outConnection
, return -1);
480 if (!outConnection
->outStream
) {
481 outConnection
->stats
.txErrors
++;
487 xSemaphoreTakeRecursive(outConnection
->lock
, portMAX_DELAY
);
489 outConnection
->txBuffer
[0] = UAVTALK_SYNC_VAL
;
491 outConnection
->txBuffer
[1] = inIproc
->type
;
492 // next 2 bytes are reserved for data length (inserted here later)
494 outConnection
->txBuffer
[4] = (uint8_t)(inIproc
->objId
& 0xFF);
495 outConnection
->txBuffer
[5] = (uint8_t)((inIproc
->objId
>> 8) & 0xFF);
496 outConnection
->txBuffer
[6] = (uint8_t)((inIproc
->objId
>> 16) & 0xFF);
497 outConnection
->txBuffer
[7] = (uint8_t)((inIproc
->objId
>> 24) & 0xFF);
499 outConnection
->txBuffer
[8] = (uint8_t)(inIproc
->instId
& 0xFF);
500 outConnection
->txBuffer
[9] = (uint8_t)((inIproc
->instId
>> 8) & 0xFF);
501 int32_t headerLength
= 10;
503 // Add timestamp when the transaction type is appropriate
504 if (inIproc
->type
& UAVTALK_TIMESTAMPED
) {
505 portTickType time
= xTaskGetTickCount();
506 outConnection
->txBuffer
[10] = (uint8_t)(time
& 0xFF);
507 outConnection
->txBuffer
[11] = (uint8_t)((time
>> 8) & 0xFF);
511 // Copy data (if any)
512 if (inIproc
->length
> 0) {
513 memcpy(&outConnection
->txBuffer
[headerLength
], inConnection
->rxBuffer
, inIproc
->length
);
516 // Store the packet length
517 outConnection
->txBuffer
[2] = (uint8_t)((headerLength
+ inIproc
->length
) & 0xFF);
518 outConnection
->txBuffer
[3] = (uint8_t)(((headerLength
+ inIproc
->length
) >> 8) & 0xFF);
521 outConnection
->txBuffer
[headerLength
+ inIproc
->length
] = inIproc
->cs
;
524 int32_t rc
= (*outConnection
->outStream
)(outConnection
->txBuffer
, headerLength
+ inIproc
->length
+ UAVTALK_CHECKSUM_LENGTH
);
527 outConnection
->stats
.txBytes
+= (rc
> 0) ? rc
: 0;
529 // evaluate return value before releasing the lock
531 if (rc
!= (int32_t)(headerLength
+ inIproc
->length
+ UAVTALK_CHECKSUM_LENGTH
)) {
532 outConnection
->stats
.txErrors
++;
537 xSemaphoreGiveRecursive(outConnection
->lock
);
544 * Complete receiving a UAVTalk packet. This will cause the packet to be unpacked, acked, etc.
545 * \param[in] connectionHandle UAVTalkConnection to be used
549 int32_t UAVTalkReceiveObject(UAVTalkConnection connectionHandle
)
551 UAVTalkConnectionData
*connection
;
553 CHECKCONHANDLE(connectionHandle
, connection
, return -1);
555 UAVTalkInputProcessor
*iproc
= &connection
->iproc
;
556 if (iproc
->state
!= UAVTALK_STATE_COMPLETE
) {
560 return receiveObject(connection
, iproc
->type
, iproc
->objId
, iproc
->instId
, connection
->rxBuffer
);
564 * Get the object ID of the current packet.
565 * \param[in] connectionHandle UAVTalkConnection to be used
566 * \return The object ID, or 0 on error.
568 uint32_t UAVTalkGetPacketObjId(UAVTalkConnection connectionHandle
)
570 UAVTalkConnectionData
*connection
;
572 CHECKCONHANDLE(connectionHandle
, connection
, return 0);
574 return connection
->iproc
.objId
;
578 * Receive an object. This function process objects received through the telemetry stream.
580 * Parser errors are considered as transmission errors and are not NACKed.
581 * Some senders (GCS) can timeout and retry if the message is not answered by an ack or nack.
583 * Object handling errors are considered as application errors and are NACked.
584 * In that case we want to nack as there is no point in the sender retrying to send invalid objects.
586 * \param[in] connection UAVTalkConnection to be used
587 * \param[in] type Type of received message (UAVTALK_TYPE_OBJ, UAVTALK_TYPE_OBJ_REQ, UAVTALK_TYPE_OBJ_ACK, UAVTALK_TYPE_ACK, UAVTALK_TYPE_NACK)
588 * \param[in] objId ID of the object to work on
589 * \param[in] instId The instance ID of UAVOBJ_ALL_INSTANCES for all instances.
590 * \param[in] data Data buffer
591 * \param[in] length Buffer length
595 static int32_t receiveObject(UAVTalkConnectionData
*connection
, uint8_t type
, uint32_t objId
, uint16_t instId
, uint8_t *data
)
601 xSemaphoreTakeRecursive(connection
->lock
, portMAX_DELAY
);
603 // Get the handle to the object. Will be null if object does not exist.
605 // Here we ask for instance ID 0 without taking into account the provided instId
606 // The provided instId will be used later when packing, unpacking, etc...
607 // TODO the above should be fixed as it is cumbersome and error prone
608 obj
= UAVObjGetByID(objId
);
610 // Process message type
612 case UAVTALK_TYPE_OBJ
:
613 case UAVTALK_TYPE_OBJ_TS
:
614 // All instances not allowed for OBJ messages
615 if (obj
&& (instId
!= UAVOBJ_ALL_INSTANCES
)) {
616 // Unpack object, if the instance does not exist it will be created!
617 if (UAVObjUnpack(obj
, instId
, data
) == 0) {
618 // Check if this object acks a pending OBJ_REQ message
619 // any OBJ message can ack a pending OBJ_REQ message
620 // even one that was not sent in response to the OBJ_REQ message
621 updateAck(connection
, type
, objId
, instId
);
630 case UAVTALK_TYPE_OBJ_ACK
:
631 case UAVTALK_TYPE_OBJ_ACK_TS
:
632 UAVT_DEBUGLOG_CPRINTF(objId
, "OBJ_ACK %X %d", objId
, instId
);
633 // All instances not allowed for OBJ_ACK messages
634 if (obj
&& (instId
!= UAVOBJ_ALL_INSTANCES
)) {
635 // Unpack object, if the instance does not exist it will be created!
636 if (UAVObjUnpack(obj
, instId
, data
) == 0) {
637 UAVT_DEBUGLOG_CPRINTF(objId
, "OBJ ACK %X %d", objId
, instId
);
638 // Object updated or created, transmit ACK
639 sendObject(connection
, UAVTALK_TYPE_ACK
, objId
, instId
, NULL
);
647 // failed to update object, transmit NACK
648 UAVT_DEBUGLOG_PRINTF("OBJ NACK %X %d", objId
, instId
);
649 sendObject(connection
, UAVTALK_TYPE_NACK
, objId
, instId
, NULL
);
653 case UAVTALK_TYPE_OBJ_REQ
:
654 // Check if requested object exists
655 UAVT_DEBUGLOG_CPRINTF(objId
, "REQ %X %d", objId
, instId
);
657 // Object found, transmit it
658 // The sent object will ack the object request on the receiver side
659 ret
= sendObject(connection
, UAVTALK_TYPE_OBJ
, objId
, instId
, obj
);
664 // failed to send object, transmit NACK
665 UAVT_DEBUGLOG_PRINTF("REQ NACK %X %d", objId
, instId
);
666 sendObject(connection
, UAVTALK_TYPE_NACK
, objId
, instId
, NULL
);
670 case UAVTALK_TYPE_NACK
:
671 // Do nothing on flight side, let it time out.
673 // The transaction takes the result code of the "semaphore taking operation" into account to determine success.
674 // If we give that semaphore in time, its "success" (ack received)
675 // If we do not give that semaphore before the timeout it will return failure.
676 // What would have to be done here is give the semaphore, but set a flag (for example connection->respFail=true)
677 // that indicates failure and then above where it checks for the result code, have it behave as if it failed
678 // if the explicit failure is set.
681 case UAVTALK_TYPE_ACK
:
682 // All instances not allowed for ACK messages
683 if (obj
&& (instId
!= UAVOBJ_ALL_INSTANCES
)) {
684 // Check if an ACK is pending
685 updateAck(connection
, type
, objId
, instId
);
696 xSemaphoreGiveRecursive(connection
->lock
);
703 * Check if an ack is pending on an object and give response semaphore
704 * \param[in] connection UAVTalkConnection to be used
705 * \param[in] obj Object
706 * \param[in] instId The instance ID of UAVOBJ_ALL_INSTANCES for all instances.
708 static void updateAck(UAVTalkConnectionData
*connection
, uint8_t type
, uint32_t objId
, uint16_t instId
)
710 if ((connection
->respObjId
== objId
) && (connection
->respType
== type
)) {
711 if ((connection
->respInstId
== UAVOBJ_ALL_INSTANCES
) && (instId
== 0)) {
712 // last instance received, complete transaction
713 xSemaphoreGive(connection
->respSema
);
714 connection
->respObjId
= 0;
715 } else if (connection
->respInstId
== instId
) {
716 xSemaphoreGive(connection
->respSema
);
717 connection
->respObjId
= 0;
723 * Send an object through the telemetry link.
724 * \param[in] connection UAVTalkConnection to be used
725 * \param[in] type Transaction type
726 * \param[in] objId The object ID
727 * \param[in] instId The instance ID or UAVOBJ_ALL_INSTANCES for all instances
728 * \param[in] obj Object handle to send (null when type is NACK)
732 static int32_t sendObject(UAVTalkConnectionData
*connection
, uint8_t type
, uint32_t objId
, uint16_t instId
, UAVObjHandle obj
)
738 // Important note : obj can be null (when type is NACK for example) so protect all obj dereferences.
740 // If all instances are requested and this is a single instance object, force instance ID to zero
741 if ((obj
!= NULL
) && (instId
== UAVOBJ_ALL_INSTANCES
) && UAVObjIsSingleInstance(obj
)) {
745 // Process message type
746 if (type
== UAVTALK_TYPE_OBJ
|| type
== UAVTALK_TYPE_OBJ_TS
|| type
== UAVTALK_TYPE_OBJ_ACK
|| type
== UAVTALK_TYPE_OBJ_ACK_TS
) {
747 if (instId
== UAVOBJ_ALL_INSTANCES
) {
748 // Get number of instances
749 numInst
= UAVObjGetNumInstances(obj
);
750 // Send all instances in reverse order
751 // This allows the receiver to detect when the last object has been received (i.e. when instance 0 is received)
753 for (n
= 0; n
< numInst
; ++n
) {
754 ret
= sendSingleObject(connection
, type
, objId
, numInst
- n
- 1, obj
);
760 ret
= sendSingleObject(connection
, type
, objId
, instId
, obj
);
762 } else if (type
== UAVTALK_TYPE_OBJ_REQ
) {
763 ret
= sendSingleObject(connection
, type
, objId
, instId
, obj
);
764 } else if (type
== UAVTALK_TYPE_ACK
|| type
== UAVTALK_TYPE_NACK
) {
765 if (instId
!= UAVOBJ_ALL_INSTANCES
) {
766 ret
= sendSingleObject(connection
, type
, objId
, instId
, obj
);
774 * Send an object through the telemetry link.
775 * \param[in] connection UAVTalkConnection to be used
776 * \param[in] type Transaction type
777 * \param[in] objId The object ID
778 * \param[in] instId The instance ID (can NOT be UAVOBJ_ALL_INSTANCES, use () instead)
779 * \param[in] obj Object handle to send (null when type is NACK)
783 static int32_t sendSingleObject(UAVTalkConnectionData
*connection
, uint8_t type
, uint32_t objId
, uint16_t instId
, UAVObjHandle obj
)
785 // IMPORTANT : obj can be null (when type is NACK for example)
787 if (!connection
->outStream
) {
788 connection
->stats
.txErrors
++;
793 connection
->txBuffer
[0] = UAVTALK_SYNC_VAL
;
795 connection
->txBuffer
[1] = type
;
796 // next 2 bytes are reserved for data length (inserted here later)
798 connection
->txBuffer
[4] = (uint8_t)(objId
& 0xFF);
799 connection
->txBuffer
[5] = (uint8_t)((objId
>> 8) & 0xFF);
800 connection
->txBuffer
[6] = (uint8_t)((objId
>> 16) & 0xFF);
801 connection
->txBuffer
[7] = (uint8_t)((objId
>> 24) & 0xFF);
803 connection
->txBuffer
[8] = (uint8_t)(instId
& 0xFF);
804 connection
->txBuffer
[9] = (uint8_t)((instId
>> 8) & 0xFF);
805 int32_t headerLength
= 10;
807 // Add timestamp when the transaction type is appropriate
808 if (type
& UAVTALK_TIMESTAMPED
) {
809 portTickType time
= xTaskGetTickCount();
810 connection
->txBuffer
[10] = (uint8_t)(time
& 0xFF);
811 connection
->txBuffer
[11] = (uint8_t)((time
>> 8) & 0xFF);
815 // Determine data length
817 if (type
== UAVTALK_TYPE_OBJ_REQ
|| type
== UAVTALK_TYPE_ACK
|| type
== UAVTALK_TYPE_NACK
) {
820 length
= UAVObjGetNumBytes(obj
);
824 if (length
> UAVOBJECTS_LARGEST
) {
825 connection
->stats
.txErrors
++;
829 // Copy data (if any)
831 if (UAVObjPack(obj
, instId
, &connection
->txBuffer
[headerLength
]) == -1) {
832 connection
->stats
.txErrors
++;
837 // Store the packet length
838 connection
->txBuffer
[2] = (uint8_t)((headerLength
+ length
) & 0xFF);
839 connection
->txBuffer
[3] = (uint8_t)(((headerLength
+ length
) >> 8) & 0xFF);
841 // Calculate and store checksum
842 connection
->txBuffer
[headerLength
+ length
] = PIOS_CRC_updateCRC(0, connection
->txBuffer
, headerLength
+ length
);
845 uint16_t tx_msg_len
= headerLength
+ length
+ UAVTALK_CHECKSUM_LENGTH
;
846 int32_t rc
= (*connection
->outStream
)(connection
->txBuffer
, tx_msg_len
);
849 if (rc
== tx_msg_len
) {
850 ++connection
->stats
.txObjects
;
851 connection
->stats
.txObjectBytes
+= length
;
852 connection
->stats
.txBytes
+= tx_msg_len
;
854 connection
->stats
.txErrors
++;
855 // TODO rc == -1 connection not open, -2 buffer full should retry
856 connection
->stats
.txBytes
+= (rc
> 0) ? rc
: 0;
865 * Functions that implements the UAVTalk Process FSM. return false to break out of current cycle
868 static bool UAVTalkProcess_SYNC(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, __attribute__((unused
)) uint8_t length
, uint8_t *position
)
870 uint8_t rxbyte
= rxbuffer
[(*position
)++];
872 if (rxbyte
!= UAVTALK_SYNC_VAL
) {
873 connection
->stats
.rxSyncErrors
++;
877 // Initialize and update the CRC
878 iproc
->cs
= PIOS_CRC_updateByte(0, rxbyte
);
880 iproc
->rxPacketLength
= 1;
884 iproc
->state
= UAVTALK_STATE_TYPE
;
888 static bool UAVTalkProcess_TYPE(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, __attribute__((unused
)) uint8_t length
, uint8_t *position
)
890 uint8_t rxbyte
= rxbuffer
[(*position
)++];
892 if ((rxbyte
& UAVTALK_TYPE_MASK
) != UAVTALK_TYPE_VER
) {
893 connection
->stats
.rxErrors
++;
894 iproc
->state
= UAVTALK_STATE_SYNC
;
899 iproc
->cs
= PIOS_CRC_updateByte(iproc
->cs
, rxbyte
);
901 iproc
->type
= rxbyte
;
902 iproc
->rxPacketLength
++;
903 iproc
->packet_size
= 0;
904 iproc
->state
= UAVTALK_STATE_SIZE
;
908 static bool UAVTalkProcess_SIZE(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
)
910 while (iproc
->rxCount
< 2 && length
> (*position
)) {
911 uint8_t rxbyte
= rxbuffer
[(*position
)++];
913 iproc
->cs
= PIOS_CRC_updateByte(iproc
->cs
, rxbyte
);
914 iproc
->packet_size
+= rxbyte
<< 8 * iproc
->rxCount
;
918 if (iproc
->rxCount
< 2) {
924 if (iproc
->packet_size
< UAVTALK_MIN_HEADER_LENGTH
|| iproc
->packet_size
> UAVTALK_MAX_HEADER_LENGTH
+ UAVTALK_MAX_PAYLOAD_LENGTH
) {
925 // incorrect packet size
926 connection
->stats
.rxErrors
++;
927 iproc
->state
= UAVTALK_STATE_ERROR
;
930 iproc
->rxPacketLength
+= 2;
932 iproc
->state
= UAVTALK_STATE_OBJID
;
936 static bool UAVTalkProcess_OBJID(__attribute__((unused
)) UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
)
938 while (iproc
->rxCount
< 4 && length
> (*position
)) {
939 uint8_t rxbyte
= rxbuffer
[(*position
)++];
940 iproc
->cs
= PIOS_CRC_updateByte(iproc
->cs
, rxbyte
);
941 iproc
->objId
+= rxbyte
<< (8 * (iproc
->rxCount
++));
944 if (iproc
->rxCount
< 4) {
948 iproc
->rxPacketLength
+= 4;
950 iproc
->state
= UAVTALK_STATE_INSTID
;
954 static bool UAVTalkProcess_INSTID(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
)
956 while (iproc
->rxCount
< 2 && length
> (*position
)) {
957 uint8_t rxbyte
= rxbuffer
[(*position
)++];
958 iproc
->cs
= PIOS_CRC_updateByte(iproc
->cs
, rxbyte
);
959 iproc
->instId
+= rxbyte
<< (8 * (iproc
->rxCount
++));
962 if (iproc
->rxCount
< 2) {
965 iproc
->rxPacketLength
+= 2;
968 UAVObjHandle obj
= UAVObjGetByID(iproc
->objId
);
970 // Determine data length
971 if (iproc
->type
== UAVTALK_TYPE_OBJ_REQ
|| iproc
->type
== UAVTALK_TYPE_ACK
|| iproc
->type
== UAVTALK_TYPE_NACK
) {
973 iproc
->timestampLength
= 0;
975 iproc
->timestampLength
= (iproc
->type
& UAVTALK_TIMESTAMPED
) ? 2 : 0;
977 iproc
->length
= UAVObjGetNumBytes(obj
);
979 iproc
->length
= iproc
->packet_size
- iproc
->rxPacketLength
- iproc
->timestampLength
;
984 if (iproc
->length
>= UAVTALK_MAX_PAYLOAD_LENGTH
) {
985 // packet error - exceeded payload max length
986 connection
->stats
.rxErrors
++;
987 iproc
->state
= UAVTALK_STATE_ERROR
;
991 // Check the lengths match
992 if ((iproc
->rxPacketLength
+ iproc
->timestampLength
+ iproc
->length
) != iproc
->packet_size
) {
993 // packet error - mismatched packet size
994 connection
->stats
.rxErrors
++;
995 iproc
->state
= UAVTALK_STATE_ERROR
;
999 // Determine next state
1000 if (iproc
->type
& UAVTALK_TIMESTAMPED
) {
1001 // If there is a timestamp get it
1002 iproc
->timestamp
= 0;
1003 iproc
->state
= UAVTALK_STATE_TIMESTAMP
;
1005 // If there is a payload get it, otherwise receive checksum
1006 if (iproc
->length
> 0) {
1007 iproc
->state
= UAVTALK_STATE_DATA
;
1009 iproc
->state
= UAVTALK_STATE_CS
;
1015 static bool UAVTalkProcess_TIMESTAMP(__attribute__((unused
)) UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
)
1017 while (iproc
->rxCount
< 2 && length
> (*position
)) {
1018 uint8_t rxbyte
= rxbuffer
[(*position
)++];
1019 iproc
->cs
= PIOS_CRC_updateByte(iproc
->cs
, rxbyte
);
1020 iproc
->timestamp
+= rxbyte
<< (8 * (iproc
->rxCount
++));
1023 if (iproc
->rxCount
< 2) {
1028 iproc
->rxPacketLength
+= 2;
1029 // If there is a payload get it, otherwise receive checksum
1030 if (iproc
->length
> 0) {
1031 iproc
->state
= UAVTALK_STATE_DATA
;
1033 iproc
->state
= UAVTALK_STATE_CS
;
1038 static bool UAVTalkProcess_DATA(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
)
1040 uint8_t toCopy
= iproc
->length
- iproc
->rxCount
;
1042 if (toCopy
> length
- (*position
)) {
1043 toCopy
= length
- (*position
);
1046 memcpy(&connection
->rxBuffer
[iproc
->rxCount
], &rxbuffer
[(*position
)], toCopy
);
1047 (*position
) += toCopy
;
1050 iproc
->cs
= PIOS_CRC_updateCRC(iproc
->cs
, &connection
->rxBuffer
[iproc
->rxCount
], toCopy
);
1051 iproc
->rxCount
+= toCopy
;
1053 iproc
->rxPacketLength
+= toCopy
;
1055 if (iproc
->rxCount
< iproc
->length
) {
1060 iproc
->state
= UAVTALK_STATE_CS
;
1064 static bool UAVTalkProcess_CS(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, __attribute__((unused
)) uint8_t length
, uint8_t *position
)
1066 // Check the CRC byte
1067 uint8_t rxbyte
= rxbuffer
[(*position
)++];
1069 if (rxbyte
!= iproc
->cs
) {
1070 // packet error - faulty CRC
1071 UAVT_DEBUGLOG_PRINTF("BAD CRC");
1072 connection
->stats
.rxCrcErrors
++;
1073 connection
->stats
.rxErrors
++;
1074 iproc
->state
= UAVTALK_STATE_ERROR
;
1077 iproc
->rxPacketLength
++;
1079 if (iproc
->rxPacketLength
!= (iproc
->packet_size
+ UAVTALK_CHECKSUM_LENGTH
)) {
1080 // packet error - mismatched packet size
1081 connection
->stats
.rxErrors
++;
1082 iproc
->state
= UAVTALK_STATE_ERROR
;
1086 connection
->stats
.rxObjects
++;
1087 connection
->stats
.rxObjectBytes
+= iproc
->length
;
1089 iproc
->state
= UAVTALK_STATE_COMPLETE
;