Homepage GitHub

CanardRxTransfer payload reading?

(kent.martin) #1

Hi all,
I’m getting some code going and have triple can interface going , but now I also need to send CAN data over serial for debugging.
For single frame transfers I have this working ok where payload_len data bytes appear in
payload_head array when onreception is called. I can simply add the informatin like trasfer id , data_type_ source and dest and send alll this info over serial and then re transmit it on the CAN bus at the other end to make a CAN bridge over serial.
I hacked libcanard slightly to get the extra info and be able to send messages with a faked source ID. This all works , except for messages with multiple frames.
for example a getnodeinfo response message appears with pointer in payload_head and payload_middle, I can’t tell where all the data actually is to be able to forward it.
I found the first 6 bytes in payload_head, but the data in payload_middle seems to start in the 4th byte (as 6th of whole data) and it doesn’t line up after that although all data appears to be there. If I try and send 6 bytes from head an the rest from middle, it does not read properly in gui tool. In the end I got the rxtransfer message and called uavcan_protocol_GetNodeInfoResponse_decode and then uavcan_protocol_GetNodeInfoResponse_encode to get a clean single buffer back.
That did not work either as I started with a 60 byte length and ended with 61 bytes after decoding and encoding. I saw that the name buffer had a 0 at the front so I moved the pointer forward one byte and reduced the name length by one and voila I got the correct display in the gui tool for my node behind the serial bridge.
So I now have a reference buffer to compare to but I can’t see how this is broken up in the two head and middle buffers? I would like to transmit the data across serial without decoding and encoding every message obviously, so can someone tell me how to find the data in the head middle and tail buffers for multi frame messages?
I do need a bridge in serial , and CAN will not do for this.
It appears as though the decode function and encode do not always work when called after each other to produce the same data.


(Pavel Kirienko) #2

What is the value of CANARD_MULTIFRAME_RX_PAYLOAD_HEAD_SIZE on your platform?

Uh-oh, sorry. It is best to steer clear of libcanard’s DSDL compiler, it’s broken. We are slowly building a new one for UAVCAN v1, any help to that end would be greatly appreciated. As for the old one, we added a warning saying that it’s unfit for use. Most of our resources are currently allocated onto finalizing UAVCAN v1 where many improvements have been made over v0, but we certainly accept contributions to the existing v0 software products.

We have this description in the sources:

     * Payload is scattered across three storages:
     *  - Head points to CanardRxState.buffer_head (length of which is up to CANARD_PAYLOAD_HEAD_SIZE), or to the
     *    payload field (possibly with offset) of the last received CAN frame.
     *  - Middle is located in the linked list of dynamic blocks (only for multi-frame transfers).
     *  - Tail points to the payload field (possibly with offset) of the last received CAN frame
     *    (only for multi-frame transfers).
     * The tail offset depends on how much data of the last frame was accommodated in the last allocated block.
     * For single-frame transfers, middle and tail will be NULL, and the head will point at first byte
     * of the payload of the CAN frame.
     * In simple cases it should be possible to get data directly from the head and/or tail pointers.
     * Otherwise it is advised to use canardDecodePrimitive().
    const uint8_t* payload_head;            ///< Always valid, i.e. not NULL.
                                            ///< For multi frame transfers, the maximum size is defined in the constant
                                            ///< CANARD_MULTIFRAME_RX_PAYLOAD_HEAD_SIZE.
                                            ///< For single-frame transfers, the size is defined in the
                                            ///< field payload_len.
    CanardBufferBlock* payload_middle;      ///< May be NULL if the buffer was not needed. Always NULL for single-frame
                                            ///< transfers.
    const uint8_t* payload_tail;            ///< Last bytes of multi-frame transfers. Always NULL for single-frame
                                            ///< transfers.
    uint16_t payload_len;                   ///< Effective length of the payload in bytes.

Does the behavior you are observing not match the description?

(kent.martin) #3

I’d like to know what source you are looking at , as that is not what is on github.

Quote “In simple cases it should be possible to get data directly from the head and/or tail pointers.
* Otherwise it is advised to use canardDecodeScalar” from git hub source in canard.h
And I want to get the entire buffer of data not individual decoded data.
Ps there is also no canarddecodePrimitive in the source quoted or function.
CANARD_MULTIFRAME_RX_PAYLOAD_HEAD_SIZE i believe was 6 on my platform.
As described in my first post I do not want individual values but rather the entire data as on block so I can re transmit it. As similar to when using single frame transfers where all data is contained in one array in the payload head section.
Libcanard dsdl is working ok for the few messages we are using so far, which is only a small number if our own messages. This will suffice for now, and we can move over later to version 1 once it has been completed thanks. So I’m not concerned on fixing those issues as they don’t really impact us if we can just deal with raw binary data for forwarding, which is the preferred case.

Thanks for your efforts,

(kent.martin) #4

I managed to get the data out using DeScattertrasnferpayload calling it multiple time to get the data 8 bits at a time. This is a bit unwieldy so I wrote another to return each buffer of data. So that’s fine now thanks.
After this I got to testing and noticed that some data was getting lost , approc 15-20% of packets were dissapearing. I debugged the code and found that shouldacceptransfer was always getting called the right number of times but ontransferrecprion was not. Further debuggin into canard and canrdstm32 faound it was not running out of pool memory and their were no rx overflows. There was LEC bits getting set in line lec = (uint8_t)((BXCAN->ESR & CANARD_STM32_CAN_ESR_LEC_MASK) >> CANARD_STM32_CAN_ESR_LEC_SHIFT); The value was 2, which may be format error?
I checked the CAN signal and it was clean and the uavcanguitool was showing all the data was there correctly on the bus. Any ideas on what may be causing this loss of data?


(kent.martin) #5

Ok, I found the problem with this.
The transfer timeout is set to #define TRANSFER_TIMEOUT_USEC 2000000
This is 2 seconds! I am sending packets every 100mS .
This was causing a lot of incoming packets to be missed.
I set it back to 75000 (75mS) and nothing is missed.
It seems you need to tailor this to the frequency of data you are expecting?
Is this really the correct fix or is there another way?


(Pavel Kirienko) #6

It is on GitHub, in the file you linked, see line 285.

I understand, but Libcanard doesn’t use large contiguous buffers. All data is stored in the scattered storage in the interest of memory footprint minimization. You will need to walk the scattered blocks.

This is per the Specification, you do not need to change this value as it does not contradict your usage.

No. From your description, I infer that the transfers were being dropped because the sender did not increment the Transfer ID field, which is mandatory. Perhaps the sender is another Libcanard-based application, and you forgot to make the transfer-ID variable static.

I highly recommend you to bring the timeout value back to the default value because it may cause issues with unintended frame duplication. You can find the background explained in Chapter 4 “Transport layer”:

(kent.martin) #7

I looked over the code and I do use static variables for transfer id’s.
I use a separate variable for each message type.
The only exception is where I am forwarding messages.
So that might explain issues for forwarding messages but not for normal cases, which i also tested as missing packets.
I will retest again later, but I’m pretty sure it is losing packets when a static transfer id is being used.

In the case for forwarding I receive the transfer id over serial along with other data and send out the CAN port with the transfer id i got from the data. If I make it static there will be only one static variable for all messages that I forward. Will that still work? Or is it required that there is a separate static variable for each message type? That would become difficult as there are multiple can buses and multiple messages being forwarded.

On a separate note , or back to an old one, I have been looking at kocherga to use as a bootloader over CAN bus. I’m not exactly up with c++ so could you tell me if there is an example in there showing how to implement uavcan with kocherga? In the ones I looked at I couldn’t find it.
There’s no description of what the files in the test folder do.
It would take me quite a while to wade through the kocherga and try to undestand and use it.
A simple example that calls it would greatly help in getting started.


(kent.martin) #8

I forgot to mention when I was losing rx packets the uavcan gui tool was seeing them fine and not losing any,
whilst libcanard was.
This rules out a problem with the sender.
There is another issue. How can this be resolved?


(kent.martin) #9

Hi, Never mind I think I know what the issue is. I am using 3 can ports but there is only one static variable for 3 ports. Could you clarify when a unique transfer_id variable is required please ?
i.e. per message, or per CAN bus , or per message per can bus.
I will need that info to figure out a way to handle all cases properly.


(Pavel Kirienko) #10

Per transfer. The details are explained in the specification. I recommend reading the new one, v1, despite the fact that you are using v0 because it’s just written better and the description is somewhat more consistent.

You should diagnose the issue by looking at the status codes returned from canardHandleRxFrame().

(kent.martin) #11

Thanks the spec quotes:
Pointer to the Transfer ID should point to a persistent variable (e.g. static or heap allocated, not on the stack);

  • it will be updated by the library after every transmission. The Transfer ID value cannot be shared between
  • transfers that have different descriptors! More on this in the transport layer specification.
    However the “transport layer” term exist nowhere else in this file and I cannot find this section.
    What exactly is the “descriptor” referring to? message id?
    The reason I ask is because terminology in the description does not appear to match the code.
    It is unclear, could you please clarify?
    Where exactly is the specification? and which section do you refer to?
    Per transfer? by that you mean each time sendcanard is called it should have a new variable?
    This seems not right, in this case you would need an unending supply of static variables for each time you called the function.
    Could you please clarify this in a simple way.
    i.e. like every message id type should have it’s own static variable.
    or only one static variable is required for all calls,
    or a unique static variable is required for every single call to the function (but wow that’s a lot or ram)


(Pavel Kirienko) #12

However the “transport layer” term exist nowhere else in this file and I cannot find this section.
What exactly is the “descriptor” referring to? message id?

The text refers to the specification (that’s v1 but it is identical with v0 in this regard). The specification has a section titled “4.1.5. Transfer descriptor”, where it says this:

Transfer emission and reception processes rely on the concept of transfer descriptor.
A transfer descriptor is a set of properties that identify a particular set of transfers that originate from the same source node, share the same port-ID, same kind (message or service), and are addressed to the same destination node (the latter applies only to unicast transfers).

The reason I ask is because terminology in the description does not appear to match the code.
It is unclear, could you please clarify?

We should work more on the documentation. The terminology you are looking for is defined well by Specification; I agree that the format is not very user-friendly but it’s the only source we have at the moment. We would certainly welcome contributions improving our docs!

Could you please clarify this in a simple way.
i.e. like every message id type should have it’s own static variable.

If you want to publish a message, you need one transfer-ID variable per data type ID (or subject-ID if you’re using the stable UAVCAN v1.0 instead of the experimental v0).

If you want to call a service, you need one transfer-ID variable per ((data type ID or service-ID if you’re using v1) AND server node-ID).

If you want to respond to a service, you don’t need any transfer-ID variables; instead, you re-use the transfer-ID value you got in the request.

(kent.martin) #13

Thanks, I implemented a unique id for each data type ID and it seems to work ok now at least for the not normal case (i.e… not bridged), for forwarding bridged messages I allocataed a pool and use one for each node and data type id combo. Haven’t tested this yet but should be ok frfom what you say. For responses I’m still using a unique one, even though not strictly necessary, but can’t hurt as the unique transfer ID variable is set to the one passed in first anyway.
I’ll test the bridge later but all good for now, thanks!

Back to the other Q, is there a sample code (with main function) for implementing uavcan protocol on kocherga? I haven’t done much c++ and it was a long time ago, so a sample here would be good to get started. I couldn’t find a description of the test files in the test folder, and I can’t seem to find one here that does that.
Are there any pointers for getting started with kocherga with uavcan?

(Pavel Kirienko) #14

I would advise studying the specification carefully before departing from it as you described lest you end up with a broken protocol. The problem with your change is that it is necessary to re-use the same transfer-ID in the response transfer to let the client match the request with the response; the modified behavior will break service calls. Please, please follow the documentation unless you have an in-depth understanding of the motivation behind every requirement provided in the specification.

Sure. Here:

(kent.martin) #15

Hi, Thanks,
silly question, but I found no reference to kocherga in there whatsoever.
A search for that term in the bootloader folder returns no results.
Is this using kocherga or did you misread the question?


(kent.martin) #16

I tried to build the zubax gnns firmware but got theses errors:
Compiler failure
Traceback (most recent call last):
File “libuavcan/libuavcan/dsdl_compiler/libuavcan_dsdlc”, line 61, in
dsdlc_run(args.source_dir, args.incdir, args.outdir)
File “/mnt/c/users/kentm/documents/github/sw4stmworkspace/zubax_gnss/firmware/libuavcan/libuavcan/dsdl_compiler/libuavcan_dsdl_compiler/init.py”, line 62, in run
run_generator(types, output_dir)
File “/mnt/c/users/kentm/documents/github/sw4stmworkspace/zubax_gnss/firmware/libuavcan/libuavcan/dsdl_compiler/libuavcan_dsdl_compiler/init.py”, line 111, in run_generator
File “/mnt/c/users/kentm/documents/github/sw4stmworkspace/zubax_gnss/firmware/libuavcan/libuavcan/dsdl_compiler/libuavcan_dsdl_compiler/init.py”, line 89, in die
raise DsdlCompilerException(str(text))
DsdlCompilerException: line 16, col 17: invalid syntax (, line 1)
line 16, col 17: invalid syntax (, line 1)

Compiling air_sensor.cpp
cc1plus: error: -Werror=implicit-fallthrough: no option -Wimplicit-fallthrough
cc1plus: error: -Werror=bool-operation: no option -Wbool-operation
zubax_chibios///chibios/os/common/ports/ARMCMx/compilers/GCC/rules.mk:198: recipe for target ‘build/obj/air_sensor.o’ failed
make: *** [build/obj/air_sensor.o] Error 1

I have python 3.6 and python 2.7 installed and running on ubuntu 18.04 under windows.

(Pavel Kirienko) #17

Sorry, the code I linked above is based on a much earlier library which was a prototype of Kocherga. Please use this example instead:

kocherga-demo.tar.gz (27.9 MB)

I had to edit the sources manually before publishing but they should still be valid.

You are running a very old GCC. The demo attached here requires v7.3.

Why the code generator has failed is unclear; consider re-running with -v if the issue persists.

(kent.martin) #18

I downloaded the link you sent.
It seems that this the korcherga repo test folder just with the libcanard and other libraries already in there.
I have seen this before as this was one of the first links you sent.
I had already looked at this and I was asking which of the files is the demo for korcherga with libcanard?
This is still my question, as I said earlier there is no explanation for what files in the test folders do.
Can you provide an explanation of what each test file does, or just the one I need to use korcherga with libcanard to make a bootloader. Please read the previous messages and provide more detail please as there is little to no documentation to explain any test code.

(Pavel Kirienko) #19

The archive that I attached contains a demo application for Kocherga, which I understood you were looking for.

Yes, it’s in the archive: