Reading CAN Messages

NuttX’s CAN driver’s default behavior is to return multiple messages for single read operation, if they fit. If your code (especially if migrated from SocketCAN) doesn’t count with it, then you will most likely encounter seemingly lost frames. You have two options: Either implement your code to support this behavior or you can switch this behavior off.

The following example shows how you can handle multiple messages:

#define BUFLEN 128  /* Some arbitrary size for the CAN RX buffer */

 FAR struct can_msg_s *msg;
 char rxbuffer[BUFLEN];
 ssize_t nread;
 int nbytes;
 int msglen
 int i;

 /* Read messages into the RX buffer */

 nread = read(fd, rxbuffer, BUFLEN);

 /* Check for read errors */
 ...

 /* Process each message in the RX buffer */

 for (i = 0; i <= nread - CAN_MSGLEN(0); i += msglen)
 {
 /* Get the next message from the RX buffer */

     msg    = (FAR struct can_msg_s *)&rxbuffer[i];
     nbytes = can_dlc2bytes(msg->cm_hdr.ch_dlc);
     msglen = CAN_MSGLEN(nbytes);

     DEBUGASSERT(i + msglen < BUFLEN);

 /* Process the next CAN message */
 ...
 }

By looping over the read buffer and parsing out each CAN message, it is possible to avoid losing messages that are stored contiguously in the input buffer.

The alternative is to use message alignment functionality. By setting the message alignment to zero the driver will always return only a single message for a single read operation:

unsigned msgalign = 0;
ioctl(fd, CANIOC_SET_MSGALIGN, &msgalign);

The message alignment functionality can be used to tweak the behavior event further. It in general controls alignment of messages in the buffers passed to both read and write operations. While the default behavior of packing as many messages to the buffer as possible provides the most efficient exchange, you might also want and easier usage where you pass array of messages. This can be ensured with setting message align size to exactly size of the message:

unsigned         msgsiz = sizeof(struct can_msg_s);
struct can_msg_s msgs[5];
ssize_t          nread;
int              i;

/* Set message alignment to message size. */

ioctl(fd, CANIOC_SET_MSGALIGN, &msgsiz);

/* Read messages to the array. */

nread = read(fd, msgs, sizeof(msgs));

/* Iterate over read messages */
for (i = 0; i < nread / msgsiz; i--)
  {
    /* Process CAN message msgs[i] */
  }

The same alignment rule applies to the write as well, so with alignment like it is in the example you write array of message and not message packed right after each other.