Recent

Author Topic: Linux, Lazarus and the Serial Port  (Read 19810 times)

mas steindorff

  • Hero Member
  • *****
  • Posts: 553
Re: Linux, Lazarus and the Serial Port
« Reply #15 on: February 27, 2017, 10:11:33 pm »
Hello JP
I've recently started a project on Ubuntu 16.04 using your updated 0.2 unit (from GIT)  I'm having trouble connecting and with this GetSerialPortNames as it does not return anything.
when I hard code the port name in as
"/dev/ttyusb0'  the result is the port just is not found   << I also do not see it in /dev >>
"/dev/ttyS0"  returns "Permission denied"                    << I can see almost 30 of these even though I only have one>>

when I debug the GetS..Names() it's the following line that fails (never returns 0)
Code: Pascal  [Select][+][-]
  1.  if FindFirst( ThisRootStr, $FFFFFFFF, sr) = 0 then
  2.  

so I believe the program does not have "rights" to open a port.  I suspect you test your code in some sort of "sudo" mode, can you give me any suggestions on how to get access to the ports?

thanks
Mas

hello,
have a look in the GetSerialPortNames function of synaser.pas (modified version) ot TLazserial package :
..snip..
{$IFNDEF MSWINDOWS}
// Modif J.P   03/2013
function GetSerialPortNames: string;
var
  Index: Integer;
  Data: string;
  TmpPorts: String;
  sr : TSearchRec;

  procedure ScanForPorts( const ThisRootStr : string); // added by PDF
  var theDevice : String;
  var FD : Cint;
  var Ser : TSerialStruct;
  begin
    if FindFirst( ThisRootStr, $FFFFFFFF, sr) = 0 then
    begin
      repeat
        if (sr.Attr and $FFFFFFFF) = Sr.Attr then
        begin
<<snip >>
end;
{$ENDIF}[/code]

Friendly, J.P
« Last Edit: February 27, 2017, 10:38:28 pm by mas steindorff »
windows 10 &11, Ubuntu 21+ IDE 3.4 general releases

Jurassic Pork

  • Hero Member
  • *****
  • Posts: 1255
Re: Linux, Lazarus and the Serial Port
« Reply #16 on: February 28, 2017, 02:14:37 am »
hello,
i have tried on LUBUNTU 16.04 with two USB serial port :   you need to change the permissions of your tty device :  you need to be member of group dialout or change the permissions of your serial devices to 666  >:D  .
Example :
Quote
jurassic@jurassic-PC:~$ dmesg | grep -i tty
[    0.000000] console [tty0] enabled
[   15.953635] usb 3-1.1: FTDI USB Serial Device converter now attached to ttyUSB0
[   15.955598] usb 3-1.2: FTDI USB Serial Device converter now attached to ttyUSB1
jurassic@jurassic-PC:~$ cd /dev
jurassic@jurassic-PC:/dev$ ls -lt /dev/ttyUS*
crw-rw---- 1 root dialout 188, 0 févr. 28  2017 /dev/ttyUSB0
crw-rw---- 1 root dialout 188, 1 févr. 28  2017 /dev/ttyUSB1
jurassic@jurassic-PC:/dev$ sudo chmod 666 /dev/ttyUSB*
[sudo] Mot de passe de jurassic :
jurassic@jurassic-PC:/dev$ ls -lt /dev/ttyUS*
crw-rw-rw- 1 root dialout 188, 0 févr. 28  2017 /dev/ttyUSB0
crw-rw-rw- 1 root dialout 188, 1 févr. 28  2017 /dev/ttyUSB1
jurassic@jurassic-PC:/dev$

Friendly, J.P
« Last Edit: February 28, 2017, 02:27:39 am by Jurassic Pork »
Jurassic computer : Sinclair ZX81 - Zilog Z80A à 3,25 MHz - RAM 1 Ko - ROM 8 Ko

mas steindorff

  • Hero Member
  • *****
  • Posts: 553
Re: Linux, Lazarus and the Serial Port
« Reply #17 on: February 28, 2017, 03:41:33 am »
thank you.
I'll make note of that.  I was trying to avoid any "install" operations but it looks like I'll need to run some basic things on each computer.

do you need to use ttyusb# as your port name string or do you use ttyS# when you tested with your USB ports?

BTY, I've been working with synaser for a few years under windows.  did you resolve the bug case where the port close operation can take up to 20seconds when the port disappears while connected?
 
also, would you like some of my extensions? 

I have a "getPortName" that returns a tstringlist of the type of each port from the reg. 
+ I have a registry bug fix where the last char of the port was being striped off. (mostly for BT VCP) -- hopefully this has been fixed in 1.6 but was still a bug in 1.4
+ some port timeout fixes (noted above) but most of my fixes have been done by using a thread.

mas

[edited]
this works but I need to do the chmod command each time it's plugged in.   
may be OK for now, the port is a real comm port in the final system.
« Last Edit: February 28, 2017, 04:01:45 am by mas steindorff »
windows 10 &11, Ubuntu 21+ IDE 3.4 general releases

Johan Holstein

  • Jr. Member
  • **
  • Posts: 67
Re: Linux, Lazarus and the Serial Port
« Reply #18 on: December 25, 2020, 11:52:01 am »
Hi,

I try to get this working on my Linux machine,
I like to set my serial port in my application and like to use Serial.ShowSetupDialog;   

That works however I can not get the serial names only the default pop up in my case /dev/ttyACM0
 
Please help....
Best regards,
Johan



hello,
have a look in the GetSerialPortNames function of synaser.pas (modified version) ot TLazserial package :

Code: Pascal  [Select][+][-]
  1. {$IFDEF MSWINDOWS}
  2. function GetSerialPortNames: string;
  3. var
  4.   reg: TRegistry;
  5.   l, v: TStringList;
  6.   n: integer;
  7. begin
  8.   l := TStringList.Create;
  9.   v := TStringList.Create;
  10.   reg := TRegistry.Create;
  11.  
  12.   try
  13. {$IFNDEF VER100}
  14. {$IFNDEF VER120}
  15.     reg.Access := KEY_READ;
  16. {$ENDIF}
  17. {$ENDIF}
  18.     reg.RootKey := HKEY_LOCAL_MACHINE;
  19.     reg.OpenKey('\HARDWARE\DEVICEMAP\SERIALCOMM\', false);
  20.     reg.GetValueNames(l);
  21.     for n := 0 to l.Count - 1 do
  22. // Modif J.P  03/2013
  23.       v.Add(Pchar(reg.ReadString(l[n])));
  24.     Result := v.CommaText ;
  25.   finally
  26.     reg.Free;
  27.     l.Free;
  28.     v.Free;
  29.   end;
  30. end;
  31. {$ENDIF}
  32. {$IFNDEF MSWINDOWS}
  33. // Modif J.P   03/2013
  34. function GetSerialPortNames: string;
  35. var
  36.   Index: Integer;
  37.   Data: string;
  38.   TmpPorts: String;
  39.   sr : TSearchRec;
  40.  
  41.   procedure ScanForPorts( const ThisRootStr : string); // added by PDF
  42.   var theDevice : String;
  43.   var FD : Cint;
  44.   var Ser : TSerialStruct;
  45.   begin
  46.     if FindFirst( ThisRootStr, $FFFFFFFF, sr) = 0 then
  47.     begin
  48.       repeat
  49.         if (sr.Attr and $FFFFFFFF) = Sr.Attr then
  50.         begin
  51.           data := sr.Name;
  52.           index := length(data);
  53.           theDevice := '/dev/' + data;
  54. // try to open the device
  55.        FD := fpopen(thedevice,O_RdWr or O_NonBlock or O_NoCtty);
  56.        if FD > 0 then
  57.           begin
  58. // try to get serial info from the device
  59.            if fpioctl( FD,TIOCGSERIAL, @Ser) <> -1 then
  60.              begin
  61. // device is serial if type is not unknown
  62.               if (Ser.typ <> 0)  then
  63.                TmpPorts := TmpPorts + '  ' + theDevice;
  64.                fpclose(FD);
  65.              end;
  66.            end;
  67.         end;
  68.       until FindNext(sr) <> 0;
  69.     end;
  70.   end;
  71.  
  72. begin
  73.   try
  74.     TmpPorts := '';
  75.     ScanForPorts( '/dev/rfcomm*');
  76.  //   ScanForPorts( '/dev/pts/*');
  77.     ScanForPorts( '/dev/ttyUSB*');
  78.     ScanForPorts( '/dev/ttyS*');
  79.     ScanForPorts( '/dev/ttyAM*'); // for ARM board
  80.     FindClose(sr);
  81.   finally
  82.     Result:=TmpPorts;
  83.   end;
  84. end;
  85. {$ENDIF}

Friendly, J.P

Johan Holstein

  • Jr. Member
  • **
  • Posts: 67
Re: Linux, Lazarus and the Serial Port
« Reply #19 on: December 25, 2020, 12:06:04 pm »
Messages....

Compile Project, Target: project1: Exit code 1, Errors: 13, Warnings: 7
unit1.pas(134,14) Error: Identifier not found "Cint"
unit1.pas(134,18) Error: Error in type definition
unit1.pas(135,15) Error: Identifier not found "TSerialStruct"
unit1.pas(135,28) Error: Error in type definition
unit1.pas(137,43) Warning: range check error while evaluating constants (4294967295 must be between -2147483648 and 2147483647)
unit1.pas(146,16) Error: Identifier not found "fpopen"
unit1.pas(146,33) Error: Identifier not found "O_RdWr"
unit1.pas(146,43) Error: Identifier not found "O_NonBlock"
unit1.pas(146,57) Error: Identifier not found "O_NoCtty"
unit1.pas(147,16) Error: Operator is not overloaded: "<erroneous type>" > "ShortInt"
unit1.pas(150,17) Error: Identifier not found "fpioctl"
unit1.pas(150,29) Error: Identifier not found "TIOCGSERIAL"
unit1.pas(153,25) Error: Illegal qualifier
unit1.pas(155,18) Error: Identifier not found "fpclose"

Graham1

  • Jr. Member
  • **
  • Posts: 64
Re: Linux, Lazarus and the Serial Port
« Reply #20 on: April 29, 2021, 04:25:22 am »
Messages....

Compile Project, Target: project1: Exit code 1, Errors: 13, Warnings: 7
(etc)

I'm sure by now you've worked out which USES you need to include, but in case you haven't here is my updated version:

Code: Pascal  [Select][+][-]
  1. unit serport;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.    {$IFDEF WINDOWS}
  9.       registry,
  10.    {$ELSE}
  11.       termio, baseunix,
  12.    {$ENDIF}
  13.    classes, sysutils;
  14.  
  15. function GetSerialPortNames: string;
  16.  
  17. {$IFDEF LINUX}
  18. { Translated from include/linux/serial.h }
  19. type   TSerialStruct = packed record
  20.          typ             : Integer;
  21.          line            : Integer;
  22.          port            : Cardinal;
  23.          irq             : Integer;
  24.          flags           : Integer;
  25.          xmit_fifo_size  : Integer;
  26.          custom_divisor  : Integer;
  27.          baud_base       : Integer;
  28.          close_delay     : Word;
  29.          io_type         : Char;
  30.          reserved_char   : Char;
  31.          hub6            : Integer;
  32.          closing_wait    : Word; // time to wait before closing
  33.          closing_wait2   : Word; // no longer used...
  34.          iomem_base      : ^Char;
  35.          iomem_reg_shift : Word;
  36.          port_high       : Cardinal;
  37.          iomap_base      : LongWord; // cookie passed into ioremap
  38.        end;
  39. {$ENDIF}
  40.  
  41. implementation
  42.  
  43. {$IFDEF WINDOWS}
  44. function GetSerialPortNames: string;
  45. var
  46.   reg: TRegistry;
  47.   l, v: TStringList;
  48.   n: integer;
  49. begin
  50.   l := TStringList.Create;
  51.   v := TStringList.Create;
  52.   reg := TRegistry.Create;
  53.   try
  54.     reg.Access := KEY_READ;
  55.     reg.RootKey := HKEY_LOCAL_MACHINE;
  56.     reg.OpenKey('\HARDWARE\DEVICEMAP\SERIALCOMM', false);
  57.     reg.GetValueNames(l);
  58.     for n := 0 to l.Count - 1 do
  59.       v.Add(reg.ReadString(l[n]));
  60.     Result := v.CommaText;
  61.   finally
  62.     reg.Free;
  63.     l.Free;
  64.     v.Free;
  65.   end;
  66. end;
  67. {$ENDIF}
  68. {$IFDEF LINUX}
  69. function GetSerialPortNames: string;
  70. var
  71.   ports : string;
  72.   flags : Longint;
  73.   rec   : TSearchRec;
  74.      
  75.   procedure ScanForPorts(const ThisRootStr : string);
  76.   var
  77.     devnam : string;
  78.     FD     : cInt;
  79.     ser    : TSerialStruct;
  80.   begin
  81.     if FindFirst(ThisRootStr, flags, rec) = 0 then
  82.     begin
  83.       repeat
  84.         if (rec.Attr and flags) = rec.Attr then
  85.         begin
  86.           devnam := '/dev/' + rec.Name;
  87.           // try to open the device
  88.           FD := fpopen(devnam, O_RdWr or O_NonBlock or O_NoCtty);
  89.           if FD > 0 then
  90.           begin
  91.           // try to get serial info from the device
  92.             if fpioctl(FD, TIOCGSERIAL, @ser) <> -1 then
  93.             begin
  94.               // device is serial if type is not unknown
  95.               if (ser.typ <> 0)  then begin
  96.                 if ports <> '' then
  97.                   ports := ports + ',' + rec.Name
  98.                 else
  99.                   ports := rec.Name;
  100.               end;
  101.             end;
  102.             fpclose(FD);
  103.           end;
  104.         end;
  105.       until FindNext(rec) <> 0;
  106.     end;
  107.   end;
  108.      
  109. begin
  110.   ports := '';
  111.   flags := faAnyFile AND (NOT faDirectory);
  112.   try
  113.     ScanForPorts('/dev/rfcomm*');
  114.     ScanForPorts('/dev/ttyAM*');
  115.     ScanForPorts('/dev/ttyS*');
  116.     ScanForPorts('/dev/ttyUSB*');
  117.     FindClose(rec);
  118.   finally
  119.     Result := ports;
  120.   end;
  121. end;
  122. {$ENDIF}
  123. {$IFDEF MACOS}
  124.   // Same as Linux?
  125. {$ENDIF}
  126.  
  127. end.
  128.  

I've changed it slightly so that both the Windows and Linux versions output a comma delimited string as that makes the rest of the program more consistent. But now I have some questions:

1. Rights
I had to use Jurassic Pork's change for 666 to see and use the ports. But I'm using a USB Serial adapter and if I unplug it and replug it, or reboot the PC, I need to run the commands again. Is there anyway to avoid this or to set the rights from within Pascal? Or to set default rights for all serial ports (forever)?

2. Port description
In Windows I can get the description of the port (e.g. "Prolific...") from the Registry. In Linux I can see the files in /Dev/Serial/ but how do I match those to the tty file in /Dev/? Or is there a better way to get the port description?

3. Mac
I haven't tried this on a Mac yet but does anybody know if it is just the same as the Linux version?

Thanks!
Windows 10/11 Home 64-bit (and Linux because I have to)
Lazarus 2.0.12 / FPC 3.2.0 (because libQt5pas 1.2.6)
Linux Mint 20 (because GLIBC_2.31)

MarkMLl

  • Hero Member
  • *****
  • Posts: 8187
Re: Linux, Lazarus and the Serial Port
« Reply #21 on: April 29, 2021, 08:17:19 am »
2. Port description
In Windows I can get the description of the port (e.g. "Prolific...") from the Registry. In Linux I can see the files in /Dev/Serial/ but how do I match those to the tty file in /Dev/? Or is there a better way to get the port description?

You have to do a lot of most unpleasant meandering through the /sys tree, some details of which are driver-dependent. You'll find locateXXXport code in the three projects below (the low-level code is the same but the parameters will vary), the objective was to try to find the port that had a certain type of device on it but TBH I'm not entirely sure it's worth the effort.

https://github.com/MarkMLl/Mastech_ms2115b

https://github.com/MarkMLl/Contec_cms50dplus

https://github.com/MarkMLl/hp2671

To my mind, this is a strong incentive for using a (genuine) FTDI interface in an embedded design: you can easily write a serial number to NVR and finding that is going to be more useful than the sort of guessing I've had to do above.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Graham1

  • Jr. Member
  • **
  • Posts: 64
Re: Linux, Lazarus and the Serial Port
« Reply #22 on: April 30, 2021, 05:53:23 am »
1. Rights
I had to use Jurassic Pork's change for 666 to see and use the ports. But I'm using a USB Serial adapter and if I unplug it and replug it, or reboot the PC, I need to run the commands again. Is there anyway to avoid this or to set the rights from within Pascal? Or to set default rights for all serial ports (forever)?
I found the answer to this on another forum. Rather than change the rights for each device each time, I need to add my user name to the group that owns it. So first:
Code: Bash  [Select][+][-]
  1. cd /dev
  2. ls -l ttyU*
That will show something like:
Code: Text  [Select][+][-]
  1. crw-rw---- 1 root dialout 188, 0 Apr 30 15:36 ttyUSB0
The owner name here is 'root' and the group is 'dialout'. So then I need to edit the file group in /etc:
Code: Bash  [Select][+][-]
  1. cd /etc
  2. sudo gedit group
and add my name to the dialout group, for example:
Code: Text  [Select][+][-]
  1. dialout:x:20:graham
(If there is already a name there, put I need a comma before my name). Then I need to save the file, logout and log back in so that the rights are re-read.

2. Port description
In Windows I can get the description of the port (e.g. "Prolific...") from the Registry. In Linux I can see the files in /Dev/Serial/ but how do I match those to the tty file in /Dev/? Or is there a better way to get the port description?
MarkMML: Wow! It's pretty amazing you managed to do that, but it's also pretty poor that you needed to! Like you said, it's difficult to know if it's really worth the effort. Thanks for the information though.

Graham
Windows 10/11 Home 64-bit (and Linux because I have to)
Lazarus 2.0.12 / FPC 3.2.0 (because libQt5pas 1.2.6)
Linux Mint 20 (because GLIBC_2.31)

MarkMLl

  • Hero Member
  • *****
  • Posts: 8187
Re: Linux, Lazarus and the Serial Port
« Reply #23 on: April 30, 2021, 09:51:14 am »
@Graham1 general points following on from both of your earlier comments. It's also possible to change device ownership and access rights by putting something appropriate in /etc/udev/rules.d, but for a standard device where an alternative has existed for many years (and been mentioned in this forum on a fairly regular basis) it's overkill.

The above also applies to physical devices which don't put an obvious unix-style device in the /dev directory, i.e. FPGA programmers and the likes.

In addition, it is possible to register to receive a network-style packet when a device is hotplugged, this is basically one of the underpinnings of udev etc... I can't remember whether you need root privilege (or elevated capabilities) for this.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

sunjob

  • New Member
  • *
  • Posts: 33
Re: Linux, Lazarus and the Serial Port
« Reply #24 on: April 21, 2024, 01:09:30 pm »
URL
my mini-mod  ;)
Code: Pascal  [Select][+][-]
  1. ////////////////////////////////////////////////////////////////////////////////
  2. function GetSerialPortNames(dev_mask:string): string; // dev_mask = /dev/ttyUSB*
  3. ////////////////////////////////////////////////////////////////////////////////
  4. var
  5. info : TSearchRec;
  6. str  : string;
  7.  
  8. begin
  9. str := '';
  10.  
  11. try
  12. if(FindFirst(dev_mask, faSysFile, info) = 0) then
  13.   begin
  14.   repeat
  15.   if(str.IsEmpty) then str :=         ExtractFileDir(dev_mask)+'/'+info.Name
  16.   else                 str := str+','+ExtractFileDir(dev_mask)+'/'+info.Name;
  17.   until FindNext(info) <> 0;
  18.   end;
  19. finally
  20.   FindClose(info);
  21.   end; // try-finally()
  22.  
  23. Result := str;
  24. end;
  25. ////////////////////////////////////////////////////////////////////////////////
  26.  
« Last Edit: April 21, 2024, 03:37:51 pm by sunjob »

 

TinyPortal © 2005-2018