OP-1337 French translation : First update for 15.05
[librepilot.git] / flight / uavtalk / uavtalk.c
blobaeb580b137fdabce136a85f208361a8167e83666
1 /**
2 ******************************************************************************
3 * @addtogroup OpenPilotSystem OpenPilot System
4 * @{
5 * @addtogroup OpenPilotLibraries OpenPilot System Libraries
6 * @{
8 * @file uavtalk.c
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
12 * Telemetry module.
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
25 * for more details.
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__); }
42 #endif
43 #ifndef UAVT_DEBUGLOG_PRINTF
44 #define UAVT_DEBUGLOG_PRINTF(...)
45 #endif
46 #ifndef UAVT_DEBUGLOG_CPRINTF
47 #define UAVT_DEBUGLOG_CPRINTF(objId, ...)
48 #endif
50 // Private functions
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);
65 /**
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
69 * \return 0 Success
70 * \return -1 Failure
72 UAVTalkConnection UAVTalkInitialize(UAVTalkOutputStream outputStream)
74 // allocate object
75 UAVTalkConnectionData *connection = pios_malloc(sizeof(UAVTalkConnectionData));
77 if (!connection) {
78 return 0;
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();
86 // allocate buffers
87 connection->rxBuffer = pios_malloc(UAVTALK_MAX_PACKET_LENGTH);
88 if (!connection->rxBuffer) {
89 return 0;
91 connection->txBuffer = pios_malloc(UAVTALK_MAX_PACKET_LENGTH);
92 if (!connection->txBuffer) {
93 return 0;
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
105 * \return 0 Success
106 * \return -1 Failure
108 int32_t UAVTalkSetOutputStream(UAVTalkConnection connectionHandle, UAVTalkOutputStream outputStream)
110 UAVTalkConnectionData *connection;
112 CHECKCONHANDLE(connectionHandle, connection, return -1);
114 // Lock
115 xSemaphoreTakeRecursive(connection->lock, portMAX_DELAY);
117 // set output stream
118 connection->outStream = outputStream;
120 // Release lock
121 xSemaphoreGiveRecursive(connection->lock);
123 return 0;
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 );
150 // Lock
151 xSemaphoreTakeRecursive(connection->lock, portMAX_DELAY);
153 // Copy stats
154 memcpy(statsOut, &connection->stats, sizeof(UAVTalkStats));
156 if (reset) {
157 // Clear stats
158 memset(&connection->stats, 0, sizeof(UAVTalkStats));
161 // Release lock
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 );
176 // Lock
177 xSemaphoreTakeRecursive(connection->lock, portMAX_DELAY);
179 // Copy stats
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;
191 if (reset) {
192 // Clear stats
193 memset(&connection->stats, 0, sizeof(UAVTalkStats));
196 // Release lock
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 );
210 // Lock
211 xSemaphoreTakeRecursive(connection->lock, portMAX_DELAY);
213 // Clear stats
214 memset(&connection->stats, 0, sizeof(UAVTalkStats));
216 // Release lock
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
240 * \return 0 Success
241 * \return -1 Failure
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
259 * \return 0 Success
260 * \return -1 Failure
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);
268 // Send object
269 if (acked == 1) {
270 return objectTransaction(connection, UAVTALK_TYPE_OBJ_ACK, obj, instId, timeoutMs);
271 } else {
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
283 * \return 0 Success
284 * \return -1 Failure
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);
292 // Send object
293 if (acked == 1) {
294 return objectTransaction(connection, UAVTALK_TYPE_OBJ_ACK_TS, obj, instId, timeoutMs);
295 } else {
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
310 * \return 0 Success
311 * \return -1 Failure
313 static int32_t objectTransaction(UAVTalkConnectionData *connection, uint8_t type, UAVObjHandle obj, uint16_t instId, int32_t timeoutMs)
315 int32_t respReceived;
316 int32_t ret = -1;
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);
322 // Send object
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;
332 if (ret == 0) {
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);
339 ret = 0;
340 } else {
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);
348 return -1;
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);
355 return ret;
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);
379 uint8_t count = 0;
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)) {
388 break;
391 if (iproc->state == UAVTALK_STATE_TYPE &&
392 !UAVTalkProcess_TYPE(connection, iproc, rxbuffer, length, position)) {
393 break;
396 if (iproc->state == UAVTALK_STATE_SIZE &&
397 !UAVTalkProcess_SIZE(connection, iproc, rxbuffer, length, position)) {
398 break;
401 if (iproc->state == UAVTALK_STATE_OBJID &&
402 !UAVTalkProcess_OBJID(connection, iproc, rxbuffer, length, position)) {
403 break;
406 if (iproc->state == UAVTALK_STATE_INSTID &&
407 !UAVTalkProcess_INSTID(connection, iproc, rxbuffer, length, position)) {
408 break;
411 if (iproc->state == UAVTALK_STATE_TIMESTAMP &&
412 !UAVTalkProcess_TIMESTAMP(connection, iproc, rxbuffer, length, position)) {
413 break;
416 if (iproc->state == UAVTALK_STATE_DATA &&
417 !UAVTalkProcess_DATA(connection, iproc, rxbuffer, length, position)) {
418 break;
421 if (iproc->state == UAVTALK_STATE_CS &&
422 !UAVTalkProcess_CS(connection, iproc, rxbuffer, length, position)) {
423 break;
427 // Done
428 processedBytes = (*position) - processedBytes;
429 connection->stats.rxBytes += processedBytes;
430 return iproc->state;
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);
451 return state;
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
461 * \return 0 Success
462 * \return -1 Failure
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++;
475 return -1;
478 UAVTalkConnectionData *outConnection;
479 CHECKCONHANDLE(outConnectionHandle, outConnection, return -1);
481 if (!outConnection->outStream) {
482 outConnection->stats.txErrors++;
484 return -1;
487 // Lock
488 xSemaphoreTakeRecursive(outConnection->lock, portMAX_DELAY);
490 outConnection->txBuffer[0] = UAVTALK_SYNC_VAL;
491 // Setup type
492 outConnection->txBuffer[1] = inIproc->type;
493 // next 2 bytes are reserved for data length (inserted here later)
494 // Setup object ID
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);
499 // Setup instance ID
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);
509 headerLength += 2;
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);
521 // Copy the checksum
522 outConnection->txBuffer[headerLength + inIproc->length] = inIproc->cs;
524 // Send the buffer.
525 int32_t rc = (*outConnection->outStream)(outConnection->txBuffer, headerLength + inIproc->length + UAVTALK_CHECKSUM_LENGTH);
527 // Update stats
528 outConnection->stats.txBytes += (rc > 0) ? rc : 0;
530 // evaluate return value before releasing the lock
531 int32_t ret = 0;
532 if (rc != (int32_t)(headerLength + inIproc->length + UAVTALK_CHECKSUM_LENGTH)) {
533 outConnection->stats.txErrors++;
534 ret = -1;
537 // Release lock
538 xSemaphoreGiveRecursive(outConnection->lock);
540 // Done
541 return ret;
545 * Complete receiving a UAVTalk packet. This will cause the packet to be unpacked, acked, etc.
546 * \param[in] connectionHandle UAVTalkConnection to be used
547 * \return 0 Success
548 * \return -1 Failure
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) {
558 return -1;
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
593 * \return 0 Success
594 * \return -1 Failure
596 static int32_t receiveObject(UAVTalkConnectionData *connection, uint8_t type, uint32_t objId, uint16_t instId, uint8_t *data)
598 UAVObjHandle obj;
599 int32_t ret = 0;
601 // Lock
602 xSemaphoreTakeRecursive(connection->lock, portMAX_DELAY);
604 // Get the handle to the object. Will be null if object does not exist.
605 // Warning :
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
612 switch (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);
623 } else {
624 ret = -1;
626 } else {
627 ret = -1;
629 break;
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);
641 } else {
642 ret = -1;
644 } else {
645 ret = -1;
647 if (ret == -1) {
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);
652 break;
654 case UAVTALK_TYPE_OBJ_REQ:
655 // Check if requested object exists
656 UAVT_DEBUGLOG_CPRINTF(objId, "REQ %X %d", objId, instId);
657 if (obj) {
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);
661 } else {
662 ret = -1;
664 if (ret == -1) {
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);
669 break;
671 case UAVTALK_TYPE_NACK:
672 // Do nothing on flight side, let it time out.
673 // TODO:
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.
680 break;
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);
687 } else {
688 ret = -1;
690 break;
692 default:
693 ret = -1;
696 // Unlock
697 xSemaphoreGiveRecursive(connection->lock);
699 // Done
700 return ret;
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)
730 * \return 0 Success
731 * \return -1 Failure
733 static int32_t sendObject(UAVTalkConnectionData *connection, uint8_t type, uint32_t objId, uint16_t instId, UAVObjHandle obj)
735 uint32_t numInst;
736 uint32_t n;
737 int32_t ret = -1;
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)) {
743 instId = 0;
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)
753 ret = 0;
754 for (n = 0; n < numInst; ++n) {
755 ret = sendSingleObject(connection, type, objId, numInst - n - 1, obj);
756 if (ret == -1) {
757 break;
760 } else {
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);
771 return ret;
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)
781 * \return 0 Success
782 * \return -1 Failure
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++;
790 return -1;
793 // Setup sync byte
794 connection->txBuffer[0] = UAVTALK_SYNC_VAL;
795 // Setup type
796 connection->txBuffer[1] = type;
797 // next 2 bytes are reserved for data length (inserted here later)
798 // Setup object ID
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);
803 // Setup instance ID
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);
813 headerLength += 2;
816 // Determine data length
817 int32_t length;
818 if (type == UAVTALK_TYPE_OBJ_REQ || type == UAVTALK_TYPE_ACK || type == UAVTALK_TYPE_NACK) {
819 length = 0;
820 } else {
821 length = UAVObjGetNumBytes(obj);
824 // Check length
825 if (length > UAVOBJECTS_LARGEST) {
826 connection->stats.txErrors++;
827 return -1;
830 // Copy data (if any)
831 if (length > 0) {
832 if (UAVObjPack(obj, instId, &connection->txBuffer[headerLength]) == -1) {
833 connection->stats.txErrors++;
834 return -1;
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);
845 // Send object
846 uint16_t tx_msg_len = headerLength + length + UAVTALK_CHECKSUM_LENGTH;
847 int32_t rc = (*connection->outStream)(connection->txBuffer, tx_msg_len);
849 // Update stats
850 if (rc == tx_msg_len) {
851 ++connection->stats.txObjects;
852 connection->stats.txObjectBytes += length;
853 connection->stats.txBytes += tx_msg_len;
854 } else {
855 connection->stats.txErrors++;
856 // TODO rc == -1 connection not open, -2 buffer full should retry
857 connection->stats.txBytes += (rc > 0) ? rc : 0;
858 return -1;
861 // Done
862 return 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++;
875 return false;
878 // Initialize and update the CRC
879 iproc->cs = PIOS_CRC_updateByte(0, rxbyte);
881 iproc->rxPacketLength = 1;
882 iproc->rxCount = 0;
884 iproc->type = 0;
885 iproc->state = UAVTALK_STATE_TYPE;
886 return true;
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;
896 return false;
899 // update the CRC
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;
906 return true;
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)++];
913 // update the CRC
914 iproc->cs = PIOS_CRC_updateByte(iproc->cs, rxbyte);
915 iproc->packet_size += rxbyte << 8 * iproc->rxCount;
916 iproc->rxCount++;
919 if (iproc->rxCount < 2) {
920 return false;;
923 iproc->rxCount = 0;
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;
929 return false;
931 iproc->rxPacketLength += 2;
932 iproc->objId = 0;
933 iproc->state = UAVTALK_STATE_OBJID;
934 return true;
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) {
946 return false;
948 iproc->rxCount = 0;
949 iproc->rxPacketLength += 4;
950 iproc->instId = 0;
951 iproc->state = UAVTALK_STATE_INSTID;
952 return true;
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) {
964 return false;
966 iproc->rxPacketLength += 2;
967 iproc->rxCount = 0;
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) {
973 iproc->length = 0;
974 iproc->timestampLength = 0;
975 } else {
976 iproc->timestampLength = (iproc->type & UAVTALK_TIMESTAMPED) ? 2 : 0;
977 if (obj) {
978 iproc->length = UAVObjGetNumBytes(obj);
979 } else {
980 iproc->length = iproc->packet_size - iproc->rxPacketLength - iproc->timestampLength;
984 // Check length
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;
989 return false;
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;
997 return false;
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;
1005 } else {
1006 // If there is a payload get it, otherwise receive checksum
1007 if (iproc->length > 0) {
1008 iproc->state = UAVTALK_STATE_DATA;
1009 } else {
1010 iproc->state = UAVTALK_STATE_CS;
1013 return true;
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) {
1025 return false;;
1028 iproc->rxCount = 0;
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;
1033 } else {
1034 iproc->state = UAVTALK_STATE_CS;
1036 return true;
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;
1050 // update the CRC
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) {
1057 return false;
1060 iproc->rxCount = 0;
1061 iproc->state = UAVTALK_STATE_CS;
1062 return true;
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;
1076 return false;;
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;
1084 return false;;
1087 connection->stats.rxObjects++;
1088 connection->stats.rxObjectBytes += iproc->length;
1090 iproc->state = UAVTALK_STATE_COMPLETE;
1091 return true;
1096 * @}
1097 * @}