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
, bool create
);
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
, true);
564 * Complete receiving a UAVTalk packet. This will cause the packet to be unpacked, acked, etc.
565 * This version will not create/unpack an object if it does not already exist.
566 * \param[in] connectionHandle UAVTalkConnection to be used
570 int32_t UAVTalkReceiveObjectNoCreate(UAVTalkConnection connectionHandle
)
572 UAVTalkConnectionData
*connection
;
574 CHECKCONHANDLE(connectionHandle
, connection
, return -1);
576 UAVTalkInputProcessor
*iproc
= &connection
->iproc
;
577 if (iproc
->state
!= UAVTALK_STATE_COMPLETE
) {
581 return receiveObject(connection
, iproc
->type
, iproc
->objId
, iproc
->instId
, connection
->rxBuffer
, false);
585 * Get the object ID of the current packet.
586 * \param[in] connectionHandle UAVTalkConnection to be used
587 * \return The object ID, or 0 on error.
589 uint32_t UAVTalkGetPacketObjId(UAVTalkConnection connectionHandle
)
591 UAVTalkConnectionData
*connection
;
593 CHECKCONHANDLE(connectionHandle
, connection
, return 0);
595 return connection
->iproc
.objId
;
599 * Receive an object. This function process objects received through the telemetry stream.
601 * Parser errors are considered as transmission errors and are not NACKed.
602 * Some senders (GCS) can timeout and retry if the message is not answered by an ack or nack.
604 * Object handling errors are considered as application errors and are NACked.
605 * In that case we want to nack as there is no point in the sender retrying to send invalid objects.
607 * \param[in] connection UAVTalkConnection to be used
608 * \param[in] type Type of received message (UAVTALK_TYPE_OBJ, UAVTALK_TYPE_OBJ_REQ, UAVTALK_TYPE_OBJ_ACK, UAVTALK_TYPE_ACK, UAVTALK_TYPE_NACK)
609 * \param[in] objId ID of the object to work on
610 * \param[in] instId The instance ID of UAVOBJ_ALL_INSTANCES for all instances.
611 * \param[in] data Data buffer
612 * \param[in] length Buffer length
616 static int32_t receiveObject(UAVTalkConnectionData
*connection
, uint8_t type
, uint32_t objId
, uint16_t instId
, uint8_t *data
, bool create
)
622 xSemaphoreTakeRecursive(connection
->lock
, portMAX_DELAY
);
624 // Get the handle to the object. Will be null if object does not exist.
626 // Here we ask for instance ID 0 without taking into account the provided instId
627 // The provided instId will be used later when packing, unpacking, etc...
628 // TODO the above should be fixed as it is cumbersome and error prone
629 obj
= UAVObjGetByID(objId
);
631 // Process message type
633 case UAVTALK_TYPE_OBJ
:
634 case UAVTALK_TYPE_OBJ_TS
:
635 // All instances not allowed for OBJ messages
636 if (obj
&& (instId
!= UAVOBJ_ALL_INSTANCES
)) {
637 // Unpack object, if create is true and the instance does not exist it will be created!
638 if (UAVObjUnpack(obj
, instId
, data
, create
) == 0) {
639 // Check if this object acks a pending OBJ_REQ message
640 // any OBJ message can ack a pending OBJ_REQ message
641 // even one that was not sent in response to the OBJ_REQ message
642 updateAck(connection
, type
, objId
, instId
);
651 case UAVTALK_TYPE_OBJ_ACK
:
652 case UAVTALK_TYPE_OBJ_ACK_TS
:
653 UAVT_DEBUGLOG_CPRINTF(objId
, "OBJ_ACK %X %d", objId
, instId
);
654 // All instances not allowed for OBJ_ACK messages
655 if (obj
&& (instId
!= UAVOBJ_ALL_INSTANCES
)) {
656 // Unpack object, if create is true and the instance does not exist it will be created!
657 if (UAVObjUnpack(obj
, instId
, data
, create
) == 0) {
658 UAVT_DEBUGLOG_CPRINTF(objId
, "OBJ ACK %X %d", objId
, instId
);
659 // Object updated or created, transmit ACK
660 sendObject(connection
, UAVTALK_TYPE_ACK
, objId
, instId
, NULL
);
668 // failed to update object, transmit NACK
669 UAVT_DEBUGLOG_PRINTF("OBJ NACK %X %d", objId
, instId
);
670 sendObject(connection
, UAVTALK_TYPE_NACK
, objId
, instId
, NULL
);
674 case UAVTALK_TYPE_OBJ_REQ
:
675 // Check if requested object exists
676 UAVT_DEBUGLOG_CPRINTF(objId
, "REQ %X %d", objId
, instId
);
678 // Object found, transmit it
679 // The sent object will ack the object request on the receiver side
680 ret
= sendObject(connection
, UAVTALK_TYPE_OBJ
, objId
, instId
, obj
);
685 // failed to send object, transmit NACK
686 UAVT_DEBUGLOG_PRINTF("REQ NACK %X %d", objId
, instId
);
687 sendObject(connection
, UAVTALK_TYPE_NACK
, objId
, instId
, NULL
);
691 case UAVTALK_TYPE_NACK
:
692 // Do nothing on flight side, let it time out.
694 // The transaction takes the result code of the "semaphore taking operation" into account to determine success.
695 // If we give that semaphore in time, its "success" (ack received)
696 // If we do not give that semaphore before the timeout it will return failure.
697 // What would have to be done here is give the semaphore, but set a flag (for example connection->respFail=true)
698 // that indicates failure and then above where it checks for the result code, have it behave as if it failed
699 // if the explicit failure is set.
702 case UAVTALK_TYPE_ACK
:
703 // All instances not allowed for ACK messages
704 if (obj
&& (instId
!= UAVOBJ_ALL_INSTANCES
)) {
705 // Check if an ACK is pending
706 updateAck(connection
, type
, objId
, instId
);
717 xSemaphoreGiveRecursive(connection
->lock
);
724 * Check if an ack is pending on an object and give response semaphore
725 * \param[in] connection UAVTalkConnection to be used
726 * \param[in] obj Object
727 * \param[in] instId The instance ID of UAVOBJ_ALL_INSTANCES for all instances.
729 static void updateAck(UAVTalkConnectionData
*connection
, uint8_t type
, uint32_t objId
, uint16_t instId
)
731 if ((connection
->respObjId
== objId
) && (connection
->respType
== type
)) {
732 if ((connection
->respInstId
== UAVOBJ_ALL_INSTANCES
) && (instId
== 0)) {
733 // last instance received, complete transaction
734 xSemaphoreGive(connection
->respSema
);
735 connection
->respObjId
= 0;
736 } else if (connection
->respInstId
== instId
) {
737 xSemaphoreGive(connection
->respSema
);
738 connection
->respObjId
= 0;
744 * Send an object through the telemetry link.
745 * \param[in] connection UAVTalkConnection to be used
746 * \param[in] type Transaction type
747 * \param[in] objId The object ID
748 * \param[in] instId The instance ID or UAVOBJ_ALL_INSTANCES for all instances
749 * \param[in] obj Object handle to send (null when type is NACK)
753 static int32_t sendObject(UAVTalkConnectionData
*connection
, uint8_t type
, uint32_t objId
, uint16_t instId
, UAVObjHandle obj
)
759 // Important note : obj can be null (when type is NACK for example) so protect all obj dereferences.
761 // If all instances are requested and this is a single instance object, force instance ID to zero
762 if ((obj
!= NULL
) && (instId
== UAVOBJ_ALL_INSTANCES
) && UAVObjIsSingleInstance(obj
)) {
766 // Process message type
767 if (type
== UAVTALK_TYPE_OBJ
|| type
== UAVTALK_TYPE_OBJ_TS
|| type
== UAVTALK_TYPE_OBJ_ACK
|| type
== UAVTALK_TYPE_OBJ_ACK_TS
) {
768 if (instId
== UAVOBJ_ALL_INSTANCES
) {
769 // Get number of instances
770 numInst
= UAVObjGetNumInstances(obj
);
771 // Send all instances in reverse order
772 // This allows the receiver to detect when the last object has been received (i.e. when instance 0 is received)
774 for (n
= 0; n
< numInst
; ++n
) {
775 ret
= sendSingleObject(connection
, type
, objId
, numInst
- n
- 1, obj
);
781 ret
= sendSingleObject(connection
, type
, objId
, instId
, obj
);
783 } else if (type
== UAVTALK_TYPE_OBJ_REQ
) {
784 ret
= sendSingleObject(connection
, type
, objId
, instId
, obj
);
785 } else if (type
== UAVTALK_TYPE_ACK
|| type
== UAVTALK_TYPE_NACK
) {
786 if (instId
!= UAVOBJ_ALL_INSTANCES
) {
787 ret
= sendSingleObject(connection
, type
, objId
, instId
, obj
);
795 * Send an object through the telemetry link.
796 * \param[in] connection UAVTalkConnection to be used
797 * \param[in] type Transaction type
798 * \param[in] objId The object ID
799 * \param[in] instId The instance ID (can NOT be UAVOBJ_ALL_INSTANCES, use () instead)
800 * \param[in] obj Object handle to send (null when type is NACK)
804 static int32_t sendSingleObject(UAVTalkConnectionData
*connection
, uint8_t type
, uint32_t objId
, uint16_t instId
, UAVObjHandle obj
)
806 // IMPORTANT : obj can be null (when type is NACK for example)
808 if (!connection
->outStream
) {
809 connection
->stats
.txErrors
++;
814 connection
->txBuffer
[0] = UAVTALK_SYNC_VAL
;
816 connection
->txBuffer
[1] = type
;
817 // next 2 bytes are reserved for data length (inserted here later)
819 connection
->txBuffer
[4] = (uint8_t)(objId
& 0xFF);
820 connection
->txBuffer
[5] = (uint8_t)((objId
>> 8) & 0xFF);
821 connection
->txBuffer
[6] = (uint8_t)((objId
>> 16) & 0xFF);
822 connection
->txBuffer
[7] = (uint8_t)((objId
>> 24) & 0xFF);
824 connection
->txBuffer
[8] = (uint8_t)(instId
& 0xFF);
825 connection
->txBuffer
[9] = (uint8_t)((instId
>> 8) & 0xFF);
826 int32_t headerLength
= 10;
828 // Add timestamp when the transaction type is appropriate
829 if (type
& UAVTALK_TIMESTAMPED
) {
830 portTickType time
= xTaskGetTickCount();
831 connection
->txBuffer
[10] = (uint8_t)(time
& 0xFF);
832 connection
->txBuffer
[11] = (uint8_t)((time
>> 8) & 0xFF);
836 // Determine data length
838 if (type
== UAVTALK_TYPE_OBJ_REQ
|| type
== UAVTALK_TYPE_ACK
|| type
== UAVTALK_TYPE_NACK
) {
841 length
= UAVObjGetNumBytes(obj
);
845 if (length
> UAVOBJECTS_LARGEST
) {
846 connection
->stats
.txErrors
++;
850 // Copy data (if any)
852 if (UAVObjPack(obj
, instId
, &connection
->txBuffer
[headerLength
]) == -1) {
853 connection
->stats
.txErrors
++;
858 // Store the packet length
859 connection
->txBuffer
[2] = (uint8_t)((headerLength
+ length
) & 0xFF);
860 connection
->txBuffer
[3] = (uint8_t)(((headerLength
+ length
) >> 8) & 0xFF);
862 // Calculate and store checksum
863 connection
->txBuffer
[headerLength
+ length
] = PIOS_CRC_updateCRC(0, connection
->txBuffer
, headerLength
+ length
);
866 uint16_t tx_msg_len
= headerLength
+ length
+ UAVTALK_CHECKSUM_LENGTH
;
867 int32_t rc
= (*connection
->outStream
)(connection
->txBuffer
, tx_msg_len
);
870 if (rc
== tx_msg_len
) {
871 ++connection
->stats
.txObjects
;
872 connection
->stats
.txObjectBytes
+= length
;
873 connection
->stats
.txBytes
+= tx_msg_len
;
875 connection
->stats
.txErrors
++;
876 // TODO rc == -1 connection not open, -2 buffer full should retry
877 connection
->stats
.txBytes
+= (rc
> 0) ? rc
: 0;
886 * Functions that implements the UAVTalk Process FSM. return false to break out of current cycle
889 static bool UAVTalkProcess_SYNC(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, __attribute__((unused
)) uint8_t length
, uint8_t *position
)
891 uint8_t rxbyte
= rxbuffer
[(*position
)++];
893 if (rxbyte
!= UAVTALK_SYNC_VAL
) {
894 connection
->stats
.rxSyncErrors
++;
898 // Initialize and update the CRC
899 iproc
->cs
= PIOS_CRC_updateByte(0, rxbyte
);
901 iproc
->rxPacketLength
= 1;
905 iproc
->state
= UAVTALK_STATE_TYPE
;
909 static bool UAVTalkProcess_TYPE(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, __attribute__((unused
)) uint8_t length
, uint8_t *position
)
911 uint8_t rxbyte
= rxbuffer
[(*position
)++];
913 if ((rxbyte
& UAVTALK_TYPE_MASK
) != UAVTALK_TYPE_VER
) {
914 connection
->stats
.rxErrors
++;
915 iproc
->state
= UAVTALK_STATE_SYNC
;
920 iproc
->cs
= PIOS_CRC_updateByte(iproc
->cs
, rxbyte
);
922 iproc
->type
= rxbyte
;
923 iproc
->rxPacketLength
++;
924 iproc
->packet_size
= 0;
925 iproc
->state
= UAVTALK_STATE_SIZE
;
929 static bool UAVTalkProcess_SIZE(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
)
931 while (iproc
->rxCount
< 2 && length
> (*position
)) {
932 uint8_t rxbyte
= rxbuffer
[(*position
)++];
934 iproc
->cs
= PIOS_CRC_updateByte(iproc
->cs
, rxbyte
);
935 iproc
->packet_size
+= rxbyte
<< 8 * iproc
->rxCount
;
939 if (iproc
->rxCount
< 2) {
945 if (iproc
->packet_size
< UAVTALK_MIN_HEADER_LENGTH
|| iproc
->packet_size
> UAVTALK_MAX_HEADER_LENGTH
+ UAVTALK_MAX_PAYLOAD_LENGTH
) {
946 // incorrect packet size
947 connection
->stats
.rxErrors
++;
948 iproc
->state
= UAVTALK_STATE_ERROR
;
951 iproc
->rxPacketLength
+= 2;
953 iproc
->state
= UAVTALK_STATE_OBJID
;
957 static bool UAVTalkProcess_OBJID(__attribute__((unused
)) UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
)
959 while (iproc
->rxCount
< 4 && length
> (*position
)) {
960 uint8_t rxbyte
= rxbuffer
[(*position
)++];
961 iproc
->cs
= PIOS_CRC_updateByte(iproc
->cs
, rxbyte
);
962 iproc
->objId
+= rxbyte
<< (8 * (iproc
->rxCount
++));
965 if (iproc
->rxCount
< 4) {
969 iproc
->rxPacketLength
+= 4;
971 iproc
->state
= UAVTALK_STATE_INSTID
;
975 static bool UAVTalkProcess_INSTID(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
)
977 while (iproc
->rxCount
< 2 && length
> (*position
)) {
978 uint8_t rxbyte
= rxbuffer
[(*position
)++];
979 iproc
->cs
= PIOS_CRC_updateByte(iproc
->cs
, rxbyte
);
980 iproc
->instId
+= rxbyte
<< (8 * (iproc
->rxCount
++));
983 if (iproc
->rxCount
< 2) {
986 iproc
->rxPacketLength
+= 2;
989 UAVObjHandle obj
= UAVObjGetByID(iproc
->objId
);
991 // Determine data length
992 if (iproc
->type
== UAVTALK_TYPE_OBJ_REQ
|| iproc
->type
== UAVTALK_TYPE_ACK
|| iproc
->type
== UAVTALK_TYPE_NACK
) {
994 iproc
->timestampLength
= 0;
996 iproc
->timestampLength
= (iproc
->type
& UAVTALK_TIMESTAMPED
) ? 2 : 0;
998 iproc
->length
= UAVObjGetNumBytes(obj
);
1000 iproc
->length
= iproc
->packet_size
- iproc
->rxPacketLength
- iproc
->timestampLength
;
1005 if (iproc
->length
>= UAVTALK_MAX_PAYLOAD_LENGTH
) {
1006 // packet error - exceeded payload max length
1007 connection
->stats
.rxErrors
++;
1008 iproc
->state
= UAVTALK_STATE_ERROR
;
1012 // Check the lengths match
1013 if ((iproc
->rxPacketLength
+ iproc
->timestampLength
+ iproc
->length
) != iproc
->packet_size
) {
1014 // packet error - mismatched packet size
1015 connection
->stats
.rxErrors
++;
1016 iproc
->state
= UAVTALK_STATE_ERROR
;
1020 // Determine next state
1021 if (iproc
->type
& UAVTALK_TIMESTAMPED
) {
1022 // If there is a timestamp get it
1023 iproc
->timestamp
= 0;
1024 iproc
->state
= UAVTALK_STATE_TIMESTAMP
;
1026 // If there is a payload get it, otherwise receive checksum
1027 if (iproc
->length
> 0) {
1028 iproc
->state
= UAVTALK_STATE_DATA
;
1030 iproc
->state
= UAVTALK_STATE_CS
;
1036 static bool UAVTalkProcess_TIMESTAMP(__attribute__((unused
)) UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
)
1038 while (iproc
->rxCount
< 2 && length
> (*position
)) {
1039 uint8_t rxbyte
= rxbuffer
[(*position
)++];
1040 iproc
->cs
= PIOS_CRC_updateByte(iproc
->cs
, rxbyte
);
1041 iproc
->timestamp
+= rxbyte
<< (8 * (iproc
->rxCount
++));
1044 if (iproc
->rxCount
< 2) {
1049 iproc
->rxPacketLength
+= 2;
1050 // If there is a payload get it, otherwise receive checksum
1051 if (iproc
->length
> 0) {
1052 iproc
->state
= UAVTALK_STATE_DATA
;
1054 iproc
->state
= UAVTALK_STATE_CS
;
1059 static bool UAVTalkProcess_DATA(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
)
1061 uint8_t toCopy
= iproc
->length
- iproc
->rxCount
;
1063 if (toCopy
> length
- (*position
)) {
1064 toCopy
= length
- (*position
);
1067 memcpy(&connection
->rxBuffer
[iproc
->rxCount
], &rxbuffer
[(*position
)], toCopy
);
1068 (*position
) += toCopy
;
1071 iproc
->cs
= PIOS_CRC_updateCRC(iproc
->cs
, &connection
->rxBuffer
[iproc
->rxCount
], toCopy
);
1072 iproc
->rxCount
+= toCopy
;
1074 iproc
->rxPacketLength
+= toCopy
;
1076 if (iproc
->rxCount
< iproc
->length
) {
1081 iproc
->state
= UAVTALK_STATE_CS
;
1085 static bool UAVTalkProcess_CS(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, __attribute__((unused
)) uint8_t length
, uint8_t *position
)
1087 // Check the CRC byte
1088 uint8_t rxbyte
= rxbuffer
[(*position
)++];
1090 if (rxbyte
!= iproc
->cs
) {
1091 // packet error - faulty CRC
1092 UAVT_DEBUGLOG_PRINTF("BAD CRC");
1093 connection
->stats
.rxCrcErrors
++;
1094 connection
->stats
.rxErrors
++;
1095 iproc
->state
= UAVTALK_STATE_ERROR
;
1098 iproc
->rxPacketLength
++;
1100 if (iproc
->rxPacketLength
!= (iproc
->packet_size
+ UAVTALK_CHECKSUM_LENGTH
)) {
1101 // packet error - mismatched packet size
1102 connection
->stats
.rxErrors
++;
1103 iproc
->state
= UAVTALK_STATE_ERROR
;
1107 connection
->stats
.rxObjects
++;
1108 connection
->stats
.rxObjectBytes
+= iproc
->length
;
1110 iproc
->state
= UAVTALK_STATE_COMPLETE
;