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.