2

I'm trying to use C/C++ to communicate with a CAN bus. I'm using sockets to read and write on the bus. I've created a write thread and a read thread. The read thread is constantly trying to read and the socket, and when a write request arrives, the write thread take control of the socket using a mutex and do the write.

I'm having a big issue with speed using this method, as a write request can sometimes take 500 ms to be completed (which is completely unfeasible for my application). I've tried to put a timeout on the read command to make it non-blocking when nothing comes on the bus, but if the timeout is too short, I have reliability issues with the read. On the other hand, if I make it too long, the speed increase is insufficient.

It's my first time working with CAN. Would you have some advices on implementation of fast, two-way CAN communication node in C/C++ ? Which library should I use to interface with the bus itself ? Which architecture would yield the lowest read and write latency ?

To give a few metrics for the application, the bus bitrate is 1MBits/sec, I'm using CAN-Open with 64 bits data packets (each message contains 32 bits for indexes and 32 bits of data). I would like a write frequency from 300 to 500hz, same for the read frequency.

Thanks a lot for your help !

EDIT :

Thanks a lot for all your comments. Here are some clarifications on my application and problems.

I'm working on a mobile robot project, and I'm using CAN-Open to communicate with motor drivers and sensors. The code will run on a Raspberry Pi CM4 running Raspbian OS mounted on a custom IO board integrating a MCP2515 CAN controller. I want to implement fast communication interface between the ROS architecture and the CAN bus. The language used could be either C or C++.

I'm currently using a homemade interface build around standard C sockets, but the speed is very low, and is a big bottleneck to the robot's performance. So I'm looking for a better solution, either:

  • An open-source library build for this purpose
  • Architecture suggestion to implement such a program
  • A combination of both

Here are the socket creation, the read and the write functions I use. Read and write being each called in a while loop in different threads (I'm using pthread):

bool connectCanBus(int* socketIDOut, std::string canInterfaceName){
  // Socket and can variables
  struct sockaddr_can addr;
  struct ifreq ifr;

  // Openning the socket to send commands over the can bus
  if ((*socketIDOut = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
    perror("[Can Controler] Unable to create socket.");
    return false;
  }

  strcpy(ifr.ifr_name, canInterfaceName.c_str());
  ioctl(*socketIDOut, SIOCGIFINDEX, &ifr);
  memset(&addr, 0, sizeof(addr));
  addr.can_family = AF_CAN;
  addr.can_ifindex = ifr.ifr_ifindex;

  // Setting option to gte errors as frames
  can_err_mask_t err_mask = 0xFFFFFFFF;
  setsockopt(*socketIDOut, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &err_mask, sizeof(err_mask));

  struct timeval tv;
  tv.tv_sec = 0;
  tv.tv_usec = 10000;
  setsockopt(*socketIDOut, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);

  // Binding Socket
  if (bind(*socketIDOut, (struct sockaddr *)&addr, sizeof(addr)) < 0)       
{
    perror("[Can Controler] Unable to bind socket.");
    close(*socketIDOut);
    return false;
  }

  ROS_INFO("CAN bus connected.");
  return true;
}

int sendCommand(const char* id, const char* message, int socket, std::mutex& mutex)
{
  struct canfd_frame frame;
  int err = parseFrame(id, message, &frame);
  if(err == 0){
    ROS_ERROR_STREAM("[Can Utils] Unable to parse frame : " << id << ", " <<  message);
        return 0;
  }

    mutex.lock();
    int res = write(socket, &frame, sizeof(struct can_frame));
    mutex.unlock();
  if (res != sizeof(struct can_frame)) {
    perror("[Can Utils] CAN bus Write error");
    return 0;
  }
  return 1;
}

int readBus(CanFrame *outFrame, int socketID, std::mutex& mutex)
{
    struct can_frame frame;

    // Reading on bus
    mutex.lock();
    int nbytes = read(socketID, &frame, sizeof(struct can_frame));
    mutex.unlock();
    if (nbytes < 0) {
        perror("[Can Utils] CAN bus Read error");
        return 0;
    }

  // Converting frame to strings
  sprint_canframe(outFrame, &frame);
  return nbytes;
}

I hope this make the question clearer and better focused.

walid barakat
  • 455
  • 1
  • 6
  • 17
boubside
  • 61
  • 2
  • 5
  • 1
    Please do not use the term "C/C++". There is no such language. They are 2 very different languages despite some similarities. Please also adjust tags of your question to match the chosen language. – Gerhardh Oct 06 '21 at 10:36
  • Library recommendations are offtopic on SO. But if you show the code you have problems with, suggestions to improve can ge given. – Gerhardh Oct 06 '21 at 10:37
  • @Gerhardh he won't show the code, as those working with Can/Canoe sw/hw usually work in automotive companies, that restrict them to publish their data outside. This is not a question for SO... He should go and debug some sw written by companies like Vector, Elektrobit, etc, etc. – alinsoar Oct 06 '21 at 10:42
  • Sockets means Linux or some such. So you can't have real-time performance, because it isn't a RTOS, period. However, 500ms delays sounds like a bug. – Lundin Oct 06 '21 at 10:48
  • @alinsoar There's no mentioning of Canoe or Vector anywhere in the post. – Lundin Oct 06 '21 at 10:50
  • Assuming you use a commercial CANopen protocol stack, then I'd use an interface by the same company. Then you don't end up in the middle of some "blame game" between Vector and IXXAT or whatever. Windows is more commonly used than Linux for PC GUI stuff in my experience. The various vendors of USB-to-CAN adapters will have a library and API for Windows. I have never done any CAN interfacing in Linux so I can't help you there. – Lundin Oct 06 '21 at 10:53
  • @Lundin "*Sockets means Linux or some such*" Never heard of *winsock*? – MatG Oct 06 '21 at 11:02
  • @MatG Sure. It implemented "Socket CAN"? Afaik thats some open source lib for Linux. This aint TCP/IP sockets. – Lundin Oct 06 '21 at 11:10
  • You're using sockets and threads (OS not specified). Without some code snippets to reason about it's very difficult to give some advice – MatG Oct 06 '21 at 11:10
  • @Lundin On sockets you send/receive strings, their meaning is handled on a higher level – MatG Oct 06 '21 at 11:14
  • @Lundin No, this is my statement. He should go and consult the software that programs canoe, as canoe works fast. – alinsoar Oct 06 '21 at 11:39
  • @alinsoar ...or IXXAT, or Kvaser, or... :) But yeah these will often communicate directly with a driver. Though I've used some cheaper brands too where some FTDI chip just translates incoming data as a virtual COM port and then you use the Windows API instead. Not a problem either, since there's hardware buffers. – Lundin Oct 06 '21 at 11:46
  • Thanks for all your comments ! I've edited the question to hopefully make it clearer. – boubside Oct 06 '21 at 13:03
  • You have only one thread for read and one thread for write. Do you use the same mutex for both directions? Can't the CAN driver handle both directions in parallel? – Gerhardh Oct 06 '21 at 13:43
  • @Gerhardh yes, I have only one thread for each direction. Messages are dispatched at higher level. I also use the same mutex for both read and write. From what I understand, the underlying library is socketCAN, I have no idea if it can handle both directions safely without a mutex protection, I will have a look into that. Honestly I'm quite new to this, and I lack some understanding. – boubside Oct 06 '21 at 14:07
  • you might find the following link useful: [canopen](https://www.can-cia.org/fileadmin/resources/documents/proceedings/2012_kleine-budde.pdf) – user3629249 Oct 06 '21 at 20:25
  • you might find the following link useful [using can from linux](https://www.can-cia.org/fileadmin/resources/documents/proceedings/2012_kleine-budde.pd) – user3629249 Oct 06 '21 at 20:43
  • @user3629249 thanks for the help. Your second links lead to an error 404 for me... – boubside Oct 07 '21 at 11:23

1 Answers1

0

Thanks to all comments on the question, I was able to solve the speed issue. I was not aware that SocketCan supports multiple socket on the same bus. Being able to create per process sockets for read and write allowed to reduce talk to the bus in a distributed manner, greatly improving speed.

boubside
  • 61
  • 2
  • 5