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
);
381 // stop processing as soon as a complete packet is received, error is encountered or buffer is processed entirely
382 while ((count
= length
- (*position
)) > 0
383 && iproc
->state
!= UAVTALK_STATE_COMPLETE
384 && iproc
->state
!= UAVTALK_STATE_ERROR
) {
385 // Receive state machine
386 if (iproc
->state
== UAVTALK_STATE_SYNC
&&
387 !UAVTalkProcess_SYNC(connection
, iproc
, rxbuffer
, length
, position
)) {
391 if (iproc
->state
== UAVTALK_STATE_TYPE
&&
392 !UAVTalkProcess_TYPE(connection
, iproc
, rxbuffer
, length
, position
)) {
396 if (iproc
->state
== UAVTALK_STATE_SIZE
&&
397 !UAVTalkProcess_SIZE(connection
, iproc
, rxbuffer
, length
, position
)) {
401 if (iproc
->state
== UAVTALK_STATE_OBJID
&&
402 !UAVTalkProcess_OBJID(connection
, iproc
, rxbuffer
, length
, position
)) {
406 if (iproc
->state
== UAVTALK_STATE_INSTID
&&
407 !UAVTalkProcess_INSTID(connection
, iproc
, rxbuffer
, length
, position
)) {
411 if (iproc
->state
== UAVTALK_STATE_TIMESTAMP
&&
412 !UAVTalkProcess_TIMESTAMP(connection
, iproc
, rxbuffer
, length
, position
)) {
416 if (iproc
->state
== UAVTALK_STATE_DATA
&&
417 !UAVTalkProcess_DATA(connection
, iproc
, rxbuffer
, length
, position
)) {
421 if (iproc
->state
== UAVTALK_STATE_CS
&&
422 !UAVTalkProcess_CS(connection
, iproc
, rxbuffer
, length
, position
)) {
428 processedBytes
= (*position
) - processedBytes
;
429 connection
->stats
.rxBytes
+= processedBytes
;
434 * Process a buffer from the telemetry stream.
435 * \param[in] connection UAVTalkConnection to be used
436 * \param[in] rxbuffer Received buffer
437 * \param[in] count bytes inside rxbuffer
438 * \return UAVTalkRxState
440 UAVTalkRxState
UAVTalkProcessInputStream(UAVTalkConnection connectionHandle
, uint8_t *rxbuffer
, uint8_t length
)
442 uint8_t position
= 0;
443 UAVTalkRxState state
= UAVTALK_STATE_ERROR
;
445 while (position
< length
) {
446 state
= UAVTalkProcessInputStreamQuiet(connectionHandle
, rxbuffer
, length
, &position
);
447 if (state
== UAVTALK_STATE_COMPLETE
) {
448 UAVTalkReceiveObject(connectionHandle
);
455 * Send a parsed packet received on one connection handle out on a different connection handle.
456 * The packet must be in a complete state, meaning it is completed parsing.
457 * The packet is re-assembled from the component parts into a complete message and sent.
458 * This can be used to relay packets from one UAVTalk connection to another.
459 * \param[in] connection UAVTalkConnection to be used
460 * \param[in] rxbyte Received byte
464 int32_t UAVTalkRelayPacket(UAVTalkConnection inConnectionHandle
, UAVTalkConnection outConnectionHandle
)
466 UAVTalkConnectionData
*inConnection
;
468 CHECKCONHANDLE(inConnectionHandle
, inConnection
, return -1);
469 UAVTalkInputProcessor
*inIproc
= &inConnection
->iproc
;
471 // The input packet must be completely parsed.
472 if (inIproc
->state
!= UAVTALK_STATE_COMPLETE
) {
473 inConnection
->stats
.rxErrors
++;
478 UAVTalkConnectionData
*outConnection
;
479 CHECKCONHANDLE(outConnectionHandle
, outConnection
, return -1);
481 if (!outConnection
->outStream
) {
482 outConnection
->stats
.txErrors
++;
488 xSemaphoreTakeRecursive(outConnection
->lock
, portMAX_DELAY
);
490 outConnection
->txBuffer
[0] = UAVTALK_SYNC_VAL
;
492 outConnection
->txBuffer
[1] = inIproc
->type
;
493 // next 2 bytes are reserved for data length (inserted here later)
495 outConnection
->txBuffer
[4] = (uint8_t)(inIproc
->objId
& 0xFF);
496 outConnection
->txBuffer
[5] = (uint8_t)((inIproc
->objId
>> 8) & 0xFF);
497 outConnection
->txBuffer
[6] = (uint8_t)((inIproc
->objId
>> 16) & 0xFF);
498 outConnection
->txBuffer
[7] = (uint8_t)((inIproc
->objId
>> 24) & 0xFF);
500 outConnection
->txBuffer
[8] = (uint8_t)(inIproc
->instId
& 0xFF);
501 outConnection
->txBuffer
[9] = (uint8_t)((inIproc
->instId
>> 8) & 0xFF);
502 int32_t headerLength
= 10;
504 // Add timestamp when the transaction type is appropriate
505 if (inIproc
->type
& UAVTALK_TIMESTAMPED
) {
506 portTickType time
= xTaskGetTickCount();
507 outConnection
->txBuffer
[10] = (uint8_t)(time
& 0xFF);
508 outConnection
->txBuffer
[11] = (uint8_t)((time
>> 8) & 0xFF);
512 // Copy data (if any)
513 if (inIproc
->length
> 0) {
514 memcpy(&outConnection
->txBuffer
[headerLength
], inConnection
->rxBuffer
, inIproc
->length
);
517 // Store the packet length
518 outConnection
->txBuffer
[2] = (uint8_t)((headerLength
+ inIproc
->length
) & 0xFF);
519 outConnection
->txBuffer
[3] = (uint8_t)(((headerLength
+ inIproc
->length
) >> 8) & 0xFF);
522 outConnection
->txBuffer
[headerLength
+ inIproc
->length
] = inIproc
->cs
;
525 int32_t rc
= (*outConnection
->outStream
)(outConnection
->txBuffer
, headerLength
+ inIproc
->length
+ UAVTALK_CHECKSUM_LENGTH
);
528 outConnection
->stats
.txBytes
+= (rc
> 0) ? rc
: 0;
530 // evaluate return value before releasing the lock
532 if (rc
!= (int32_t)(headerLength
+ inIproc
->length
+ UAVTALK_CHECKSUM_LENGTH
)) {
533 outConnection
->stats
.txErrors
++;
538 xSemaphoreGiveRecursive(outConnection
->lock
);
545 * Complete receiving a UAVTalk packet. This will cause the packet to be unpacked, acked, etc.
546 * \param[in] connectionHandle UAVTalkConnection to be used
550 int32_t UAVTalkReceiveObject(UAVTalkConnection connectionHandle
)
552 UAVTalkConnectionData
*connection
;
554 CHECKCONHANDLE(connectionHandle
, connection
, return -1);
556 UAVTalkInputProcessor
*iproc
= &connection
->iproc
;
557 if (iproc
->state
!= UAVTALK_STATE_COMPLETE
) {
561 return receiveObject(connection
, iproc
->type
, iproc
->objId
, iproc
->instId
, connection
->rxBuffer
);
565 * Get the object ID of the current packet.
566 * \param[in] connectionHandle UAVTalkConnection to be used
567 * \return The object ID, or 0 on error.
569 uint32_t UAVTalkGetPacketObjId(UAVTalkConnection connectionHandle
)
571 UAVTalkConnectionData
*connection
;
573 CHECKCONHANDLE(connectionHandle
, connection
, return 0);
575 return connection
->iproc
.objId
;
579 * Receive an object. This function process objects received through the telemetry stream.
581 * Parser errors are considered as transmission errors and are not NACKed.
582 * Some senders (GCS) can timeout and retry if the message is not answered by an ack or nack.
584 * Object handling errors are considered as application errors and are NACked.
585 * In that case we want to nack as there is no point in the sender retrying to send invalid objects.
587 * \param[in] connection UAVTalkConnection to be used
588 * \param[in] type Type of received message (UAVTALK_TYPE_OBJ, UAVTALK_TYPE_OBJ_REQ, UAVTALK_TYPE_OBJ_ACK, UAVTALK_TYPE_ACK, UAVTALK_TYPE_NACK)
589 * \param[in] objId ID of the object to work on
590 * \param[in] instId The instance ID of UAVOBJ_ALL_INSTANCES for all instances.
591 * \param[in] data Data buffer
592 * \param[in] length Buffer length
596 static int32_t receiveObject(UAVTalkConnectionData
*connection
, uint8_t type
, uint32_t objId
, uint16_t instId
, uint8_t *data
)
602 xSemaphoreTakeRecursive(connection
->lock
, portMAX_DELAY
);
604 // Get the handle to the object. Will be null if object does not exist.
606 // Here we ask for instance ID 0 without taking into account the provided instId
607 // The provided instId will be used later when packing, unpacking, etc...
608 // TODO the above should be fixed as it is cumbersome and error prone
609 obj
= UAVObjGetByID(objId
);
611 // Process message type
613 case UAVTALK_TYPE_OBJ
:
614 case UAVTALK_TYPE_OBJ_TS
:
615 // All instances not allowed for OBJ messages
616 if (obj
&& (instId
!= UAVOBJ_ALL_INSTANCES
)) {
617 // Unpack object, if the instance does not exist it will be created!
618 if (UAVObjUnpack(obj
, instId
, data
) == 0) {
619 // Check if this object acks a pending OBJ_REQ message
620 // any OBJ message can ack a pending OBJ_REQ message
621 // even one that was not sent in response to the OBJ_REQ message
622 updateAck(connection
, type
, objId
, instId
);
631 case UAVTALK_TYPE_OBJ_ACK
:
632 case UAVTALK_TYPE_OBJ_ACK_TS
:
633 UAVT_DEBUGLOG_CPRINTF(objId
, "OBJ_ACK %X %d", objId
, instId
);
634 // All instances not allowed for OBJ_ACK messages
635 if (obj
&& (instId
!= UAVOBJ_ALL_INSTANCES
)) {
636 // Unpack object, if the instance does not exist it will be created!
637 if (UAVObjUnpack(obj
, instId
, data
) == 0) {
638 UAVT_DEBUGLOG_CPRINTF(objId
, "OBJ ACK %X %d", objId
, instId
);
639 // Object updated or created, transmit ACK
640 sendObject(connection
, UAVTALK_TYPE_ACK
, objId
, instId
, NULL
);
648 // failed to update object, transmit NACK
649 UAVT_DEBUGLOG_PRINTF("OBJ NACK %X %d", objId
, instId
);
650 sendObject(connection
, UAVTALK_TYPE_NACK
, objId
, instId
, NULL
);
654 case UAVTALK_TYPE_OBJ_REQ
:
655 // Check if requested object exists
656 UAVT_DEBUGLOG_CPRINTF(objId
, "REQ %X %d", objId
, instId
);
658 // Object found, transmit it
659 // The sent object will ack the object request on the receiver side
660 ret
= sendObject(connection
, UAVTALK_TYPE_OBJ
, objId
, instId
, obj
);
665 // failed to send object, transmit NACK
666 UAVT_DEBUGLOG_PRINTF("REQ NACK %X %d", objId
, instId
);
667 sendObject(connection
, UAVTALK_TYPE_NACK
, objId
, instId
, NULL
);
671 case UAVTALK_TYPE_NACK
:
672 // Do nothing on flight side, let it time out.
674 // The transaction takes the result code of the "semaphore taking operation" into account to determine success.
675 // If we give that semaphore in time, its "success" (ack received)
676 // If we do not give that semaphore before the timeout it will return failure.
677 // What would have to be done here is give the semaphore, but set a flag (for example connection->respFail=true)
678 // that indicates failure and then above where it checks for the result code, have it behave as if it failed
679 // if the explicit failure is set.
682 case UAVTALK_TYPE_ACK
:
683 // All instances not allowed for ACK messages
684 if (obj
&& (instId
!= UAVOBJ_ALL_INSTANCES
)) {
685 // Check if an ACK is pending
686 updateAck(connection
, type
, objId
, instId
);
697 xSemaphoreGiveRecursive(connection
->lock
);
704 * Check if an ack is pending on an object and give response semaphore
705 * \param[in] connection UAVTalkConnection to be used
706 * \param[in] obj Object
707 * \param[in] instId The instance ID of UAVOBJ_ALL_INSTANCES for all instances.
709 static void updateAck(UAVTalkConnectionData
*connection
, uint8_t type
, uint32_t objId
, uint16_t instId
)
711 if ((connection
->respObjId
== objId
) && (connection
->respType
== type
)) {
712 if ((connection
->respInstId
== UAVOBJ_ALL_INSTANCES
) && (instId
== 0)) {
713 // last instance received, complete transaction
714 xSemaphoreGive(connection
->respSema
);
715 connection
->respObjId
= 0;
716 } else if (connection
->respInstId
== instId
) {
717 xSemaphoreGive(connection
->respSema
);
718 connection
->respObjId
= 0;
724 * Send an object through the telemetry link.
725 * \param[in] connection UAVTalkConnection to be used
726 * \param[in] type Transaction type
727 * \param[in] objId The object ID
728 * \param[in] instId The instance ID or UAVOBJ_ALL_INSTANCES for all instances
729 * \param[in] obj Object handle to send (null when type is NACK)
733 static int32_t sendObject(UAVTalkConnectionData
*connection
, uint8_t type
, uint32_t objId
, uint16_t instId
, UAVObjHandle obj
)
739 // Important note : obj can be null (when type is NACK for example) so protect all obj dereferences.
741 // If all instances are requested and this is a single instance object, force instance ID to zero
742 if ((obj
!= NULL
) && (instId
== UAVOBJ_ALL_INSTANCES
) && UAVObjIsSingleInstance(obj
)) {
746 // Process message type
747 if (type
== UAVTALK_TYPE_OBJ
|| type
== UAVTALK_TYPE_OBJ_TS
|| type
== UAVTALK_TYPE_OBJ_ACK
|| type
== UAVTALK_TYPE_OBJ_ACK_TS
) {
748 if (instId
== UAVOBJ_ALL_INSTANCES
) {
749 // Get number of instances
750 numInst
= UAVObjGetNumInstances(obj
);
751 // Send all instances in reverse order
752 // This allows the receiver to detect when the last object has been received (i.e. when instance 0 is received)
754 for (n
= 0; n
< numInst
; ++n
) {
755 ret
= sendSingleObject(connection
, type
, objId
, numInst
- n
- 1, obj
);
761 ret
= sendSingleObject(connection
, type
, objId
, instId
, obj
);
763 } else if (type
== UAVTALK_TYPE_OBJ_REQ
) {
764 ret
= sendSingleObject(connection
, type
, objId
, instId
, obj
);
765 } else if (type
== UAVTALK_TYPE_ACK
|| type
== UAVTALK_TYPE_NACK
) {
766 if (instId
!= UAVOBJ_ALL_INSTANCES
) {
767 ret
= sendSingleObject(connection
, type
, objId
, instId
, obj
);
775 * Send an object through the telemetry link.
776 * \param[in] connection UAVTalkConnection to be used
777 * \param[in] type Transaction type
778 * \param[in] objId The object ID
779 * \param[in] instId The instance ID (can NOT be UAVOBJ_ALL_INSTANCES, use () instead)
780 * \param[in] obj Object handle to send (null when type is NACK)
784 static int32_t sendSingleObject(UAVTalkConnectionData
*connection
, uint8_t type
, uint32_t objId
, uint16_t instId
, UAVObjHandle obj
)
786 // IMPORTANT : obj can be null (when type is NACK for example)
788 if (!connection
->outStream
) {
789 connection
->stats
.txErrors
++;
794 connection
->txBuffer
[0] = UAVTALK_SYNC_VAL
;
796 connection
->txBuffer
[1] = type
;
797 // next 2 bytes are reserved for data length (inserted here later)
799 connection
->txBuffer
[4] = (uint8_t)(objId
& 0xFF);
800 connection
->txBuffer
[5] = (uint8_t)((objId
>> 8) & 0xFF);
801 connection
->txBuffer
[6] = (uint8_t)((objId
>> 16) & 0xFF);
802 connection
->txBuffer
[7] = (uint8_t)((objId
>> 24) & 0xFF);
804 connection
->txBuffer
[8] = (uint8_t)(instId
& 0xFF);
805 connection
->txBuffer
[9] = (uint8_t)((instId
>> 8) & 0xFF);
806 int32_t headerLength
= 10;
808 // Add timestamp when the transaction type is appropriate
809 if (type
& UAVTALK_TIMESTAMPED
) {
810 portTickType time
= xTaskGetTickCount();
811 connection
->txBuffer
[10] = (uint8_t)(time
& 0xFF);
812 connection
->txBuffer
[11] = (uint8_t)((time
>> 8) & 0xFF);
816 // Determine data length
818 if (type
== UAVTALK_TYPE_OBJ_REQ
|| type
== UAVTALK_TYPE_ACK
|| type
== UAVTALK_TYPE_NACK
) {
821 length
= UAVObjGetNumBytes(obj
);
825 if (length
> UAVOBJECTS_LARGEST
) {
826 connection
->stats
.txErrors
++;
830 // Copy data (if any)
832 if (UAVObjPack(obj
, instId
, &connection
->txBuffer
[headerLength
]) == -1) {
833 connection
->stats
.txErrors
++;
838 // Store the packet length
839 connection
->txBuffer
[2] = (uint8_t)((headerLength
+ length
) & 0xFF);
840 connection
->txBuffer
[3] = (uint8_t)(((headerLength
+ length
) >> 8) & 0xFF);
842 // Calculate and store checksum
843 connection
->txBuffer
[headerLength
+ length
] = PIOS_CRC_updateCRC(0, connection
->txBuffer
, headerLength
+ length
);
846 uint16_t tx_msg_len
= headerLength
+ length
+ UAVTALK_CHECKSUM_LENGTH
;
847 int32_t rc
= (*connection
->outStream
)(connection
->txBuffer
, tx_msg_len
);
850 if (rc
== tx_msg_len
) {
851 ++connection
->stats
.txObjects
;
852 connection
->stats
.txObjectBytes
+= length
;
853 connection
->stats
.txBytes
+= tx_msg_len
;
855 connection
->stats
.txErrors
++;
856 // TODO rc == -1 connection not open, -2 buffer full should retry
857 connection
->stats
.txBytes
+= (rc
> 0) ? rc
: 0;
866 * Functions that implements the UAVTalk Process FSM. return false to break out of current cycle
869 static bool UAVTalkProcess_SYNC(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, __attribute__((unused
)) uint8_t length
, uint8_t *position
)
871 uint8_t rxbyte
= rxbuffer
[(*position
)++];
873 if (rxbyte
!= UAVTALK_SYNC_VAL
) {
874 connection
->stats
.rxSyncErrors
++;
878 // Initialize and update the CRC
879 iproc
->cs
= PIOS_CRC_updateByte(0, rxbyte
);
881 iproc
->rxPacketLength
= 1;
885 iproc
->state
= UAVTALK_STATE_TYPE
;
889 static bool UAVTalkProcess_TYPE(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_TYPE_MASK
) != UAVTALK_TYPE_VER
) {
894 connection
->stats
.rxErrors
++;
895 iproc
->state
= UAVTALK_STATE_SYNC
;
900 iproc
->cs
= PIOS_CRC_updateByte(iproc
->cs
, rxbyte
);
902 iproc
->type
= rxbyte
;
903 iproc
->rxPacketLength
++;
904 iproc
->packet_size
= 0;
905 iproc
->state
= UAVTALK_STATE_SIZE
;
909 static bool UAVTalkProcess_SIZE(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
)
911 while (iproc
->rxCount
< 2 && length
> (*position
)) {
912 uint8_t rxbyte
= rxbuffer
[(*position
)++];
914 iproc
->cs
= PIOS_CRC_updateByte(iproc
->cs
, rxbyte
);
915 iproc
->packet_size
+= rxbyte
<< 8 * iproc
->rxCount
;
919 if (iproc
->rxCount
< 2) {
925 if (iproc
->packet_size
< UAVTALK_MIN_HEADER_LENGTH
|| iproc
->packet_size
> UAVTALK_MAX_HEADER_LENGTH
+ UAVTALK_MAX_PAYLOAD_LENGTH
) {
926 // incorrect packet size
927 connection
->stats
.rxErrors
++;
928 iproc
->state
= UAVTALK_STATE_ERROR
;
931 iproc
->rxPacketLength
+= 2;
933 iproc
->state
= UAVTALK_STATE_OBJID
;
937 static bool UAVTalkProcess_OBJID(__attribute__((unused
)) UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
)
939 while (iproc
->rxCount
< 4 && length
> (*position
)) {
940 uint8_t rxbyte
= rxbuffer
[(*position
)++];
941 iproc
->cs
= PIOS_CRC_updateByte(iproc
->cs
, rxbyte
);
942 iproc
->objId
+= rxbyte
<< (8 * (iproc
->rxCount
++));
945 if (iproc
->rxCount
< 4) {
949 iproc
->rxPacketLength
+= 4;
951 iproc
->state
= UAVTALK_STATE_INSTID
;
955 static bool UAVTalkProcess_INSTID(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
)
957 while (iproc
->rxCount
< 2 && length
> (*position
)) {
958 uint8_t rxbyte
= rxbuffer
[(*position
)++];
959 iproc
->cs
= PIOS_CRC_updateByte(iproc
->cs
, rxbyte
);
960 iproc
->instId
+= rxbyte
<< (8 * (iproc
->rxCount
++));
963 if (iproc
->rxCount
< 2) {
966 iproc
->rxPacketLength
+= 2;
969 UAVObjHandle obj
= UAVObjGetByID(iproc
->objId
);
971 // Determine data length
972 if (iproc
->type
== UAVTALK_TYPE_OBJ_REQ
|| iproc
->type
== UAVTALK_TYPE_ACK
|| iproc
->type
== UAVTALK_TYPE_NACK
) {
974 iproc
->timestampLength
= 0;
976 iproc
->timestampLength
= (iproc
->type
& UAVTALK_TIMESTAMPED
) ? 2 : 0;
978 iproc
->length
= UAVObjGetNumBytes(obj
);
980 iproc
->length
= iproc
->packet_size
- iproc
->rxPacketLength
- iproc
->timestampLength
;
985 if (iproc
->length
>= UAVTALK_MAX_PAYLOAD_LENGTH
) {
986 // packet error - exceeded payload max length
987 connection
->stats
.rxErrors
++;
988 iproc
->state
= UAVTALK_STATE_ERROR
;
992 // Check the lengths match
993 if ((iproc
->rxPacketLength
+ iproc
->timestampLength
+ iproc
->length
) != iproc
->packet_size
) {
994 // packet error - mismatched packet size
995 connection
->stats
.rxErrors
++;
996 iproc
->state
= UAVTALK_STATE_ERROR
;
1000 // Determine next state
1001 if (iproc
->type
& UAVTALK_TIMESTAMPED
) {
1002 // If there is a timestamp get it
1003 iproc
->timestamp
= 0;
1004 iproc
->state
= UAVTALK_STATE_TIMESTAMP
;
1006 // If there is a payload get it, otherwise receive checksum
1007 if (iproc
->length
> 0) {
1008 iproc
->state
= UAVTALK_STATE_DATA
;
1010 iproc
->state
= UAVTALK_STATE_CS
;
1016 static bool UAVTalkProcess_TIMESTAMP(__attribute__((unused
)) UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
)
1018 while (iproc
->rxCount
< 2 && length
> (*position
)) {
1019 uint8_t rxbyte
= rxbuffer
[(*position
)++];
1020 iproc
->cs
= PIOS_CRC_updateByte(iproc
->cs
, rxbyte
);
1021 iproc
->timestamp
+= rxbyte
<< (8 * (iproc
->rxCount
++));
1024 if (iproc
->rxCount
< 2) {
1029 iproc
->rxPacketLength
+= 2;
1030 // If there is a payload get it, otherwise receive checksum
1031 if (iproc
->length
> 0) {
1032 iproc
->state
= UAVTALK_STATE_DATA
;
1034 iproc
->state
= UAVTALK_STATE_CS
;
1039 static bool UAVTalkProcess_DATA(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, uint8_t length
, uint8_t *position
)
1041 uint8_t toCopy
= iproc
->length
- iproc
->rxCount
;
1043 if (toCopy
> length
- (*position
)) {
1044 toCopy
= length
- (*position
);
1047 memcpy(&connection
->rxBuffer
[iproc
->rxCount
], &rxbuffer
[(*position
)], toCopy
);
1048 (*position
) += toCopy
;
1051 iproc
->cs
= PIOS_CRC_updateCRC(iproc
->cs
, &connection
->rxBuffer
[iproc
->rxCount
], toCopy
);
1052 iproc
->rxCount
+= toCopy
;
1054 iproc
->rxPacketLength
+= toCopy
;
1056 if (iproc
->rxCount
< iproc
->length
) {
1061 iproc
->state
= UAVTALK_STATE_CS
;
1065 static bool UAVTalkProcess_CS(UAVTalkConnectionData
*connection
, UAVTalkInputProcessor
*iproc
, uint8_t *rxbuffer
, __attribute__((unused
)) uint8_t length
, uint8_t *position
)
1067 // Check the CRC byte
1068 uint8_t rxbyte
= rxbuffer
[(*position
)++];
1070 if (rxbyte
!= iproc
->cs
) {
1071 // packet error - faulty CRC
1072 UAVT_DEBUGLOG_PRINTF("BAD CRC");
1073 connection
->stats
.rxCrcErrors
++;
1074 connection
->stats
.rxErrors
++;
1075 iproc
->state
= UAVTALK_STATE_ERROR
;
1078 iproc
->rxPacketLength
++;
1080 if (iproc
->rxPacketLength
!= (iproc
->packet_size
+ UAVTALK_CHECKSUM_LENGTH
)) {
1081 // packet error - mismatched packet size
1082 connection
->stats
.rxErrors
++;
1083 iproc
->state
= UAVTALK_STATE_ERROR
;
1087 connection
->stats
.rxObjects
++;
1088 connection
->stats
.rxObjectBytes
+= iproc
->length
;
1090 iproc
->state
= UAVTALK_STATE_COMPLETE
;