Homepage GitHub

Libcanard v1 canard_dsdl.c

uint8_t  mode   = canardDSDLGetU8(heartbeat_transfer->payload,  heartbeat_transfer->payload_size, 34,  3);
uint32_t uptime = canardDSDLGetU32(heartbeat_transfer->payload, heartbeat_transfer->payload_size,  0, 32);
uint32_t vssc   = canardDSDLGetU32(heartbeat_transfer->payload, heartbeat_transfer->payload_size, 37, 19);
uint8_t  health = canardDSDLGetU8(heartbeat_transfer->payload,  heartbeat_transfer->payload_size, 32,  2);

I am confused about “heartbeat_transfer->payload, heartbeat_transfer->payload_size”.
How can i access the file “32085.Heartbeat.1.0.uavcan” to make it as a buffer(heartbeat_transfer->payload)?

Please read the rest of the documentation:

CanardRxSubscription heartbeat_subscription;
(void) canardRxSubscribe(&ins,   // Subscribe to messages uavcan.node.Heartbeat.
                         CanardTransferKindMessage,
                         32085,  // The fixed Subject-ID of the Heartbeat message type (see DSDL definition).
                         7,      // The maximum payload size (max DSDL object size) from the DSDL definition.
                         CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
                         &heartbeat_subscription);
CanardTransfer transfer;
const int8_t result = canardRxAccept(&ins,
                                     &received_frame,            // The CAN frame received from the bus.
                                     redundant_interface_index,  // If the transport is not redundant, use 0.
                                     &transfer);
if (result < 0)
{
    // An error has occurred: either an argument is invalid or we've ran out of memory.
    // It is possible to statically prove that an out-of-memory will never occur for a given application if
    // the heap is sized correctly; for background, refer to the Robson's Proof and the documentation for O1Heap.
    // Reception of an invalid frame is NOT an error.
    abort();
}
else if (result == 1)
{
    processReceivedTransfer(redundant_interface_index, &transfer);  // A transfer has been received, process it.
    ins.memory_free(&ins, (void*)transfer.payload);  // Deallocate the dynamic memory afterwards.
}
else
{
    // Nothing to do.
    // The received frame is either invalid or it's a non-last frame of a multi-frame transfer.
    // Reception of an invalid frame is NOT reported as an error because it is not an error.
}

The contents of files suffixed with .uavcan are of serialization type?

I still don’t know how to use “uint8_t mode = canardDSDLGetU8(heartbeat_transfer->payload, heartbeat_transfer->payload_size, 34, 3);” to get data from 32085.Heartbeat.1.0.uavcan. :dizzy_face:

CanardRxSubscription heartbeat_subscription;
(void) canardRxSubscribe(&ins,   // Subscribe to messages uavcan.node.Heartbeat.
                         CanardTransferKindMessage,
                         32085,  // The fixed Subject-ID of the Heartbeat message type (see DSDL definition).
                         7,      // The maximum payload size (max DSDL object size) from the DSDL definition.
                         CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
                         &heartbeat_subscription)

Are these code is decode the data of 32085.Heartbeat.1.0.uavcan?

uint8_t  mode   = canardDSDLGetU8(heartbeat_transfer->payload,  heartbeat_transfer->payload_size, 34,  3);
uint32_t uptime = canardDSDLGetU32(heartbeat_transfer->payload, heartbeat_transfer->payload_size,  0, 32);
uint32_t vssc   = canardDSDLGetU32(heartbeat_transfer->payload, heartbeat_transfer->payload_size, 37, 19);
uint8_t  health = canardDSDLGetU8(heartbeat_transfer->payload,  heartbeat_transfer->payload_size, 32,  2);

Does these code mean that i don’t need to use nunauvt to decode the 32085.Heartbeat.1.0.uavcan to a .h file?

*.uavcan files do not contain any data. They merely specify what the data should look like. In order to publish a UAVCAN message you need to serialize it per the format defined in the respective *.uavcan file (vice versa when subscribing). You can do that manually with the help of the canardDSDL...() functions, or you could use Nunavut to generate the serialization/deserialization code automatically.

No. The fragment you posted instructs the library to accept Heartbeat transfers from the network. It is up to you then to deserialize their payload using either of the methods I mentioned above.

Thanks for your reply! I still have the following quesion:
Quesion 1: Could you please tell me the common command of nnvg to serialize/deserialize?
Quesion 2: What does serialization and deserialization mean? I think of serialization and deserialization as generating a file. Is that right?
Quesion 3:Do i have to subscribe to messages uavcan.node.Heartbeat?
I sincerely hope you can reply me. Thanks a lot !

Nunavut cannot serialize or deserialize anything. It can only create code for you that performs serialization/deserialization of your data types. Please read the Nunavut documentation for further information; if you have any specific questions about that, feel free to ask them here.

No. Serialization means converting an application data object (or, more generally, any pertinent state of your application) into a well-defined representation. Deserialization is the inverse of serialization. Please read this:

No, it is entirely optional. You do have to publish them, though.

  • Do I need to serialize a string when I send it? For example “Hello UAVCAN”
const CanardTransfer transfer = {
			    .timestamp_usec = 0,      
			    .priority       = CanardPriorityNominal,
			    .transfer_kind  = CanardTransferKindMessage,
			    .port_id        = 1234,                       // This is the subject-ID. 
			    .remote_node_id = CANARD_NODE_ID_UNSET,       // Messages cannot be unicast, so use UNSET.
			    .transfer_id    = my_message_transfer_id,
			    .payload_size   = 13,
			    .payload        = "Hello UAVCAN",
			};

Yes, of course. Strings are like variable-length arrays of uint8: they are encoded in UTF-8 and prefixed with the length prefix (uint8/uint16/uint32). This is explained in the Specification.

const CanardTransfer transfer = {
			    .timestamp_usec = 0,      
			    .priority       = CanardPriorityNominal,
			    .transfer_kind  = CanardTransferKindMessage,
			    .port_id        = 1234,                       // This is the subject-ID. 
			    .remote_node_id = CANARD_NODE_ID_UNSET,       // Messages cannot be unicast, so use UNSET.
			    .transfer_id    = my_message_transfer_id,
			    .payload_size   = 13,
			    .payload        = "Hello UAVCAN",
			};

what is the difference between transfer_id and port_id ?

Please read chapter 2 “Basic concepts” of the UAVCAN Specification. Briefly:

  • Port-ID (subject-ID, service-ID) specifies the purpose of the data. This is like the topic name in ROS or DDS or some MQs.

  • Transfer-ID identifies transfers (that is, messages in case of pub/sub links) under the same port-ID. It allows you to distinguish multiple messages received from a given subject.


Does unique node-ID mean transfer-ID?

No. If they were the same thing, they would have been named identically. Please read the specification, every question you asked so far is answered there.

use libcanard v1
platform: stm32h743 baremetal platforms
runs results:  No matching subscription.

void UavcanRun(void)
{

	static uint8_t a = 0;
	CanardInstance ins = canardInit(&memAllocate, &memFree);
	ins.mtu_bytes = CANARD_MTU_CAN_FD;                      //Defaults to 64 (CAN FD); here we select Classic CAN.
	ins.node_id   = 42;
    a++;
	if(a == 1) //subcribe once 
	{
	 a++;
	CanardRxSubscription heartbeat_subscription;
	(void) canardRxSubscribe(&ins,   					// Subscribe to messages uavcan.node.Heartbeat.
							 CanardTransferKindMessage,
							 32085,  					// The fixed Subject-ID of the Heartbeat message type (see DSDL definition).
							 7,      					// The maximum payload size (max DSDL object size) from the DSDL definition.
							 CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
							 &heartbeat_subscription);

	CanardRxSubscription mymsg_subscription;
	(void) canardRxSubscribe(&ins,   					// Subscribe to messages uavcan.node.Heartbeat.
							 CanardTransferKindMessage,
							 4416,  					// The fixed Subject-ID of the Heartbeat message type (see DSDL definition).
							 2,      					// The maximum payload size (max DSDL object size) from the DSDL definition.
							 CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
							 &mymsg_subscription);
	}

		if(cnt %1000 == 0){       //excute per 1000ms 
			uint8_t buffer[7] = {0};  
			canardDSDLSetUxx(&buffer[0], 	34,          		2,  		3);   // mode
			canardDSDLSetUxx(&buffer[0], 	32,          		2,  		2);   // health
			canardDSDLSetUxx(&buffer[0],  	0, 					0xDEADBEEF, 32);   // uptime
			canardDSDLSetUxx(&buffer[0], 	37,    				0x7FFFF, 	19);   // vssc
			static uint8_t heartbeat_transfer_id;
			 
			const CanardTransfer transfer = {
				.timestamp_usec = HAL_GetTick(),      
				.priority       = CanardPriorityNominal, 
				.transfer_kind  = CanardTransferKindMessage,
				.port_id        = 32085,                       // This is the subject-ID.
				.remote_node_id = CANARD_NODE_ID_UNSET,       // Messages cannot be unicast, so use UNSET.
				.transfer_id    = heartbeat_transfer_id,
				.payload_size   = 7,   
				.payload        = &buffer[0], 
			};
			++heartbeat_transfer_id; 
			int32_t result = canardTxPush(&ins, &transfer);
			printf("------------------send hearttransfer-------------------------\n");
			if (result < 0)
			{
				// An error has occurred: either an argument is invalid or we've ran out of memory.
				// It is possible to statically prove that an out-of-memory will never occur for a given application if the
				// heap is sized correctly; for background, refer to the Robson's Proof and the documentation for O1Heap.
		//	    abort();
				printf("Illegal parameter or Memory Exhausted \n");
	//				return ;
			}
		}

		if(cnt %200 == 0){//excute per 200ms 
				uint8_t my_msg[2] = {0};
				canardDSDLSetUxx(&my_msg[0], 	0,          		120,  		8);  
				canardDSDLSetUxx(&my_msg[0], 	8,          		110,  		8);   
				static uint8_t mymsg_transfer_id;
				 
				const CanardTransfer mytransfer = {
					.timestamp_usec = HAL_GetTick(),      
					.priority       = CanardPriorityNominal, 
					.transfer_kind  = CanardTransferKindMessage,
					.port_id        = 4416,                       // This is the subject-ID.
					.remote_node_id = CANARD_NODE_ID_UNSET,       // Messages cannot be unicast, so use UNSET.
					.transfer_id    = mymsg_transfer_id,
					.payload_size   = 2,   
					.payload        = &my_msg[0], 
				};
				++mymsg_transfer_id;  
				int32_t myresult = canardTxPush(&ins, &mytransfer); 
				printf("---------------------send mytransfer------------\n");
				if (myresult < 0)
				{
					// An error has occurred: either an argument is invalid or we've ran out of memory.
					// It is possible to statically prove that an out-of-memory will never occur for a given application if the
					// heap is sized correctly; for background, refer to the Robson's Proof and the documentation for O1Heap.
			//	    abort();
					printf("Illegal parameter or Memory Exhausted \n");
			//				return ;
				}
		}
		    static uint8_t fail_count;
			FDCAN_TxHeaderTypeDef *txmsg = NULL;
			uint8_t sendbuf[7] = {0}; 


			for (const CanardFrame* txf = NULL; (txf = canardTxPeek(&ins)) != NULL;)  // Look at the top of the TX queue.
			{
				printf("enter txmsg:\n");
				static uint8_t i = 0;
				txmsg->Identifier = txf->extended_can_id >> 8 & 0x00ffffU;  
				txmsg->DataLength = txf->payload_size << 16 &  0x000F0000U; 
				txmsg->IdType = FDCAN_EXTENDED_ID;
				txmsg->FDFormat = FDCAN_FD_CAN;
				txmsg->TxFrameType = FDCAN_DATA_FRAME;
				txmsg->BitRateSwitch = FDCAN_BRS_OFF;
				txmsg->TxEventFifoControl = FDCAN_STORE_TX_EVENTS;
				txmsg->ErrorStateIndicator = FDCAN_ESI_ACTIVE;
				txmsg->MessageMarker = 0x01;

				
				memcpy(sendbuf,txf->payload,txf->payload_size); 

			HAL_StatusTypeDef ret = HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, txmsg, sendbuf);  //stm32 hal library
			HAL_FDCAN_Start(&hfdcan1); 
			HAL_FDCAN_EnableTxBufferRequest(&hfdcan1, FDCAN_TX_BUFFER0);


			if(ret != HAL_OK)
			{

				// just exit and try again later. If we fail 8 times in a row
				// then start discarding to prevent the pool filling up
				if (fail_count < 8) {
					fail_count++;
					printf("send fail,fail_count:%d\n",fail_count);
				} else {
					canardTxPop(&ins);
					printf("fail_count 8 times, exit send\n");
					ins.memory_free(&ins, (CanardFrame*)txf);  // Deallocate the dynamic memory afterwards.
				}
				return;
			}
			else{
				canardTxPop(&ins);
				fail_count = 0;
				ins.memory_free(&ins, (CanardFrame*)txf);  // Deallocate the dynamic memory afterwards.
			}

		}

//////////////////////////	//receive serialized datat     //////////////////////////

				FDCAN_RxHeaderTypeDef RxHeader={0};
				uint32_t RxLocation = 0;
				uint8_t recdata[8] = {0};  
			   	CanardFrame received_frame;
			   	received_frame.payload = &recdata;

				 if(HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO0, &RxHeader, recdata) == HAL_OK) 

				{
				printf("RxHeader.Identifier = %d\n",RxHeader.Identifier);

				CanardTransfer receive={0};

				if(RxHeader.Identifier == 32085)
					received_frame.payload_size = 7;
				else
					received_frame.payload_size = 2;

				received_frame.extended_can_id = RxHeader.Identifier;
				memcpy(received_frame.payload,recdata,received_frame.payload_size);

				uint8_t result = canardRxAccept(&ins,
										&received_frame,            // The CAN frame received from the bus.
										0,  						// If the transport is not redundant, use 0.
										&receive);
				if (result < 0)
				{
					// An error has occurred: either an argument is invalid or we've ran out of memory.
					// It is possible to statically prove that an out-of-memory will never occur for a given application if
					// the heap is sized correctly; for background, refer to the Robson's Proof and the documentation for O1Heap.
					// Reception of an invalid frame is NOT an error.
				

					printf("RxAccept failure\n");


					printf("An error has occurred: either an argument is invalid or we've ran out of memory.\n");
					abort();
				}
				else if (result == 1) 
				{
					printf("Receive Port ID :%d\n",receive.port_id);
					//processReceivedTransfer(redundant_interface_index, &transfer);  // A transfer has been received, process it.
					ins.memory_free(&ins, (void*)receive.payload);  // Deallocate the dynamic memory afterwards.
				}
				else
				{
					// Nothing to do.
					// The received frame is either invalid or it's a non-last frame of a multi-frame transfer.
					// Reception of an invalid frame is NOT reported as an error because it is not an error.
					printf("nothing to do \n");

				}
}

Thanks for your reply.I run this code ,i found it return " No matching subscription. " at canardRxAccept.
Is UAVCAN subscribed, sent and received in my code correct?
–>use libcanard v1 ------https://github.com/UAVCAN/libcanard
–>platform: stm32h743 baremetal platforms
–>runs results: No matching subscription.

And how to make rxAcceptFrame function return 1, so i can handle the received data.

There are many logical errors in your code; I will mention only some of the most critical ones.

  1. It would appear that the function UavcanRun() is to be invoked periodically while updating an external state. Yet the state is not retained between invocations – the library instance is reinitialized from scratch upon the entry.

  2. Even if the above were to be done correctly, it still wouldn’t work because you would be destroying and re-initializing the subscription instances every 256th invocation due to the integer overflow.

  3. This manipulation destroys the identifier: txmsg->Identifier = txf->extended_can_id >> 8 & 0x00ffffU;

  4. This is incorrect because the original frame payload length information is overwritten by your hardcoded constants; you need to use the length information from the driver:

if (RxHeader.Identifier == 32085)
    received_frame.payload_size = 7;
else
    received_frame.payload_size = 2;
  1. This memcpy copies the data back where it was – the source and the destination addresses are identical, which results in undefined behavior:
uint8_t recdata[8] = {0};
CanardFrame received_frame;
received_frame.payload = &recdata;
...
memcpy(received_frame.payload,recdata,received_frame.payload_size);

My ability to look through your code is limited because doing so is rather time-consuming. You seem to lack certain fundamental knowledge like understanding the concept of state, memory addressing, and memory management. This remark is not to pontificate but to help – consider practicing with simpler tasks first and build the complexity gradually. You may find help with that elsewhere.

Thank you very much! how about this? It that right to give the handled length?

txmsg->DataLength = txf->payload_size << 16 &  0x000F0000U; 
txmsg->DataLength = txf->payload_size;