Recent

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

ThomasK

  • New Member
  • *
  • Posts: 40
Re: CAN-BUS SocketCAN
« Reply #75 on: June 03, 2021, 01:20:16 pm »
@avra, I have put yor sent & transmit code together with the epoll routines from the latest candump.c into a GUI app for Lazarus.

It works principally, I can send, receive, see that there is data after calling epoll_wait.

But the num_events is always either 0 or 1 even if more than 1 message was sent to the port.
Code: Pascal  [Select][+][-]
  1. unit mainunit;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, sockets, baseunix, can, linux;
  9.  
  10. type
  11.  
  12.   { TFCANTest }
  13.  
  14.   TFCANTest = class(TForm)
  15.     bInitcan0: TButton;
  16.     bSendData: TButton;
  17.     bReceiveData: TButton;
  18.     BClosecan0: TButton;
  19.     bEpollCreate: TButton;
  20.     bEpollQuery: TButton;
  21.     mLog: TMemo;
  22.     mLogTest: TMemo;
  23.     STnum_events: TStaticText;
  24.     STevents_pending: TStaticText;
  25.     StaticText3: TStaticText;
  26.     StaticText4: TStaticText;
  27.     procedure BClosecan0Click(Sender: TObject);
  28.     procedure bEpollCreateClick(Sender: TObject);
  29.     procedure bEpollQueryClick(Sender: TObject);
  30.     procedure bInitcan0Click(Sender: TObject);
  31.     procedure bReceiveDataClick(Sender: TObject);
  32.     procedure bSendDataClick(Sender: TObject);
  33.     procedure FormCreate(Sender: TObject);
  34.  
  35.   private
  36.     procedure Addmessages(mess2show: string);
  37.  
  38.   public
  39.  
  40.   end;
  41.  
  42.  
  43. const
  44.   AF_CAN = 29;
  45.   PF_CAN = 29;
  46.   CANDevice ='can0';
  47. type
  48. if_info = record
  49.   s            : integer;
  50.   cmdlinename  : ^byte;
  51.   dropcnt,
  52.   last_dropcnt : uint32;
  53. end;
  54. {
  55. type
  56.   EPoll_Data = record
  57.     case integer of
  58.       0: (ptr: pointer);
  59.       1: (fd: cint);
  60.       2: (u32: cuint);
  61.       3: (u64: cuint64);
  62.   end;
  63.   TEPoll_Data =  Epoll_Data;
  64.   PEPoll_Data = ^Epoll_Data;
  65.   EPoll_Event = record
  66.     Events: cuint32;
  67.     Data  : TEpoll_Data;
  68.   end;
  69.  
  70.   TEPoll_Event =  Epoll_Event;
  71.   PEpoll_Event = ^Epoll_Event;
  72. }
  73. var
  74.   FCANTest       : TFCANTest;
  75.   sock_info      : if_info;
  76.   events_pending : EPoll_Event;
  77.   event_setup    : EPoll_Event;
  78.   Pevent_Setup   : PEPoll_Event;
  79.   PPoll_Event    : PEPoll_Event;
  80.   fd_epoll,
  81.   num_events,
  82.   currmax        : integer;
  83.   s:     cint;
  84.   addr:  sockaddr_can;
  85.   ifr:   ifreq;
  86.   frame: can_frame;
  87.   timeout_ms : integer; //* default to no timeout */
  88.  
  89. implementation
  90.  
  91. {$R *.lfm}
  92.  
  93. { TFCANTest }
  94. procedure TFCANTest.Addmessages(mess2show: string);
  95. begin
  96.   mLogtest.Lines.BeginUpdate;
  97.   mLogtest.Lines.Add(mess2show);
  98.   mLogtest.Lines.EndUpdate;
  99.   mLogtest.SelStart := Length(mLogtest.Lines.Text)-1;
  100.   mLogtest.SelLength:=0;
  101. end;
  102.  
  103. procedure TFCANTest.FormCreate(Sender: TObject);
  104. begin
  105.   currmax            :=16;                                                       // number of CAN devices
  106.   event_setup.events :=EPOLLIN;
  107.   Pevent_Setup       :=@(event_setup);
  108.   PPoll_event        :=@(events_pending);
  109.   timeout_ms         :=10;
  110.   bInitcan0.caption  :=bInitcan0.caption + ' ' + CANDevice;
  111.   bClosecan0.caption  :=bclosecan0.caption + ' ' + CANDevice;
  112. end;
  113.  
  114. procedure TFCANTest.bInitcan0Click(Sender: TObject);
  115. var
  116.   perror : string;
  117. begin
  118.   s := fpsocket(PF_CAN, SOCK_RAW, CAN_RAW);
  119.   if s < 0 then
  120.   begin
  121.     perror:='socket creation error';
  122.     mLog.Lines.Add(perror);
  123.     Addmessages(perror);
  124.     Exit;
  125.   end else begin
  126.      mLog.Lines.Add('Socket: '+inttostr(s));
  127.      Addmessages('Socket: '+inttostr(s));
  128.   end;
  129.   strcopy(ifr.ifr_name, CANDevice);
  130.   if fpioctl(s, SIOCGIFINDEX, @ifr) < 0 then
  131.   begin
  132.     perror:='error IOctl';
  133.     mLog.Lines.Add(perror);
  134.     Addmessages(perror);
  135.     Exit;
  136.   end;
  137.   memset(@addr, 0, sizeof(addr));
  138.   addr.can_family  := AF_CAN;
  139.   addr.can_ifindex := ifr.ifr_ifindex;
  140.   if fpbind(s, @addr, sizeof(addr)) < 0 then // if fpbind(s, Psockaddr(@addr), sizeof(addr)) < 0 then
  141.   begin
  142.     perror:='error bind';
  143.     mLog.Lines.Add(perror);
  144.     Addmessages(perror);
  145.     Exit;
  146.   end;
  147. end;
  148.  
  149. procedure TFCANTest.bReceiveDataClick(Sender: TObject);
  150. var
  151.   nbytes,
  152.   i      : integer;
  153.   perror : string;
  154. begin
  155.     nbytes := fpread(s, frame, sizeof(can_frame));
  156.     if nbytes < 0 then
  157.     begin
  158.       perror:='Error during Read';
  159.       mLog.Lines.Add(perror);
  160.       Addmessages(perror);
  161.       Exit;
  162.     end;
  163.     if nbytes < sizeof(can_frame) then // paranoid check
  164.     begin
  165.       perror:='Read: Incomplete CAN frame';
  166.       mLog.Lines.Add(perror);
  167.       Addmessages(perror);
  168.       Exit;
  169.     end;
  170.     Perror:=Format('0x%03X [%d] ', [frame.can_id and CAN_SFF_MASK, frame.can_dlc]);
  171.     for i := 0 to frame.can_dlc - 1 do
  172.       Perror:=Perror + Format('%02.2X ', [frame.data[i]]);
  173.     mLog.Lines.Add(perror);
  174.     Addmessages(perror);
  175. end;
  176.  
  177. procedure TFCANTest.bSendDataClick(Sender: TObject);
  178. var
  179.   perror : string;
  180.   psizef : integer;
  181. begin
  182.   frame.can_id   := $020;    // CAN message ID
  183.   frame.can_dlc  := 8;       // payload length will be 8 bytes
  184.   frame.data[0]  := $10;
  185.   frame.data[1]  := $11;
  186.   frame.data[2]  := $12;
  187.   frame.data[3]  := $13;
  188.   frame.data[4]  := $14;
  189.   frame.data[5]  := $15;
  190.   frame.data[6]  := $16;
  191.   frame.data[7]  := $17;
  192.  
  193. psizef:= fpwrite(s, frame, sizeof(can_frame));
  194. if psizef <> sizeof(can_frame) then
  195.   begin
  196.     perror:='error write ' + InttoStr(psizef);
  197.     mLog.Lines.Add(perror);
  198.     Addmessages(perror);
  199.     Exit;
  200.   end else begin
  201.     mLog.Lines.Add(InttoStr(psizef) + ' bytes written');
  202.     Addmessages(InttoStr(psizef) + ' bytes written');
  203.   end;
  204. end;
  205.  
  206. procedure TFCANTest.BClosecan0Click(Sender: TObject);
  207. var
  208.   perror : string;
  209. begin
  210.   if fpclose(s) < 0 then
  211.     begin
  212.       perror:='error close socket ';
  213.       mLog.Lines.Add(perror);
  214.       Addmessages(perror);
  215.       Exit;
  216.     end;
  217.   if fpclose(fd_epoll) < 0 then
  218.     begin
  219.       perror:='error close epoll ';
  220.       mLog.Lines.Add(perror);
  221.       Addmessages(perror);
  222.       Exit;
  223.     end;
  224. end;
  225.  
  226. procedure TFCANTest.bEpollCreateClick(Sender: TObject);
  227. var
  228.   pepollerr : integer;
  229. begin
  230.   fd_epoll := epoll_create(1);
  231.   if fd_epoll < 0 then begin
  232.     mLog.Lines.Add('epoll create error');
  233.     Addmessages('epoll create error');
  234.     Exit;
  235.   end;
  236.   pepollerr:= epoll_ctl(fd_epoll, EPOLL_CTL_ADD, s, pevent_setup);
  237.   if pepollerr< 0 then begin
  238.     mLog.Lines.Add(IntTostr(pepollerr)+ ' epoll add to socket error');
  239.     Addmessages(IntTostr(pepollerr)+ ' epoll add to socket error');
  240.     Exit;
  241.   end;
  242. end;
  243.  
  244. procedure TFCANTest.bEpollQueryClick(Sender: TObject);
  245. begin
  246.   num_events := epoll_wait(fd_epoll, PPoll_event, currmax, timeout_ms);
  247.   STnum_events.caption:= IntToStr(num_events);
  248.   STevents_pending.caption:=IntToStr(PPoll_event^.Events);
  249. end;
  250.  
  251.  
  252. end.
  253.  

This would be sufficient, because it can prevent that the read routine gets stuck if there is nothing to read.

But the candump repeats the reads as often as num_events.... may be not, because when it is fast enough it sees only one telegram.
« Last Edit: June 04, 2021, 05:40:02 pm by ThomasK »
Started Pascal on a Siemens 4004/151 in 1977. TurboPascal 1.0 in 1984 on PC-Dos.

ThomasK

  • New Member
  • *
  • Posts: 40
Re: CAN-BUS SocketCAN
« Reply #76 on: June 04, 2021, 01:23:17 pm »
update.

ip commands added

{$i can.ioctl.inc}
{$i can.if.inc}
{$i can.sockios.inc}   included in zip.

epoll_wait delivers a '1' if the file descriptor has an EPOLLIN event. After all data is read, this returns to 0.

btw. runs on a Raspberry Pi 4 with inno maker dual RS485 and CAN Module, both isolated.
Other CAN device is attached to a W10 laptop. (Peak USB)
Maybe I try to get the USB-CAN running with a virtual machine on the laptop.
« Last Edit: June 08, 2021, 07:23:43 am by ThomasK »
Started Pascal on a Siemens 4004/151 in 1977. TurboPascal 1.0 in 1984 on PC-Dos.

avra

  • Hero Member
  • *****
  • Posts: 2226
    • Additional info
Re: CAN-BUS SocketCAN
« Reply #77 on: June 05, 2021, 05:19:46 pm »
But the num_events is always either 0 or 1 even if more than 1 message was sent to the port.
I am in the middle of industrial plant commissioning, so I will not be able to take a look at this before next week.
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

ThomasK

  • New Member
  • *
  • Posts: 40
Re: CAN-BUS SocketCAN
« Reply #78 on: June 06, 2021, 08:48:10 am »
I am in the middle of industrial plant commissioning, so I will not be able to take a look at this before next week.

Thanks.
But I think the num_events as result of epoll_wait is not increased with number of events per input, but with number of inputs having an event.

If only one input is polled it can simply be seen as „true“ for having data to read, if the only event type is EPOLLIN.
Started Pascal on a Siemens 4004/151 in 1977. TurboPascal 1.0 in 1984 on PC-Dos.

ThomasK

  • New Member
  • *
  • Posts: 40
Re: CAN-BUS SocketCAN
« Reply #79 on: June 08, 2021, 10:19:01 am »
I converted the LibSocketCAN library into a Pascal Unit.

beside the 'set' functions, which require root rights, the 'get' functions work as normal user.

Code: Pascal  [Select][+][-]
  1. unit LibSockCAN;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. //lib/arm-linux-gnueabihf/libsocketcan.so
  8. //libsocketcan.h
  9. uses can.netlink, ctypes,
  10.  
  11. if_linkpart;   // a short version of the linux/if_link.h
  12.  
  13. {$linklib c}
  14. {$linklib libsocketcan}
  15. type
  16.  pdword   = ^dword;
  17.  p_mybt   = ^can_bittiming;
  18.  p_mycm   = ^can_ctrlmode;
  19.  p_myclk  = ^can_clock;
  20.  p_mybtc  = ^can_bittiming_const;
  21.  p_mybcs  = ^can_berr_counter;
  22.  p_mycds  = ^can_device_stats;
  23.  p_myrls  = ^rtnl_link_stats64;  //defined in if_linkpart derived from <linux/if_link.h>  
  24.  rx_packets = qword;
  25.  
  26.  
  27. //These functions work only with root rights
  28.  
  29. function can_do_restart(myifname : ansistring) : longint; cdecl; external;
  30. function can_set_restart_ms(myifname : ansistring; restart_ms : dword) : longint; cdecl; external;
  31. function can_do_stop(myifname : ansistring) : longint; cdecl; external;
  32. function can_do_start(myifname : ansistring) : longint; cdecl; external;
  33. function can_set_bitrate(myifname : ansistring; bitrate : dword) : longint; cdecl; external;
  34. function can_set_bitrate_samplepoint(myifname : ansistring; bitrate, samplepoint : dword) : longint; cdecl; external;
  35. function can_set_bittiming(myifname : ansistring; mybt : p_mybt) : longint; cdecl; external;
  36. function can_set_ctrlmode(myifname : ansistring; mycm : p_mycm ) : longint; cdecl; external;
  37.  
  38. //These functions work with user rights
  39.  
  40. function can_get_restart_ms(myifname : ansistring; myrms : pdword)  :longint; cdecl; external;
  41. function can_get_bittiming(myifname : ansistring; mybt : p_mybt ) : longint; cdecl; external;
  42. function can_get_ctrlmode(myifname : ansistring; mycm : p_mycm) :longint; cdecl; external;
  43. function can_get_state(myifname : ansistring; mystate : pdword): longint; cdecl; external;
  44. function can_get_clock(myifname : ansistring; myclock : p_myclk) :longint; cdecl; external;
  45. function can_get_bittiming_const(myifname : ansistring; mybtc : p_mybtc) :longint; cdecl; external;
  46. function can_get_berr_counter(myifname : ansistring; mybcs : p_mybcs):longint; cdecl; external;
  47. function can_get_device_stats(myifname : ansistring; mycds : p_mycds):longint; cdecl; external;
  48. function can_get_link_stats(myifname : ansistring; myrls : p_myrls):longint; cdecl; external;
  49.  
  50.  
  51. implementation
  52.  
  53.  
  54. end.
  55.  


I have tested all the 'get' and from the 'set' the ones I would need.

Doing that, was easier than using ip commands and parsing the text returns for the desired value.
« Last Edit: June 08, 2021, 05:45:02 pm by ThomasK »
Started Pascal on a Siemens 4004/151 in 1977. TurboPascal 1.0 in 1984 on PC-Dos.

avra

  • Hero Member
  • *****
  • Posts: 2226
    • Additional info
Re: CAN-BUS SocketCAN
« Reply #80 on: June 09, 2021, 01:51:33 pm »
But the num_events is always either 0 or 1 even if more than 1 message was sent to the port.
Based on a quick look, it seams to me that instead of a single PEPoll_event you should try to create an array of PEPoll_event, pass number of elements to epoll_create(), and pass PEPoll_event array to epoll_wait(). Exactly аs in C demo that you can find at https://linux.die.net/man/7/epoll. I know that epoll_create() with kernels newer then 2.6.8 should not care for size parameter (as long as it's positive), but C demo thinks that it does matter so you should probably listen to it (at least until you make it work).

I converted the LibSocketCAN library into a Pascal Unit.
Very nice and thank you! If you agree to put the same license as in my CAN wrapper and demos, then I can add your wrapper also - if you add some simple console demo (of course, it will be clear who is the author of which file). What I would probably do is rename LibSockCAN.pas to can.lib.pas, and rename if_linkpart.pas (which is missing, btw) to can.if.linkpart.inc and include it from can.lib unit. What do you think?
« Last Edit: June 09, 2021, 02:00:53 pm by avra »
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

ThomasK

  • New Member
  • *
  • Posts: 40
Re: CAN-BUS SocketCAN
« Reply #81 on: June 09, 2021, 02:50:44 pm »
(at least until you make it work).
I actually don't care how many events occur, because the epoll is fixed to the event EPOLLIN. As long as the query returns '1' there is data to read, if
 it is '0' then not.

Very nice and thank you! If you agree to put the same license as in my CAN wrapper and demos, then I can add your wrapper also - if you add some simple console demo (of course, it will be clear who is the author of which file).
I can rework the whole stuff, no problem

Is really a console application required? A GUI exists, which I left out of the .zip due to its size and in case of a console app I would have to do a cmd parser......

What I would probably do is rename LibSockCAN.pas to can.lib.pas, and rename if_linkpart.pas (which is missing, btw) to can.if.linkpart.inc and include it from can.lib unit. What do you think?

I can do the renaming, the licensing stuff and and check if all is available, no problem. 

I am working on another topic : https://forum.lazarus.freepascal.org/index.php/topic,54944.msg408418.html#msg408418
and want to solve that too.
Pic of the GUI attached.
« Last Edit: June 09, 2021, 04:31:32 pm by ThomasK »
Started Pascal on a Siemens 4004/151 in 1977. TurboPascal 1.0 in 1984 on PC-Dos.

avra

  • Hero Member
  • *****
  • Posts: 2226
    • Additional info
Re: CAN-BUS SocketCAN
« Reply #82 on: June 09, 2021, 06:45:30 pm »
Is really a console application required? A GUI exists, which I left out of the .zip due to its size and in case of a console app I would have to do a cmd parser......
Although you can find basic cmd parsers in hlcanerrdump and hlcanerrsim, there is really no need to go that far. Just fix interface name and other needed parameters in code and that shall be fine. GUI advanced demos are nice, but SocketCAN package will be part of FPC and there is a good chance that it will run on a headless SBC without Lazarus, so simple console demos are more important.

What I would probably do is rename LibSockCAN.pas to can.lib.pas, and rename if_linkpart.pas (which is missing, btw) to can.if.linkpart.inc and include it from can.lib unit. What do you think?
I can do the renaming, the licensing stuff and and check if all is available, no problem.
Thanks! It would be great if you could use license templates from my demos and wrappers.

I am working on another topic : https://forum.lazarus.freepascal.org/index.php/topic,54944.msg408418.html#msg408418
and want to solve that too.
Pic of the GUI attached.
Nice. I wrote a solution proposal there.
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

ThomasK

  • New Member
  • *
  • Posts: 40
Re: CAN-BUS SocketCAN
« Reply #83 on: June 10, 2021, 11:17:37 am »
Hello Avra,

I have reworked the library and included the definition for

Code: Pascal  [Select][+][-]
  1. rtnl_link_stats64  =   record

in can.lib.pas library itself, like in libsocketcan.h. Explanations of this data type can be found for example here https://github.com/torvalds/linux/blob/master/include/uapi/linux/if_link.h.

I updated the license disclaimer of the original libsocketcan.h and added it to the library.
Besides the can.lib.pas, only can.netlink.pas is required to use libsocketcan.

A demo GUI application for Lazarus is included, it does not exercise all functions with all parameters set, but the use becomes clear.
If you want to use "set" commands it must be started with root rights, otherwise they fail.

Feel free to use the lib.can.pas together with your code.

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

avra

  • Hero Member
  • *****
  • Posts: 2226
    • Additional info
Re: CAN-BUS SocketCAN
« Reply #84 on: June 10, 2021, 03:31:01 pm »
Thanks Thomas, very nice! I will take a look as soon as I get some free time.
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

avra

  • Hero Member
  • *****
  • Posts: 2226
    • Additional info
Re: CAN-BUS SocketCAN
« Reply #85 on: July 01, 2021, 03:45:27 pm »
@Thomas: I have made small changes needed for your LibSocketCAN wrapper and demo to fit the rest of the project. Please review and comment if needed.

@Everyone: I am also thinking about making a high level pascal like interface to avoid ugly c naming in LibSocketCAN, like I already did for SocketCAN. However, I do not know if I should add it to can.hl.pas where the rest of the high level stuff is, or to a separate unit? I am in doubt since LibSocketCAN wrapper in can.lib.pas has {$linklib libsocketcan} which is a dependency that needs installation (from source or package manager), and I don't want to force that dependency to all SocketCAN users. Is lib dependency forced on everyone as soon as we include lib with $linklib, or we need to call something from that lib first? I do not know this so I ask if someone else knows?
« Last Edit: July 02, 2021, 12:55:51 am by avra »
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

ThomasK

  • New Member
  • *
  • Posts: 40
Re: CAN-BUS SocketCAN
« Reply #86 on: July 28, 2021, 10:48:34 am »
@Avra,

sorry for the late reply, I changed job so my days are very busy....

I will review the new demo and come back.

About integration of LibSocketCAN into can.hl my two cents are keep it as an tool on top, because  it is not always needed but a grip to do some diagnostics.

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

avra

  • Hero Member
  • *****
  • Posts: 2226
    • Additional info
Re: CAN-BUS SocketCAN
« Reply #87 on: July 28, 2021, 12:43:16 pm »
sorry for the late reply, I changed job so my days are very busy....

I will review the new demo and come back.
Take your time. No rush.

About integration of LibSocketCAN into can.hl my two cents are keep it as an tool on top, because it is not always needed but a grip to do some diagnostics.
Your wrapper will stay as can.lib unit, and future pascalish thin wrapper will be a separate can.lib.hl unit. That way we will still be able to easily convert C demos when needed, while having more eye pleasing pascalish named access as with the rest of the high level CAN.

I wish you well on your new job!
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

 

TinyPortal © 2005-2018