SocketCAN is a powerful tool for creating CAN Bus oriented applications on Linux. Popular choices for interfacing with SocketCAN are of course C/C++ or Python using the python-can package. As a huge fan of both SocketCAN and C#, I challenged myself to write the current Wikipedia example entirely in C#. In order to achieve this, .NET has Platform Invocation Services (P/Invoke) that allows managed code to call unmanaged code. For example, operating system libraries or 3rd party libraries written in an unmanaged language like C/C++ can be accessed by a managed .NET language such as C# using P/Invoke. This has come up in my professional career of developing vehicle network communication tools where many vehicle communication libraries are typically written in C or C++. P/Invoke can be leveraged in order to bridge the gap between an application written entirely in C# (and optionally XAML) and an unmanaged device API.
Here is the SocketCAN sample code from Wikipedia that I will be porting to C#:
int main(void) { int s; int nbytes; struct sockaddr_can addr; struct can_frame frame; struct ifreq ifr; const char *ifname = "vcan0"; if((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) == -1) { perror("Error while opening socket"); return -1; } strcpy(ifr.ifr_name, ifname); ioctl(s, SIOCGIFINDEX, &ifr); addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex; printf("%s at index %d\n", ifname, ifr.ifr_ifindex); if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) { perror("Error in socket bind"); return -2; } frame.can_id = 0x123; frame.can_dlc = 2; frame.data[0] = 0x11; frame.data[1] = 0x22; nbytes = write(s, &frame, sizeof(struct can_frame)); printf("Wrote %d bytes\n", nbytes); return 0; }
The SocketCAN code above makes use of several constants. There are constants for the Address Family, Protocol Family, the Socket Type, and later an ioctl SIOCGIFINDEX used for looking up an interface index by name. I have defined these constants in a simple static class.
public static class Constants { // Address Family CAN public const int AF_CAN = 29; // Per socket.h // Protocol Family CAN public const int PF_CAN = 29; // Per socket.h // Raw Protocol Interface public const int SOCK_RAW = 3; // Per socket_type.h // Socket Configuration Control: name -> if_index mapping public const int SIOCGIFINDEX = 0x8933; // Per ioctls.h }
SocketCAN also defines several different Protocol Types that can be used within the CAN Protocol Family. The Protocol Types combined with the Protocol Family and Socket Type constants above are what we need to pass into the socket
libc function. In this case it makes the most sense to utilize an enum in order to define the Protocol Types in C#.
public enum ProtocolType { /* RAW sockets */ CAN_RAW = 1, /* Broadcast Manager */ CAN_BCM = 2, /* VW Transport Protocol v1.6 */ CAN_TP16 = 3, /* VW Transport Protocol v2.0 */ CAN_TP20 = 4, /* Bosch MCNet */ CAN_MCNET = 5, /* ISO 15765-2 Transport Protocol */ CAN_ISOTP = 6, /* SAE J1939 */ CAN_J1939 = 7, CAN_NPROTO = 8, }
The ioctl
function in libc makes use of an interface request structure called ifreq
and is defined in if.h
.
struct ifreq { # define IFHWADDRLEN 6 # define IFNAMSIZ IF_NAMESIZE union { char ifrn_name[IFNAMSIZ]; /* Interface name, e.g. "en0". */ } ifr_ifrn; union { struct sockaddr ifru_addr; struct sockaddr ifru_dstaddr; struct sockaddr ifru_broadaddr; struct sockaddr ifru_netmask; struct sockaddr ifru_hwaddr; short int ifru_flags; int ifru_ivalue; int ifru_mtu; struct ifmap ifru_map; char ifru_slave[IFNAMSIZ]; /* Just fits the size */ char ifru_newname[IFNAMSIZ]; __caddr_t ifru_data; } ifr_ifru; };
The members we care about in each union are interface name and index:
# define ifr_name ifr_ifrn.ifrn_name /* interface name */ # define ifr_ifindex ifr_ifru.ifru_ivalue /* interface index */
The C# Ifreq
class below represents the above ifreq
struct in C. The StructLayout attribute is used to control the memory layout of the unmanaged type. The Sequential option takes the members in the managed type and places them in the same order as they appear in unmanaged memory. Additionally, we specify that the Name string needs to be marshaled as a fixed-length character array of size 16.
// Interface Request Structure used for Socket IOCTLs [StructLayout(LayoutKind.Sequential)] public class Ifreq { [MarshalAs(UnmanagedType.ByValTStr, SizeConst=16)] public string Name; public int IfIndex { get; set; } public Ifreq(string name) { Name = name; IfIndex = 0; } }
The bind
function in libc is passed a sockaddr_can
struct that specifies the address family and index for the CAN interface. There is also the ISO-TP CAN Transmit and Response ID information that we will not make use of in this demo, but it is necessary to be aware of these members for marshalling purposes.
struct sockaddr_can { sa_family_t can_family; int can_ifindex; union { /* transport protocol class address info (e.g. ISOTP) */ struct { canid_t rx_id, tx_id; } tp; /* reserved for future CAN protocols address information */ } can_addr; };
Creating the managed class counterpart for this struct is about as easy as it gets as all members of the C struct are integer-based. We specify the Sequential StructLayout to ensure the unmanaged type is properly laid out in memory.
[StructLayout(LayoutKind.Sequential)] public class SockAddrCan { public ushort CanFamily { get; set; } public int CanIfIndex { get; set; } public uint RxId { get; set; } public uint TxId { get; set; } }
The last struct we need to model is can_frame
which is what we will be sending to the write
function of libc.
struct can_frame { canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */ __u8 can_dlc; /* frame payload length in byte (0 .. 8) */ __u8 __pad; /* padding */ __u8 __res0; /* reserved / padding */ __u8 __res1; /* reserved / padding */ __u8 data[8] __attribute__((aligned(8))); };
The can_frame
is simple to model as well in C# with the exception that we have to specify that the Data byte array is marshaled as an array of elements of size 8. Again, we use Sequential StructLayout to dictate how the unmanaged type should be laid out in memory by using the order defined in the C# class counterpart.
[StructLayout(LayoutKind.Sequential)] public class CanFrame { public uint CanId { get; set; } public byte Dlc { get; set; } public byte Pad { get; set; } public byte Res0 { get; set; } public byte Res1 { get; set; } [MarshalAs(UnmanagedType.ByValArray, SizeConst=8)] public byte[] Data; public CanFrame() { Data = new byte[8]; } }
Lastly, there is the libc function calls socket
, ioctl
, bind
, and write
that we need to invoke. To do this we use the DllImport attribute and specify libc as the library we are using. We also set SetLastError to true in order to potentially utilize errno via Marshal.GetLastWin32Error.
[DllImport("libc", EntryPoint="socket", SetLastError=true)] public static extern IntPtr Socket(int addressFamily, int socketType, ProtocolType protocolType); [DllImport("libc", EntryPoint="ioctl", SetLastError=true)] public static extern int Ioctl(IntPtr socketHandle, int request, [In][Out] Ifreq ifreq); [DllImport("libc", EntryPoint="bind", SetLastError=true)] public static extern int Bind(IntPtr socketHandle, SockAddrCan addr, int addrSize); [DllImport("libc", EntryPoint="write", SetLastError=true)] public static extern int Write(IntPtr socketHandle, CanFrame frame, int frameSize);
Putting this altogether we get the following C# port of the original C code.
static void Main(string[] args) { IntPtr socketHandle = NativeMethods.Socket(Constants.PF_CAN, Constants.SOCK_RAW, ProtocolType.CAN_RAW); Console.WriteLine($"Socket Handle: {socketHandle}"); if (socketHandle != new IntPtr(-1)) { var ifr = new Ifreq("vcan0"); int ioctlResult = NativeMethods.Ioctl(socketHandle, Constants.SIOCGIFINDEX, ifr); Console.WriteLine($"Ioctl Return Code: {ioctlResult}"); if (ioctlResult != -1) { var addr = new SockAddrCan() { CanFamily = Constants.AF_CAN, CanIfIndex = ifr.IfIndex, }; Console.WriteLine($"{ifr.Name} at index {ifr.IfIndex}"); int bindResult = NativeMethods.Bind(socketHandle, addr, Marshal.SizeOf(typeof(SockAddrCan))); Console.WriteLine($"Bind Return Code: {bindResult}"); if (bindResult != -1) { var canFrame = new CanFrame() { CanId = 0x123, Dlc = 2, }; canFrame.Data[0] = 0x11; canFrame.Data[1] = 0x22; int nBytes = NativeMethods.Write(socketHandle, canFrame, Marshal.SizeOf(typeof(CanFrame))); Console.WriteLine($"Wrote {nBytes} bytes"); } } } }
Output from this application will look something like this:
Socket Handle: 27 Ioctl Return Code: 0 vcan0 at index 4 Bind Return Code: 0 Wrote 16 bytes
If we listen with candump on vcan0
and then run this program we will see the following:
candump vcan0 vcan0 123 [2] 11 22
Now that we have confirmed that the application outputs the correct CAN message out onto the bus, the porting process is complete. This code is a great start to incorporating SocketCAN into applications written in C# using .NET and what is laid out here can certainly be expanded upon. Thanks as always for reading. The C# source code from this article can be downloaded here.
Thank you for the article,
I was able to send test messages, but how I can use this method for listening of the massages from other devices?
I need to send some message amd wait for the answers.
Hi Dmitry, can-utils provides candump and cansniffer out of the box which are excellent for that purpose. Are you trying to listen to inter-ECU traffic or listen to another Tester Y-cabled into the vehicle DLC?
Amazing! With your example I’m able to write messages to the network but How to read the message from the C# program?
Hi Davide, I have developed a wrapper library that supports the majority of SocketCAN functionality from .NET: https://github.com/derek-will/SocketCANSharp
Using that library you can use the read / recv functions to read data from the CAN bus.
Hi Derek,
This is exactly which I was looking for. Thanks.
I want to consume CAN -Utils apis in my ASP.net core web application.
I have almost no experience with C files.
To be able to import these dlls, do I need to do anything extra?
Currently while running this piece of code for .net core 3.1 I get this error:
System.DllNotFoundException: ‘Unable to load DLL ‘libc’ or one of its dependencies: The specified module could not be found.
Can you help me out here?
Thanks
Deepika
Are you running the code on Linux?
Hi Derek,
Thank you for this useful post.
This post make me believe that Can-utils C library for linux can be used for C# .net core application.
Currently I want to develop a ASP.net core application which can consume all the APIS of CAN-UTILS same as you did for CANSEND.c
What do you think, to achieve this I need to write all consumer files in C# and do dllimport?
How do I achieve candump or receiving of message from CAN command on my .Net application?
Can you help me out here?
Hi Deepika, I actually went and wrote a full library for using SocketCAN in .NET.
https://github.com/derek-will/SocketCANSharp
I have an example application in that repo that shows how one can go about writing a bus analyzer in C# that leverages SocketCAN.
Derek,
Did you compare the cpu load of your socket can library with the CanRaw one in the Iot.DeviceSocketCan library?
Hi Derek,
This looks really interesting. I’m interested in having a cross platform application (windows and Ubuntu) using either CAN Bus or Modbus. I was hoping to use C#. It seams like there is probably a lot out there for modbus and not allot for CAN. This looks really interesting. Would SocketCANSharp work for an application that needed to run in windows and Linux? Or would it only work in Linux? I have never used CAN before so I’m not sure how long it would take me to figure this out. I’m hoping I can take the visual studio published app and run it directly on windows or linux.
Would SocketCanSharp run this way? Would you recommend another solution?
Looks really interesting.
Best Regards,
Jeff
Hello Mr. Will,
I am currently doing my Master thesis. I want to develop a HMI based on Raspberry pi and can bus protocol and an inverter. I have added your library SocketCANSharp in my project. I can now read values from the inverter through the CAN BUS and now I want to write over the can bus and I don’t know how can I program this, programming with C#.
can you please help me
with kind regards
Hasnae
Translated with http://www.DeepL.com/Translator (free version)
Hi Derek
I can now read values through can bus using your library SocketCANSharp and now want to write values through can bus. do you have any idea how i can do this while i am programming nit c#
Hasnae