Recent

Author Topic: CAN-BUS SocketCAN  (Read 22505 times)

avra

  • Hero Member
  • *****
  • Posts: 2128
    • Additional info
Re: CAN-BUS SockectCAN
« Reply #45 on: March 12, 2021, 07:53:40 am »
@Avra: Many thanks.
You're most welcome. I'm glad it's of use to others.  :P

Wouldn't it be better to upload your work to Bitbucket, Github, ..., and make an own repo for that?
This is just a temporary public playground. My final goal is to make SocketCAN header translations and demos integral part of FreePascal, so that CAN works out of the box without downloading anything additional. Like Python and C already have.

So far my development and testing is on Debian x86-64 via vcan, using can-utils and Kayak. I will probably also test it on Manjaro x86-64. While I do intend to test it with real hardware on Raspbery Pi and PocketBeagle in the future, I would appreciate any testing feedback I can get now. Especially on 32 and 64 bit ARM - even with just vcan.
« Last Edit: March 12, 2021, 01:41:17 pm by avra »
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

avra

  • Hero Member
  • *****
  • Posts: 2128
    • Additional info
Re: CAN-BUS SockectCAN
« Reply #46 on: March 15, 2021, 02:43:27 pm »
Here is SocketCAN BCM cyclic demo where you let kernel take care of sending interval. Enjoy!  :D

Code: Pascal  [Select][+][-]
  1. //////////////////////////////////////////////////////////////////////////////////
  2. //                                                                              //
  3. //  This is a SocketCAN BCM cyclic demo created by Zeljko Avramovic             //
  4. //  FPC modified LGPL licence: https://wiki.freepascal.org/FPC_modified_LGPL    //
  5. //                                                                              //
  6. //  Virtual CAN adapter vcan0 is hard coded and you can bring it up like this:  //
  7. //  sudo modprobe vcan                                                          //
  8. //  sudo ip link add dev vcan0 type vcan                                        //
  9. //  sudo ip link set vcan0 mtu 72            # needed for CAN FD                //
  10. //  sudo ip link set vcan0 up                                                   //
  11. //                                                                              //
  12. //////////////////////////////////////////////////////////////////////////////////
  13.  
  14. program cancyclic;
  15.  
  16. {$mode delphi} {$H+}
  17.  
  18. uses
  19.   cthreads, classes, sysutils, sockets, baseunix, crt, can, can.bcm;
  20.  
  21. type
  22.   bcm_msg = record
  23.     head: bcm_msg_head_without_frames; // we shall use more then 1 frame so bcm_msg_head is not enough
  24.     frame: array[0..2] of can_frame;   // and we add here space for our 3 frames
  25.   end;
  26.  
  27. procedure perror(const S : string);
  28. begin
  29.   WriteLn(S, ', SocketError = ', SocketError);
  30. end;
  31.  
  32. var
  33.   s, i: cint;
  34.   addr: sockaddr_can;
  35.   ifr:  ifreq;
  36.   msg:  bcm_msg;
  37. begin
  38.   try
  39.     Writeln('CAN Sockets BCM Cyclic Demo');
  40.     s := fpsocket(PF_CAN, SOCK_DGRAM, CAN_BCM);
  41.     if s < 0 then
  42.     begin
  43.       perror('Socket');
  44.       Exit;
  45.     end;
  46.  
  47.     strcopy(ifr.ifr_name, 'vcan0'); // ifr.ifr_name := 'vcan0' + #0;
  48.     if fpioctl(s, SIOCGIFINDEX, @ifr) < 0 then
  49.     begin
  50.       perror('IOctl');
  51.       Exit;
  52.     end;
  53.  
  54.     memset(@addr, 0, sizeof(addr));
  55.     addr.can_family  := AF_CAN;
  56.     addr.can_ifindex := ifr.ifr_ifindex;
  57.  
  58.     if fpconnect(s, @addr, sizeof(addr)) < 0 then
  59.     begin
  60.       perror('Connect');
  61.       Exit;
  62.     end;
  63.  
  64.     msg.head.opcode        := TX_SETUP;
  65.     msg.head.can_id        := $567; // if TX_CP_CAN_ID flag is set then all messages shall have this CAN ID
  66.     msg.head.flags         := SETTIMER OR STARTTIMER OR TX_ANNOUNCE {OR TX_CP_CAN_ID} {OR TX_RESET_MULTI_IDX};
  67.     msg.head.nframes       := 3;
  68.     msg.head.count         := 0; // single interval (use only ival2)
  69.     msg.head.ival1.tv_sec  := 0;
  70.     msg.head.ival1.tv_usec := 0;
  71.     msg.head.ival2.tv_sec  := 0;
  72.     msg.head.ival2.tv_usec := 250000; // 250 ms interval between CAN messages
  73.     msg.frame[0].can_id    := $550; // not used if TX_CP_CAN_ID falg is set
  74.     msg.frame[0].can_dlc   := 2;
  75.     msg.frame[0].data      := [$01,$02,$03,$04,$05,$06,$07,$08];
  76.     msg.frame[1].can_id    := $551; // not used if TX_CP_CAN_ID falg is set
  77.     msg.frame[1].can_dlc   := 4;
  78.     msg.frame[1].data      := [$11,$12,$13,$14,$15,$16,$17,$18];
  79.     msg.frame[2].can_id    := $552; // not used if TX_CP_CAN_ID falg is set
  80.     msg.frame[2].can_dlc   := 8;
  81.     msg.frame[2].data      := [$21,$22,$23,$24,$25,$26,$27,$28];
  82.  
  83.     if fpwrite(s, msg, sizeof(msg)) <> sizeof(msg) then
  84.     begin
  85.       perror('Write');
  86.       Exit;
  87.     end;
  88.  
  89.     repeat
  90.       WriteLn('Press any key to stop...');
  91.       msg.frame[2].DataByte[0] := msg.frame[2].DataByte[0] + 1;
  92.       for i := 0 to msg.frame[2].can_dlc - 1 do
  93.         Write(Format('%02.2X ', [msg.frame[2].data[i]]));
  94.       WriteLn('');
  95.       WriteLn('');
  96.       fpwrite(s, msg, sizeof(msg)); // comment out this line if you do not want to publish changed frame[2]
  97.       Delay(2000); // loop every 2 seconds
  98.     until KeyPressed;
  99.  
  100.     if fpclose(s) < 0 then
  101.     begin
  102.       perror('Close');
  103.       Exit;
  104.     end;
  105.  
  106.   except
  107.     on e:Exception do
  108.       WriteLn(e.Message);
  109.   end;
  110. end.
  111.  
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

radek

  • Newbie
  • Posts: 2
Re: CAN-BUS SockectCAN
« Reply #47 on: March 16, 2021, 01:01:29 am »
Hi,
I apologize in advance for my English, I am also interested in the implementation of CAN for Lazarus.

I wonder what the performance difference will be if I use:
1) This solution via SocketCAN
2) Direct control of IC MCP2518 via SPI bus using Lazarus
3) Direct control of IC MCP2518 via USB using IC MCP2210 (USB to SPI driver) libusb

I'm interested because I want the application to work under both Windows and Linux. When I use MCP2210 and MCP2518, I can control the CAN driver via SPI directly in Windows and Linux. If I use SocketCAN, then I have to implement another solution for Windows.

avra

  • Hero Member
  • *****
  • Posts: 2128
    • Additional info
Re: CAN-BUS SockectCAN
« Reply #48 on: March 16, 2021, 08:35:47 am »
I am also interested in the implementation of CAN for Lazarus.
Nice to hear  ;)

I wonder what the performance difference will be if I use:
1) This solution via SocketCAN
2) Direct control of IC MCP2518 via SPI bus using Lazarus
3) Direct control of IC MCP2518 via USB using IC MCP2210 (USB to SPI driver) libusb
Any modern PC or SBC CPU is capable of handling CAN without problems. Bottleneck is communication channel. In case of Lawicel/SLCAN/CAN232 it is serial communication - if you use classic AVR ATMEGA328 based Arduino it is 115200 (STM32 or ESP32 based ones may use speeds higher then that so they do not have that problem), you don't use filters and want to see all of high speed CAN traffic then expect to miss frames here and there. Using MCP2518 direct via SPI on Raspberry Pi or PocketBeagle for CAN and CAN FD is solid and comfortable. I haven't used MCP2518 over MCP2210 but I guess that might also skip frames. SocketCAN is just a Berkeley sockets abstraction layer over any Linux CAN hardware, and you use it the same with both SPI MCP2518 and Lawicel/SLCAN/CAN232 (there is no driver for MCP2518 over MCP2210). So, performance does not suffer if you use SocketCAN.

I'm interested because I want the application to work under both Windows and Linux. When I use MCP2210 and MCP2518, I can control the CAN driver via SPI directly in Windows and Linux. If I use SocketCAN, then I have to implement another solution for Windows.
Well, you said it all. Since you want your app to be used on both Windows and Linux, unfortunately you need to give up SocketCAN (unless someone implements it under Windows). Your best bet would be to use some STM32 or ESP32 Lawicel/SLCAN/CAN232 using higher serial speeds, which would probably avoid frame dropping unless you use it on a very, very high traffic 1Mbit CAN.

Also take a look at this discussion: https://forum.lazarus.freepascal.org/index.php/topic,51477.msg377993.html#msg377993
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

radek

  • Newbie
  • Posts: 2
Re: CAN-BUS SockectCAN
« Reply #49 on: March 16, 2021, 12:19:30 pm »
Just to add, the MCP2210 is a High Speed USB HID driver for the SPI bus with speeds from 1500bps to 12Mbps, so there will be no bottleneck. The only problem is that I need to constantly read the 64 byte register to see if the MCP2518 has received any data, this may cause some CPU usage.

avra

  • Hero Member
  • *****
  • Posts: 2128
    • Additional info
Re: CAN-BUS SockectCAN
« Reply #50 on: March 16, 2021, 02:08:42 pm »
Just to add, the MCP2210 is a High Speed USB HID driver for the SPI bus with speeds from 1500bps to 12Mbps, so there will be no bottleneck. The only problem is that I need to constantly read the 64 byte register to see if the MCP2518 has received any data, this may cause some CPU usage.
It doesn't matter how fast you set it's SPI to talk to CAN chip. It seams to use USB HID, which is limited to 1000 messages per second where each message can have up to 64 bytes. You do the math. Ah yes, and while you are transferring data over USB you can not get new CAN messages.

https://github.com/daniel-santos/mcp2210-linux/issues/11
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

joelucsd

  • Newbie
  • Posts: 2
Re: CAN-BUS SockectCAN
« Reply #51 on: March 23, 2021, 10:23:02 pm »
Thank you for the work to get the CANsocket working.  I pulled in the files from can.zip and it worked.  I am using an older version of FPC on the raspberry Pi.  The example worked.  I used that code as a template for my Lazarus project.

I needed an Extended ID.  Will look at that tomorrow.  Might just need to set the extended ID manually before stuffing the value into the frame field.

Regards,
Joel

avra

  • Hero Member
  • *****
  • Posts: 2128
    • Additional info
Re: CAN-BUS SockectCAN
« Reply #52 on: March 25, 2021, 08:28:09 am »
Thank you for the work to get the CANsocket working.
You're most welcome.  ;)

I pulled in the files from can.zip and it worked.  I am using an older version of FPC on the raspberry Pi.  The example worked.  I used that code as a template for my Lazarus project.
Thank you for the report. I didn't catch time to test on Pi and PocketBeagle yet, so I'm glad to hear it works on Pi. Just for info, could you tell me what CAN hardware did you use during testing?

I needed an Extended ID.  Will look at that tomorrow.  Might just need to set the extended ID manually before stuffing the value into the frame field.
So far I have attached enough for 1:1 translation of various C examples you can find on the net. Right now I am working on bit packed overlays which would allow easy access to individual bits, and some higher level helper methods. After that I will probably add SAE J1939 (for trucks, buses, tractors, boats and heavy machinery) where Extended ID is used, and at the end provide a FPC patch so that we can all use SocketCAN out of the box.
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

joelucsd

  • Newbie
  • Posts: 2
Re: CAN-BUS SocketCAN
« Reply #53 on: March 25, 2021, 06:00:16 pm »
I am running on a Raspberry Pi 4.
Raspbian GNU/Linux 10
FPC version is: 3.0.4+dfsg-22+rpi1
Lazarus: 2.0.0+dfsg-2   2019-03-01
I am using a USB to CAN adapter from www.inno-maker.com.  It is a nice cheap solution.
http://wiki.inno-maker.com/display/HOMEPAGE/usb2can
I got mine through Amazon
You do have to enable it with a couple of commands:
'sudo ip link set can0 type can bitrate 250000'
'sudo ifconfig can0 up'

I did get extended addressing to work.  Like I suggested, I just had to set bit 31 to select it before writing the ID into the frame structure.  Then it came out on the bus.  The Python library has an additional parameter in the call to say it is the 29 bit version of the ID.  Setting the bit manually before works fine.  You just need to know to do it.  (And I don't like the Python syntax.  So would rather use Pascal or C)
« Last Edit: March 25, 2021, 06:11:16 pm by joelucsd »

avra

  • Hero Member
  • *****
  • Posts: 2128
    • Additional info
Re: CAN-BUS SocketCAN
« Reply #54 on: March 26, 2021, 10:18:54 am »
I am running on a Raspberry Pi 4.
Raspbian GNU/Linux 10
FPC version is: 3.0.4+dfsg-22+rpi1
Lazarus: 2.0.0+dfsg-2   2019-03-01
I am using a USB to CAN adapter from www.inno-maker.com.
Thanks for the report. I guess with 3.0.4 you had to manually add missing CAN constants.

You do have to enable it with a couple of commands:
'sudo ip link set can0 type can bitrate 250000'
'sudo ifconfig can0 up'
That's pretty standard for a CAN device that kernel can recognize.

I did get extended addressing to work.  Like I suggested, I just had to set bit 31 to select it before writing the ID into the frame structure.
I know. That's why I said "I am working on bit packed overlays which would allow easy access to individual bits".  ;)
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

avra

  • Hero Member
  • *****
  • Posts: 2128
    • Additional info
Re: CAN-BUS SocketCAN
« Reply #55 on: March 31, 2021, 11:14:03 am »
I have implemented high level CAN access in can.hl unit. So, if you do your own code from scratch it is recommended to use this unit. Old low level C-like access will stay for easier future upgrades, and you will probably use it only when you want 1:1 translation of some C example from the net. In the attachment you will find new CAN examples hlcancyclic, hlcanfdreceive, hlcanfdtransmit, hlcanfilter, hlcanreceive and hlcantransmit. These are all "high level" versions of previous examples using can.hl unit (if you can give "high level" name to pascalish renaming, bit packed overlays and some helpers).

I hope you will find CAN usage more pleasant now.

@joelucsd: Take a look at hlcanfdtransmit which uses extended 29-bit CAN ID addressing now. It is as simple as this:
Code: Pascal  [Select][+][-]
  1.     CanFDFrame.ID.Value := $444444; // CAN FD message extended ID
  2.     CanFDFrame.ID.EFF   := true;    // confirm that we have 29-bit CAN ID

Talk is cheap. Here is what CAN code looks like now:
Code: Pascal  [Select][+][-]
  1. program hlcanfdtransmit; // higher level pascalized version of canfdtransmit demo
  2.  
  3. {$mode delphi} {$H+}
  4.  
  5. uses
  6.   cthreads, sysutils, sockets, baseunix, can, can.hl, can.raw;
  7.  
  8. procedure perror(const S : string);
  9. begin
  10.   WriteLn(S, ', SocketError = ', SocketError);
  11. end;
  12.  
  13. var
  14.   Err:          integer;
  15.   CanHandle:    TCanHandle;
  16.   CanAddr:      TCanSockAddr;
  17.   CanInterface: TCanIfreq;
  18.   CanFDFrame:   TCanFDFrame;
  19. begin
  20.   try
  21.     Writeln('CAN FD Sockets Transmit Demo');
  22.     CanHandle := CanSocket(SOCK_RAW, CAN_RAW);
  23.     if CanHandle < 0 then
  24.     begin
  25.       perror('Socket');
  26.       Exit;
  27.     end;
  28.  
  29.     CanInterface.Name := 'vcan0' + #0;
  30.     if CanIOCtl(CanHandle, SIOCGIFINDEX, CanInterface) < 0 then
  31.     begin
  32.       perror('IOctl');
  33.       Exit;
  34.     end;
  35.  
  36.     CanAddr.Clear;
  37.     CanAddr.CanFamily  := AF_CAN;
  38.     CanAddr.CanIfIndex := CanInterface.IfIndex;
  39.  
  40.     if CanBind(CanHandle, CanAddr) < 0 then
  41.     begin
  42.       perror('Bind');
  43.       Exit;
  44.     end;
  45.  
  46.     CanFDFrame.ID.Value := $444444; // CAN FD message extended ID
  47.     CanFDFrame.ID.EFF   := true;    // confirm that we have 29-bit CAN ID
  48.     CanFDFrame.Len      := 20;      // payload length will be 20 bytes
  49.     CanFDFrame.Data     := [$01, $02, $03, $04, $05, $06, $07, $08, $09, $0A, $0B, $0C, $0D, $0E, $0F, $10,
  50.                             $11, $12, $13, $14, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
  51.                             $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
  52.                             $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00];
  53.                             // shows as '0x444444 [40] 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14' in canfdreceive
  54.     {
  55.     CanFDFrame.Data[0]  := $01; // alternative for the above
  56.     CanFDFrame.Data[1]  := $02; // shows as '0x444444 [40] 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14' in canfdreceive
  57.     CanFDFrame.Data[2]  := $03;
  58.     CanFDFrame.Data[3]  := $04;
  59.     CanFDFrame.Data[4]  := $05;
  60.     CanFDFrame.Data[5]  := $06;
  61.     CanFDFrame.Data[6]  := $07;
  62.     CanFDFrame.Data[7]  := $08;
  63.     CanFDFrame.Data[8]  := $09;
  64.     CanFDFrame.Data[9]  := $0A;
  65.     CanFDFrame.Data[10] := $0B;
  66.     CanFDFrame.Data[11] := $0C;
  67.     CanFDFrame.Data[12] := $0D;
  68.     CanFDFrame.Data[13] := $0E;
  69.     CanFDFrame.Data[14] := $0F;
  70.     CanFDFrame.Data[15] := $10;
  71.     CanFDFrame.Data[16] := $11;
  72.     CanFDFrame.Data[17] := $12;
  73.     CanFDFrame.Data[18] := $13;
  74.     CanFDFrame.Data[19] := $14;
  75.     }
  76.  
  77.     Err := CanSetSockOpt(CanHandle, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, ENABLE_CAN_OPTION, SizeOf(ENABLE_CAN_OPTION));
  78.     if Err <> 0 then
  79.     begin
  80.       if Err = ESysENOPROTOOPT then
  81.         WriteLn('CAN FD Protocol not available. Try newer kernel');
  82.       perror('SetSockopt');
  83.       Exit;
  84.     end;
  85.  
  86.     if CanWrite(CanHandle, CanFDFrame) <> SizeOf(CanFDFrame) then
  87.     begin
  88.       perror('Write');
  89.       Exit;
  90.     end;
  91.  
  92.     if CanClose(CanHandle) < 0 then
  93.     begin
  94.       perror('Close');
  95.       Exit;
  96.     end;
  97.  
  98.   except
  99.     on e: Exception do
  100.       WriteLn(e.Message);
  101.   end;
  102. end.
  103.  

Code: Pascal  [Select][+][-]
  1. program hlcanfdreceive; // higher level pascalized version of canfdreceive demo
  2.  
  3. {$mode delphi} {$H+}
  4.  
  5. uses
  6.   cthreads, sysutils, sockets, baseunix, can, can.hl, can.raw;
  7.  
  8. procedure perror(const S : string);
  9. begin
  10.   WriteLn(S, ', SocketError = ', SocketError);
  11. end;
  12.  
  13. var
  14.   i, BytesNo:   byte;
  15.   Err:          integer;
  16.   CanHandle:    TCanHandle;
  17.   CanAddr:      TCanSockAddr;
  18.   CanInterface: TCanIfreq;
  19.   CanFDFrame:   TCanFDFrame;
  20. begin
  21.   try
  22.     Writeln('CAN FD Sockets Receive Demo');
  23.     CanHandle := CanSocket(SOCK_RAW, CAN_RAW);
  24.     if CanHandle < 0 then
  25.     begin
  26.       perror('Socket');
  27.       Exit;
  28.     end;
  29.  
  30.     CanInterface.Name := 'vcan0' + #0;
  31.     if CanIOCtl(CanHandle, SIOCGIFINDEX, CanInterface) < 0 then
  32.     begin
  33.       perror('IOctl');
  34.       Exit;
  35.     end;
  36.  
  37.     CanAddr.Clear;
  38.     CanAddr.CanFamily  := AF_CAN;
  39.     CanAddr.CanIfIndex := CanInterface.IfIndex;
  40.  
  41.     if CanBind(CanHandle, CanAddr) < 0 then
  42.     begin
  43.       perror('Bind');
  44.       Exit;
  45.     end;
  46.  
  47.     Err := CanSetSockOpt(CanHandle, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, ENABLE_CAN_OPTION, SizeOf(ENABLE_CAN_OPTION));
  48.     if Err <> 0 then
  49.     begin
  50.       if Err = ESysENOPROTOOPT then
  51.         WriteLn('CAN FD Protocol not available. Try newer kernel.');
  52.       perror('SetSockopt');
  53.       Exit;
  54.     end;
  55.  
  56.     BytesNo := CanRead(CanHandle, CanFDFrame);
  57.     if BytesNo < 0 then
  58.     begin
  59.       perror('Read');
  60.       Exit;
  61.     end;
  62.  
  63.     if BytesNo = CANFD_MTU then    // test CAN FD with 'cansend vcan0 123##1223344556677889900'
  64.       WriteLn('CAN FD frame received with length ', IntToStr(CanFDFrame.Len))
  65.     else if BytesNo = CAN_MTU then // test legacy CAN with 'cansend vcan0 456#DEADBEEF'
  66.       WriteLn('CAN legacy frame received with length ', IntToStr(CanFDFrame.Len))
  67.     else
  68.       WriteLn('Invalid CAN(FD) frame received');
  69.  
  70.     Write(format('0x%03X [%d] ', [CanFDFrame.ID.Value, CanFDFrame.Len]));
  71.     for i := 0 to CanFDFrame.Len - 1 do
  72.       Write(Format('%02.2X ', [CanFDFrame.Data[i]]));
  73.     WriteLn('');
  74.  
  75.     if CanClose(CanHandle) < 0 then
  76.     begin
  77.       perror('Close');
  78.       Exit;
  79.     end;
  80.  
  81.   except
  82.     on e: Exception do
  83.       WriteLn(e.Message);
  84.   end;
  85. end.
  86.  

Hopefully, J1939 examples are next.

Remember, this is all still a playground and things may change until I include SocketCAN support into FP trunk.
« Last Edit: April 03, 2021, 10:41:55 am by avra »
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

avra

  • Hero Member
  • *****
  • Posts: 2128
    • Additional info
Re: CAN-BUS SocketCAN
« Reply #56 on: April 16, 2021, 02:20:52 pm »
I've been thinking about creating some unique CAN tools. There are so many tools for sniffing, dumping and hacking your car CAN communication, but very few touch showing CAN error message details. The ones which do, show just a few selected messages, and there is none that shows all CAN error messages with their finest details in human readable form. So, I have created hlcanerrdump. Quite a unique tool created with FreePascal. Enjoy!

Code: Pascal  [Select][+][-]
  1. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2. //                                                                                                //
  3. //  hlcanerrdump utility to display SocketCAN error messages, by Zeljko Avramovic (c) 2021        //
  4. //                                                                                                //
  5. //  Dual license:                                                                                 //
  6. //  1. FPC modified LGPL (chosen for compatibility with FreePascal and Lazarus)                   //
  7. //  2. BSD3              (chosen for compatibility with everything else)                          //
  8. //                                                                                                //
  9. //  Virtual CAN adapter vcan0 is hard coded and you can bring it up like this:                    //
  10. //  sudo modprobe vcan                                                                            //
  11. //  sudo ip link add dev vcan0 type vcan                                                          //
  12. //  sudo ip link set vcan0 mtu 72              # needed for CAN FD                                //
  13. //  sudo ip link set vcan0 up                                                                     //
  14. //                                                                                                //
  15. //  To simulate error messages use hlcanerrsim utility like this:                                 //
  16. //  ./hlcanerrsim LostArbitrationBit=09 TX BusOff Data7=AA Data6=BB                               //
  17. //                                                                                                //
  18. //  That should show in hlcanerrdump utility as:                                                  //
  19. //  0x04A [8] 16 00 80 00 00 00 BB AA  ERR=LostArbitrationBit09,BusOff,Prot(Type(TX),Loc(Unspec)) //
  20. //                                                                                                //
  21. //  Alternatively, you could use candump from can-utils to check error messages like this:        //
  22. //  candump -s -c -c -x -a any,0~0,#FFFFFFFF                                                      //
  23. //                                                                                                //
  24. ////////////////////////////////////////////////////////////////////////////////////////////////////
  25.  
  26. program hlcanerrdump; // dump CAN error messages
  27.  
  28. {$mode delphi} {$H+}
  29.  
  30. uses
  31.   classes, sysutils, strutils, sockets, baseunix, can, can.hl, can.raw, can.error, crt;
  32.  
  33. procedure perror(const S : string);
  34. begin
  35.   WriteLn(S, ', SocketError = ', SocketError);
  36. end;
  37.  
  38. var
  39.   i, BytesNo:   integer;
  40.   CanHandle:    TCanHandle;
  41.   CanAddress:   TCanSockAddress;
  42.   CanInterface: TCanIFreq;
  43.   CanFrame:     TCanFrame;
  44.   CanMask:      TCanErrMask;
  45.   CanFilter:    TCanFilter;
  46.   SockOpt:      cint;
  47.   ErrFlagStr:   string;
  48.   Errors:       TStringList;
  49. begin
  50.   try
  51.     try
  52.       WriteLn('CAN Sockets Error Messages Dump. Waiting for errors...');
  53.       Errors    := TStringList.Create;
  54.       CanHandle := CanSocket(SOCK_RAW, CAN_RAW);
  55.       if CanHandle < 0 then
  56.       begin
  57.         perror('Socket');
  58.         Exit;
  59.       end;
  60.  
  61.       CanInterface.Name := 'vcan0';
  62.       if CanIOCtl(CanHandle, SIOCGIFINDEX, CanInterface) < 0 then
  63.       begin
  64.         perror('IOctl');
  65.         Exit;
  66.       end;
  67.  
  68.       CanAddress.Clear;
  69.       CanAddress.CanFamily  := AF_CAN;
  70.       CanAddress.CanIfIndex := CanInterface.IfIndex;
  71.  
  72.       if CanBind(CanHandle, CanAddress) < 0 then
  73.       begin
  74.         perror('Bind');
  75.         Exit;
  76.       end;
  77.  
  78.       CanFilter.ID.InverseFilter              := true; // no normal CAN frames
  79.       CanFilter.Mask.Full                     := 0;    // inverting nothing gives us everything
  80.       // CanFilter.Mask.Error.LostArbitration := true; // you might want to exclude arbitration errors
  81.       CanSetSockOpt(CanHandle, SOL_CAN_RAW, CAN_RAW_FILTER, CanFilter, SizeOf(CanFilter));
  82.  
  83.       CanMask.ERR := true;                // only error frames
  84.       CanSetSockOpt(CanHandle, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, CanMask, SizeOf(CanMask));
  85.  
  86.       SockOpt := CanFcntl(CanHandle, F_GETFL);                        // get socket options
  87.       if SockOpt < 0 then
  88.       begin
  89.         perror('CanFcntl: Error = ' + CanGetErrNo.ToString);
  90.         Exit;
  91.       end;
  92.  
  93.       if CanFcntl(CanHandle, F_SETFL, SockOpt or O_NONBLOCK) < 0 then // set nonblock socket (to be able to exit after key press)
  94.       begin
  95.         perror('NonBlock: Error = ' + CanGetErrNo.ToString);
  96.         Exit;
  97.       end;
  98.  
  99.       repeat
  100.         BytesNo := CanRead(CanHandle, CanFrame);
  101.         if BytesNo = SizeOf(CanFrame) then
  102.         begin
  103.           Write(Format(IfThen(CanFrame.ID.EFF, '0x%08.8X [%d] ', '     0x%03.3X [%d] '), [CanFrame.ID.Value, CanFrame.DLC])); // extended or standard frame
  104.           for i := 0 to CanFrame.DLC - 1 do
  105.             Write(Format('%02.2X ', [CanFrame.Data[i]]));
  106.  
  107.           Errors.Clear;
  108.           if CanFrame.ID.Mask.Error.TxTimeout       then Errors.Add('TxTimeout');
  109.           if CanFrame.ID.Mask.Error.LostArbitration then Errors.Add('LostArbitrationBit' + Format('%.*d', [2, CanFrame.DataError.LostArbitration.BitNumber]));
  110.           if CanFrame.ID.Mask.Error.NoAck           then Errors.Add('NoAck');
  111.           if CanFrame.ID.Mask.Error.BusOff          then Errors.Add('BusOff');
  112.           if CanFrame.ID.Mask.Error.BusError        then Errors.Add('BusError');
  113.           if CanFrame.ID.Mask.Error.Restarted       then Errors.Add('Restarted');
  114.           if CanFrame.ID.Mask.Error.Controller      then
  115.           begin
  116.             ErrFlagStr := '';
  117.             if CanFrame.DataError.Controller.OverflowRX then ErrFlagStr := ErrFlagStr + 'OverflowRX,';
  118.             if CanFrame.DataError.Controller.OverflowTX then ErrFlagStr := ErrFlagStr + 'OverflowTX,';
  119.             if CanFrame.DataError.Controller.WarningRX  then ErrFlagStr := ErrFlagStr + 'WarningRX,';
  120.             if CanFrame.DataError.Controller.WarningTX  then ErrFlagStr := ErrFlagStr + 'WarningTX,';
  121.             if CanFrame.DataError.Controller.PassiveRX  then ErrFlagStr := ErrFlagStr + 'PassiveRX,';
  122.             if CanFrame.DataError.Controller.PassiveTX  then ErrFlagStr := ErrFlagStr + 'PassiveTX,';
  123.             if CanFrame.DataError.Controller.Active     then ErrFlagStr := ErrFlagStr + 'Active';
  124.             if ErrFlagStr.EndsWith(',') then SetLength(ErrFlagStr, ErrFlagStr.Length - 1);
  125.             Errors.Add('Ctrl(' + ErrFlagStr + ')');
  126.           end;
  127.           if CanFrame.ID.Mask.Error.Protocol        then
  128.           begin
  129.             ErrFlagStr := 'Type(';
  130.             if CanFrame.DataError.Protocol.Category.SingleBit          then ErrFlagStr := ErrFlagStr + 'SingleBit,';
  131.             if CanFrame.DataError.Protocol.Category.FrameFormat        then ErrFlagStr := ErrFlagStr + 'FrameFormat,';
  132.             if CanFrame.DataError.Protocol.Category.BitStuffing        then ErrFlagStr := ErrFlagStr + 'BitStuffing,';
  133.             if CanFrame.DataError.Protocol.Category.Bit0               then ErrFlagStr := ErrFlagStr + 'Bit0,';
  134.             if CanFrame.DataError.Protocol.Category.Bit1               then ErrFlagStr := ErrFlagStr + 'Bit1,';
  135.             if CanFrame.DataError.Protocol.Category.BusOverload        then ErrFlagStr := ErrFlagStr + 'BusOverload,';
  136.             if CanFrame.DataError.Protocol.Category.ActiveAnnouncement then ErrFlagStr := ErrFlagStr + 'ActiveAnnouncement,';
  137.             if CanFrame.DataError.Protocol.Category.TX                 then ErrFlagStr := ErrFlagStr + 'TX';
  138.             if ErrFlagStr.EndsWith(',') then SetLength(ErrFlagStr, ErrFlagStr.Length - 1);
  139.             ErrFlagStr := ErrFlagStr + '),Loc(';
  140.             case CanFrame.DataError.Protocol.Location of
  141.               CAN_ERR_PROT_LOC_UNSPEC:     ErrFlagStr := ErrFlagStr + 'Unspec';  // zero
  142.               CAN_ERR_PROT_LOC_SOF:        ErrFlagStr := ErrFlagStr + 'SOF';     // start of frame
  143.               CAN_ERR_PROT_LOC_ID28_21:    ErrFlagStr := ErrFlagStr + 'ID28_21'; // ID bits 28 - 21 (SFF: 10 - 3)
  144.               CAN_ERR_PROT_LOC_ID20_18:    ErrFlagStr := ErrFlagStr + 'ID20_18'; // ID bits 20 - 18 (SFF: 2 - 0 )
  145.               CAN_ERR_PROT_LOC_SRTR:       ErrFlagStr := ErrFlagStr + 'SRTR';    // substitute RTR (SFF: RTR)
  146.               CAN_ERR_PROT_LOC_IDE:        ErrFlagStr := ErrFlagStr + 'IDE';     // identifier extension
  147.               CAN_ERR_PROT_LOC_ID17_13:    ErrFlagStr := ErrFlagStr + 'ID17_13'; // ID bits 17-13
  148.               CAN_ERR_PROT_LOC_ID12_05:    ErrFlagStr := ErrFlagStr + 'ID12_05'; // ID bits 12-5
  149.               CAN_ERR_PROT_LOC_ID04_00:    ErrFlagStr := ErrFlagStr + 'ID04_00'; // ID bits 4-0
  150.               CAN_ERR_PROT_LOC_RTR:        ErrFlagStr := ErrFlagStr + 'RTR';     // RTR
  151.               CAN_ERR_PROT_LOC_RES1:       ErrFlagStr := ErrFlagStr + 'RES1';    // reserved bit 1
  152.               CAN_ERR_PROT_LOC_RES0:       ErrFlagStr := ErrFlagStr + 'RES0';    // reserved bit 0
  153.               CAN_ERR_PROT_LOC_DLC:        ErrFlagStr := ErrFlagStr + 'DLC';     // data length code
  154.               CAN_ERR_PROT_LOC_DATA:       ErrFlagStr := ErrFlagStr + 'DATA';    // data section
  155.               CAN_ERR_PROT_LOC_CRC_SEQ:    ErrFlagStr := ErrFlagStr + 'CRC_SEQ'; // CRC sequence
  156.               CAN_ERR_PROT_LOC_CRC_DEL:    ErrFlagStr := ErrFlagStr + 'CRC_DEL'; // CRC delimiter
  157.               CAN_ERR_PROT_LOC_ACK:        ErrFlagStr := ErrFlagStr + 'ACK';     // ACK slot
  158.               CAN_ERR_PROT_LOC_ACK_DEL:    ErrFlagStr := ErrFlagStr + 'ACK_DEL'; // ACK delimiter
  159.               CAN_ERR_PROT_LOC_EOF:        ErrFlagStr := ErrFlagStr + 'EOF';     // end of frame
  160.               CAN_ERR_PROT_LOC_INTERM:     ErrFlagStr := ErrFlagStr + 'INTERM';  // intermission
  161.             otherwise
  162.               ErrFlagStr := ErrFlagStr + 'Unknown';   // protocol location not recognized
  163.             end;
  164.             Errors.Add('Prot(' + ErrFlagStr + '))');
  165.           end;
  166.           if CanFrame.ID.Mask.Error.Transceiver then
  167.           begin
  168.             ErrFlagStr := '';
  169.             case CanFrame.DataError.Transceiver of
  170.               CAN_ERR_TRX_UNSPEC:              ErrFlagStr := ErrFlagStr + 'Unspec';            // $00, 0000 0000
  171.               CAN_ERR_TRX_CANH_NO_WIRE:        ErrFlagStr := ErrFlagStr + 'CanHiNoWire';       // $04, 0000 0100
  172.               CAN_ERR_TRX_CANH_SHORT_TO_BAT:   ErrFlagStr := ErrFlagStr + 'CanHiShortToBAT';   // $05, 0000 0101
  173.               CAN_ERR_TRX_CANH_SHORT_TO_VCC:   ErrFlagStr := ErrFlagStr + 'CanHiShortToVCC';   // $06, 0000 0110
  174.               CAN_ERR_TRX_CANH_SHORT_TO_GND:   ErrFlagStr := ErrFlagStr + 'CanHiShortToGND';   // $07, 0000 0111
  175.               CAN_ERR_TRX_CANL_NO_WIRE:        ErrFlagStr := ErrFlagStr + 'CanLoNoWire';       // $40, 0100 0000
  176.               CAN_ERR_TRX_CANL_SHORT_TO_BAT:   ErrFlagStr := ErrFlagStr + 'CanLoShortToBAT';   // $50, 0101 0000
  177.               CAN_ERR_TRX_CANL_SHORT_TO_VCC:   ErrFlagStr := ErrFlagStr + 'CanLoShortToVCC';   // $60, 0110 0000
  178.               CAN_ERR_TRX_CANL_SHORT_TO_GND:   ErrFlagStr := ErrFlagStr + 'CanLoShortToGND';   // $70, 0111 0000
  179.               CAN_ERR_TRX_CANL_SHORT_TO_CANH:  ErrFlagStr := ErrFlagStr + 'CanLoShortToCanHi'; // $80, 1000 0000
  180.             otherwise
  181.               ErrFlagStr := ErrFlagStr + 'Unknown'; // transceiver error not recognized
  182.             end;
  183.             Errors.Add('Trans(' + ErrFlagStr + ')');
  184.           end;
  185.  
  186.           if Errors.Count > 0 then
  187.             Write(' ERR=');
  188.  
  189.           for i := 0 to Errors.Count - 1 do
  190.           begin
  191.             Write(Errors.Strings[i]);
  192.             if i <> (Errors.Count - 1) then
  193.               Write(',');
  194.           end;
  195.           WriteLn('');
  196.         end;
  197.         Sleep(5);
  198.       until KeyPressed;
  199.  
  200.       if CanClose(CanHandle) < 0 then
  201.       begin
  202.         perror('Close');
  203.         Exit;
  204.       end;
  205.     except
  206.       on e: Exception do
  207.         WriteLn(e.Message);
  208.     end;
  209.   finally
  210.     Errors.Free;
  211.   end;
  212. end.
  213.  

Beta version attached here is tied to vcan0. Before I put it into FPC trunk I will make it accept CAN interface as a parameter.
« Last Edit: April 16, 2021, 02:45:41 pm by avra »
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

avra

  • Hero Member
  • *****
  • Posts: 2128
    • Additional info
Re: CAN-BUS SocketCAN
« Reply #57 on: April 16, 2021, 02:29:25 pm »
I have searched all the net and I could not find a single open source tool for simulating CAN error messages. None. Zero. So, I have created hlcanerrsim. A unique tool created with FreePascal. Enjoy!

UPDATE: Source exceeds single message limit, so you can find it in can.zip archive in previous message.
« Last Edit: April 16, 2021, 02:38:19 pm by avra »
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

avra

  • Hero Member
  • *****
  • Posts: 2128
    • Additional info
Re: CAN-BUS SocketCAN
« Reply #58 on: April 28, 2021, 10:33:07 am »
hlcanerrdump now accepts CAN interface as first parameter and CAN filter for error messages as further parameters. Especially useful example is to use IgnoreNoAck parameter for filtering NoAck messages.
Code: [Select]
avra@vm-debian:/mnt/e/can$ ./hlcanerrdump
CAN Sockets Error Messages Dump

Usage: hlcanerrdump <CAN interface> [options]

CAN interface:                   ( CAN interface is case sensitive )
    can1                         ( or can2, can3 or virtual ones like vcan0, vcan1...

Options:                         ( options are not case sensitive )
                                 ( ERROR CLASS (MASK) IN CAN ID: )
    IgnoreTxTimeout              ( filter TX timeout by netdevice driver error messages )
    IgnoreLostArbitration        ( filter lost arbitration error messages )
    IgnoreController             ( filter controller problem error messages )
    IgnoreProtocol               ( filter protocol error messages )
    IgnoreTransceiver            ( filter transceiver status error messages )
    IgnoreNoAck                  ( filter no ACK on transmission error messages )
    IgnoreBusOff                 ( filter bus off error messages )
    IgnoreBusError               ( filter bus error messages )
    IgnoreRestarted              ( filter controller restarted messages )
                                 ( DEBUG HELPERS: )
    ShowFilterAndMaskBits        ( display all filtering bits )

Examples:

    ./hlcanerrdump can1
    ( dump all CAN error messages from CAN interface can1 )

    ./hlcanerrdump vcan0 IgnoreNoAck IgnoreBusOff
    ( dump all CAN error messages from virtual CAN interface vcan0 except NoACk and BusOff)

hlcanerrsim now accepts CAN interface as first parameter. It still accepts error message types as parameters for forming simulated error messages.
Code: [Select]
avra@vm-debian:/mnt/e/can$ ./hlcanerrsim
CAN Sockets Error Messages Simulator

Usage: hlcanerrsim <CAN interface> [options]

CAN interface:                   ( CAN interface is case sensitive )
    can1                         ( or can2, can3 or virtual ones like vcan0, vcan1...

Options:                         ( options are not case sensitive )
                                 ( ERROR CLASS (MASK) IN CAN ID: )
    TxTimeout                    ( TX timeout by netdevice driver )
    NoAck                        ( received no ACK on transmission )
    BusOff                       ( bus off )
    BusError                     ( bus error, may flood! )
    Restarted                    ( controller restarted )
                                 ( ARBITRATIONLOST IN CAN ID + BIT NUMBER IN DATA[0]: )
    LostArbitrationBit=<00..29>  ( decimal bit number in bitstream )
                                 ( CONTROLLER IN CAN ID + ERROR STATUS IN DATA[1]: )
    OverflowRX                   ( RX buffer overflow )
    OverflowTX                   ( TX buffer overflow )
    WarningRX                    ( reached warning level for RX errors )
    WarningTX                    ( reached warning level for TX errors )
    PassiveRX                    ( reached error passive status RX, errors > 127 )
    PassiveTX                    ( reached error passive status TX, errors > 127 )
    Active                       ( recovered to error active state )
                                 ( PROTOCOL ERROR IN CAN ID + TYPE IN DATA[2]: )
    SingleBit                    ( single bit error )
    FrameFormat                  ( frame format error )
    BitStuffing                  ( bit stuffing error )
    Bit0                         ( unable to send dominant bit )
    Bit1                         ( unable to send recessive bit )
    BusOverload                  ( bus overload )
    ActiveAnnouncement           ( active error announcement )
    TX                           ( error occurred on transmission )
                                 ( PROTOCOL ERROR IN CAN ID + LOCATION IN DATA[3]: )
    SOF                          ( start of frame )
    ID28_21                      ( ID bits 21..28, SFF: 3..10 )
    ID20_18                      ( ID bits 18..20, SFF: 0..2 )
    SRTR                         ( substitute RTR, SFF: RTR )
    IDE                          ( identifier extension )
    ID17_13                      ( ID bits 13..17 )
    ID12_05                      ( ID bits 5..12 )
    ID04_00                      ( ID bits 0..4 )
    RTR                          ( RTR )
    RES1                         ( reserved bit 1 )
    RES0                         ( reserved bit 0 )
    DLC                          ( data length code )
    DATA                         ( data section )
    CRC_SEQ                      ( CRC sequence )
    CRC_DEL                      ( CRC delimiter )
    ACK                          ( ACK slot )
    ACK_DEL                      ( ACK delimiter )
    EOF                          ( end of frame )
    INTERM                       ( intermission )
                                 ( TRANSCEIVER ERROR IN CAN ID + STATUS IN DATA[4]: )
                                 ( CANH CANL )
    TransUnspec                  ( 0000 0000 )
    CanHiNoWire                  ( 0000 0100 )
    CanHiShortToBAT              ( 0000 0101 )
    CanHiShortToVCC              ( 0000 0110 )
    CanHiShortToGND              ( 0000 0111 )
    CanLoNoWire                  ( 0100 0000 )
    CanLoShortToBAT              ( 0101 0000 )
    CanLoShortToVCC              ( 0110 0000 )
    CanLoShortToGND              ( 0111 0000 )
    CanLoShortToCanHi            ( 1000 0000 )
                                 ( CUSTOM BYTE TO DATA[0..7]: )
    Data<0..7>=<00..FF>          ( write hex number to one of 8 payload bytes )

Examples:

    ./hlcanerrsim can1 LostArbitrationBit=09 TX BusOff Data7=AA Data6=BB
    ( can1: 9th arb. bit lost, transmission, bus off, controller specific info in Data[6] and Data[7] )

    ./hlcanerrsim vcan0 NoAck TxTimeout Active
    ( vcan0: received no ACK on transmission, driver timeout, protocol type active error announcement )

    ./hlcanerrsim vcan0 BusError CanHiNoWire Restarted INTERM
    ( vcan0: bus error, lost CANH wiring, controller restarted, protocol location intermission )
« Last Edit: April 28, 2021, 02:23:41 pm by avra »
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

ThomasK

  • New Member
  • *
  • Posts: 34
Re: CAN-BUS SocketCAN
« Reply #59 on: May 15, 2021, 02:40:45 pm »
Hello Avra,

is there already a way to perform the actions which the 'ip' command does in pascal?

like "sudo ip link set can0 type can bitrate 125000 sample-point 0.875"
and "sudo ip link set can0 up"
and "sudo ip link set can0 down"?

I got the Inno-Maker CAN RS485 HAT to work with my test gear from PEAK and don't want to do work which is already done.....

Cheers,

Thomas
Started Pascal on a Siemens 4004/151 in 1977. TurboPascal 1.0 in 1984 on PC-Dos.

 

TinyPortal © 2005-2018