Use libcanard to send getinfo responce problem

I am using an STM32 with Libcanard and the C code provided above to send a Cyphal/CAN uavcan.node.GetInfo.Response. However, the messages received (as seen in the attached CAN log images) appear corrupted. Specifically, the last CAN frame of the transfer contains extra garbage bytes (BC 10) appended after the expected payload (32 00 00, which should represent the end of the ‘name’ string and the zero counts for CRC and CoA). This corruption prevents Yakut from successfully parsing the GetInfo response and leads to transport errors being reported. Could the provided C code be responsible for this issue?

#define MY_NODE_NAME "org.stm32.12"
#define MY_SW_VERSION_MAJOR 1
#define MY_SW_VERSION_MINOR 0
#define MY_HW_VERSION_MAJOR 0
#define MY_HW_VERSION_MINOR 1
if (transfer->metadata.transfer_kind == CanardTransferKindRequest &&
        transfer->metadata.port_id == uavcan_node_GetInfo_1_0_FIXED_PORT_ID_)
    {
        // Received a GetInfo request targeted at us (or broadcast)

        // 1. (Optional) Deserialize the request payload (it should be empty)
        uavcan_node_GetInfo_Request_1_0 req;
        size_t req_payload_size = transfer->payload_size;
        if (uavcan_node_GetInfo_Request_1_0_deserialize_(&req, transfer->payload, &req_payload_size) < 0) {
            // Deserialization failed (though payload is empty, this check might catch framing issues)
            // Log error, but maybe still proceed to respond? Or ignore.
        }

        // 2. Prepare the response payload
        uavcan_node_GetInfo_Response_1_0 response = {0}; // Initialize all fields to zero/empty

        // -- Populate mandatory fields --
        response.protocol_version.major = CANARD_CYPHAL_SPECIFICATION_VERSION_MAJOR;
        response.protocol_version.minor = CANARD_CYPHAL_SPECIFICATION_VERSION_MINOR;

        response.hardware_version.major = MY_HW_VERSION_MAJOR;
        response.hardware_version.minor = MY_HW_VERSION_MINOR;

        response.software_version.major = MY_SW_VERSION_MAJOR;
        response.software_version.minor = MY_SW_VERSION_MINOR;

        // -- Populate optional fields --
        response.software_vcs_revision_id = 0; // Or use actual Git commit hash ID if available

        get_unique_id(response.unique_id); // Fill the 16-byte unique ID array

        // Set the node name (ensure null termination and check length)
        strncpy((char*)response.name.elements, MY_NODE_NAME, sizeof(response.name.elements) - 1);
        //response.name.elements[sizeof(response.name.elements) - 1] = '\0'; // Ensure null termination
        response.name.elements[sizeof(response.name.elements)] ; // Ensure null termination
        response.name.count = strlen((char*)response.name.elements);

        // software_image_crc and certificate_of_authenticity are optional
        response.software_image_crc.count = 0; // No CRC available
        response.certificate_of_authenticity.count = 0; // No CoA available


        // 3. Serialize the response
        getinfo_response_ser_buf_size = sizeof(getinfo_response_ser_buf); // Reset size before use
        int8_t ser_res = uavcan_node_GetInfo_Response_1_0_serialize_(&response, getinfo_response_ser_buf, &getinfo_response_ser_buf_size);

        if (ser_res >= 0)
        {
            // 4. Prepare response metadata
            CanardTransferMetadata response_metadata = {
                .priority = CanardPriorityNominal, // Respond with nominal priority
                .transfer_kind = CanardTransferKindResponse, // *** This is a RESPONSE ***
                .port_id = uavcan_node_GetInfo_1_0_FIXED_PORT_ID_, // Use the same service Port ID
                .remote_node_id = transfer->metadata.remote_node_id, // *** Respond TO the requester ***
                .transfer_id = transfer->metadata.transfer_id, // *** Use the SAME transfer ID ***
            };

            // 5. Push response to the TX queue
            canardTxPush(&queue, &canard, 0, &response_metadata, getinfo_response_ser_buf_size, getinfo_response_ser_buf);

            // Ignore push errors for now, or log them
        }


Could you provide the message as text to copy instead of pictures? Are you using BXCan or HAL CAN?

I use HAL CAN

Node ID: 5
Message Content :
protocol_version (uavcan.node.Version.1.0):
major: 1
minor: 0
hardware_version (uavcan.node.Version.1.0):
major: 0
minor: 1
software_version (uavcan.node.Version.1.0):
major: 1
minor: 0
software_vcs_revision_id (uint64): 0
unique_id (uint8[16]): 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10
name : “org.stm32.12” (Length: 12 bytes)
(ASCII Hex: 0C 6f 72 67 2e 73 74 6d 33 32 2e 31 32)
software_image_crc (optional uint64 array, max 1 element): 0
certificate_of_authenticity (CoA) (byte array, up to 222 bytes): 0

Your code looks correct. The transfer also looks correct, the “garbage bytes” are the transfer-CRC. You need to add error handling to the TxPush function and then look into the part that offloads the CAN frames from the TX queue into the CAN driver.

If you’re using the stock CAN driver from ST, you need to be sure to avoid inner priority inversion:

Thank you for your reply.
After reviewing the Cyphal specification, I found that BC 10, as defined by Cyphal/CAN, is a multi-frame transport that includes an additional transfer CRC.
The error I was experiencing, regarding the reversed third and fourth lines of the GetInfo response, has been resolved. I fixed this by changing hcan1.Init.TransmitFifoPriority from DISABLE to ENABLE in my code. After this change, the message transmission order was corrected, and I can now see the correct GetInfo response message on Yakut.
Thank you very much.

static void MX_CAN1_Init(void)
{

  /* USER CODE BEGIN CAN1_Init 0 */

  /* USER CODE END CAN1_Init 0 */

  /* USER CODE BEGIN CAN1_Init 1 */

  /* USER CODE END CAN1_Init 1 */
  hcan1.Instance = CAN1;
  hcan1.Init.Prescaler = 1;
  hcan1.Init.Mode = CAN_MODE_NORMAL;
  hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
  hcan1.Init.TimeSeg1 = CAN_BS1_13TQ;
  hcan1.Init.TimeSeg2 = CAN_BS2_2TQ;
  hcan1.Init.TimeTriggeredMode = DISABLE;
  hcan1.Init.AutoBusOff = DISABLE; // Consider enabling for robustness
  hcan1.Init.AutoWakeUp = DISABLE;
  hcan1.Init.AutoRetransmission = DISABLE; // Cyphal handles retransmission at transfer layer if needed
  hcan1.Init.ReceiveFifoLocked = DISABLE; // Allow overwrite on overrun
  hcan1.Init.TransmitFifoPriority = ENABLE;
  if (HAL_CAN_Init(&hcan1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN CAN1_Init 2 */

  /* USER CODE END CAN1_Init 2 */

}


You should be careful with this. From your description I see that you replaced one problem (frame reordering) with another (inner priority inversion). Earlier I linked a post on the forum discussing this problem; there were a few more posts similar to yours where people ran into the same problem.

The correct scheduling policy is neither priority-based nor a simple FIFO. In the case of your controller specifically, the correct policy is to configure your controller in the priority TX mode (like it was before your fix), and for every outgoing frame:

  1. Check if the frame you want to transmit has a higher priority than the highest-priority frame already in the hardware queue.
  2. If the priority of your frame is not higher, do not add it to the hardware queue (assume it is full) — leave it waiting in the libcanard queue instead.
  3. Otherwise, your frame either has a higher priority, or the hardware queue is empty. In either case it is safe to add your frame.

The capacity of the TX queue should be at least as large as the number of distinct transfer priority levels used in your application; if the queue is smaller, a priority inversion may occur.