* * *

Author Topic: String representations of error codes retuned by FpOpen(), FpWrite(), etc  (Read 301 times)

ertank

  • Jr. Member
  • **
  • Posts: 88
Hello,

I am trying to use Raspberry Pi 3 GPIO ports natively using code examples in wiki page: http://wiki.freepascal.org/Lazarus_on_Raspberry_Pi#_Native_hardware_access

Here we call FpOpen(), FpWrite() and similar functions defined in BaseUnix unit. They return number results. If there is an error all I have is a number.

Is it possible to learn more meaningful text representation of such error codes? Such as "file read-only", "no such file", etc.

My searches are stuck at fpgeterrno which also returns a number. I am not advised to use fpseterrno  as I am not directly communicating with the kernel as to the documentation http://www.freepascal.org/docs-html/rtl/baseunix/fpgeterrno.html

Any help is appreciated.



Thaddy

  • Hero Member
  • *****
  • Posts: 3049
Use SysErrorMessage with fpGetErrNo.
Code: Pascal  [Select]
  1. // example written on RPi3 ;)
  2. program untitled;
  3. {$ifdef fpc}{$mode delphi}{$H+}{$endif}
  4.  
  5. uses BaseUnix,sysutils;
  6. var
  7.   h:cint;
  8. begin
  9. {$push}{$I-}
  10.   h:=fpOpen('/sys/class/nothinghere', O_RDWR); // fake device file ....will fail...
  11.   if h = -1 then
  12.     writeln(SysErrorMessage(fpGetErrNo))  //No such file or directory
  13.   else
  14.     fpClose(h);
  15. {$pop}
  16. end.
« Last Edit: March 06, 2017, 07:39:29 am by Thaddy »

ertank

  • Jr. Member
  • **
  • Posts: 88
Hi Thaddy,

It seems Raspberry Pi related example codes on wiki pages are not up to date, or I am doing something terribly wrong.

Below is my unit:
Code: [Select]
unit uGPIO;

{$mode objfpc}{$H+}

interface

uses
  Classes,
  SysUtils,
  DateUtils,
  Forms,
  BaseUnix;

procedure Log(const Value: string);
function SetupGPIO(const Pin: PChar; Direction: PChar): Boolean;
function ReleaseGPIO(const Pin: PChar): Boolean;
function SetGPIOPinOut(const Pin: PChar): Boolean;
function SetGPIOPinIn(const Pin: PChar): Boolean;

const
  PIN_OUT: PChar = 'out';
  PIN_IN: PChar = 'in';
  PIN_ON: PChar = '1';
  PIN_OFF: PChar = '0';

  PIN_1: PChar = '1';
  PIN_2: PChar = '2';
  PIN_3: PChar = '3';
  PIN_4: PChar = '4';
  PIN_5: PChar = '5';
  PIN_6: PChar = '6';
  PIN_7: PChar = '7';
  PIN_8: PChar = '8';
  PIN_9: PChar = '9';
  PIN_10: PChar = '10';
  PIN_11: PChar = '11';
  PIN_12: PChar = '12';
  PIN_13: PChar = '13';
  PIN_14: PChar = '14';
  PIN_15: PChar = '15';
  PIN_16: PChar = '16';
  PIN_17: PChar = '17';
  PIN_18: PChar = '18';
  PIN_19: PChar = '19';
  PIN_20: PChar = '20';
  PIN_21: PChar = '21';
  PIN_22: PChar = '22';
  PIN_23: PChar = '23';
  PIN_24: PChar = '24';
  PIN_25: PChar = '25';
  PIN_26: PChar = '26';
  PIN_27: PChar = '27';
  PIN_28: PChar = '28';
  PIN_29: PChar = '29';
  PIN_30: PChar = '30';
  PIN_31: PChar = '31';
  PIN_32: PChar = '32';
  PIN_33: PChar = '33';
  PIN_34: PChar = '34';
  PIN_35: PChar = '35';
  PIN_36: PChar = '36';
  PIN_37: PChar = '37';
  PIN_38: PChar = '38';
  PIN_39: PChar = '39';
  PIN_40: PChar = '40';

var
  BasePath: string;
  LogFileName: string;

implementation

procedure Log(const Value: string);
var
  F: TextFile;
  Prefix: string;
begin
  LogFileName := BasePath + 'log/' + FormatDateTime('yyyy-mm-dd', Now()) + '.log';

  AssignFile(F, LogFileName);

  {$I-}
  if FileExists(LogFileName) then
    Append(F)
  else
    ReWrite(F);

  if IOResult <> 0 then Exit();

  try
    Prefix := FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz   ', Now());
    Prefix := Prefix + StringReplace(Value, #13, ' ', [rfReplaceAll]);
    Prefix := StringReplace(Prefix, #10, ' ', [rfReplaceAll]);

    WriteLn(F, Prefix);
  finally
    CloseFile(F);
    {$I+}
  end;
end;

// Use physical pin number. Not GPIO Number
function SetupGPIO(const Pin: PChar; Direction: PChar): Boolean;
var
  FileDesc: Integer;
  ReturnCode: Longint;
begin
  if (Direction <> 'out') and (Direction <> 'in') then
  begin
    Log('SetupGPIO: wrong direction parameter: ' + QuotedStr(StrPas(Direction)));
    Exit(False);
  end;

  try
    FileDesc := FpOpen('/sys/class/gpio/export', O_WrOnly);
    if FileDesc <= 0 then
    begin
      Result := False;
      Log('SetupGPIO-fpopen: ' + SysErrorMessage(FpGetErrNo()));
    end;

    ReturnCode := fpwrite(FileDesc, Pin[0], Length(Pin));
    if ReturnCode = -1 then
    begin
      Result := False;
      Log('SetupGPIO-fpwrite: ' + SysErrorMessage(FpGetErrNo()));
    end;
  finally
    ReturnCode := fpclose(fileDesc);
    if ReturnCode <> 0 then
    begin
      Log('SetupGPIO-fpclose' + SysErrorMessage(FpGetErrNo()));
    end;
  end;

  if Result then
  begin
    if Direction = 'out' then
    Result := SetGPIOPinOut(Pin)
  else
    Result := SetGPIOPinIn(Pin);
  end;

  if Result then Log('SetupGPIO: Pin: ' + StrPas(Pin) + ' - success');
end;


// Use physical pin number. Not GPIO Number
function ReleaseGPIO(const Pin: PChar): Boolean;
var
  FileDesc: Integer;
  ReturnCode: Longint;
begin
  try
    FileDesc := FpOpen('/sys/class/gpio/unexport', O_WrOnly);
    if FileDesc <= 0 then
    begin
      Result := False;
      Log('ReleaseGPIO-fpopen: ' + SysErrorMessage(FpGetErrNo()));
    end;

    if Result then
    begin
      ReturnCode := fpwrite(FileDesc, Pin[0], Length(Pin));
      if ReturnCode = -1 then
      begin
        Result := False;
        Log('ReleaseGPIO-fpwrite: ' + SysErrorMessage(FpGetErrNo()));
      end;
    end;
  finally
    ReturnCode := fpclose(fileDesc);
    if ReturnCode <> 0 then
    begin
      Log('SetupGPIO-fpclose' + SysErrorMessage(FpGetErrNo()));
    end;
  end;

  // Return True in anyway
  Result := True;
  Log('ReleaseGPIO: Pin: ' + StrPas(Pin) + ' - success');
end;


function SetGPIOPinOut(const Pin: PChar): Boolean;
var
  FileName: ShortString;
  FileDesc: Integer;
  ReturnCode: Longint;
begin
  FileName := '/sys/class/gpio/gpio' + StrPas(Pin) + '/direction';
  Log('SetGPIOPinOut: FileName: ' + FileName);

  Result := True;
  try
    FileDesc := FpOpen(FileName, O_WrOnly);
    if FileDesc <= 0 then
    begin
      Result := False;
      Log('InitGPIOPin-fpopen: ' + SysErrorMessage(FpGetErrNo()));
    end;

    if Result then
    begin
      ReturnCode := fpwrite(FileDesc, 'out', 3);
      if ReturnCode = -1 then
      begin
        Result := False;
        Log('InitGPIOPin-fpwrite: ' + SysErrorMessage(FpGetErrNo()));
      end;
    end;
  finally
    ReturnCode := fpclose(fileDesc);
    if ReturnCode <> 0 then
    begin
      Log('SetGPIOPinOut-fpclose: ' + SysErrorMessage(FpGetErrNo()));
    end;
  end;
end;

function SetGPIOPinIn(const Pin: PChar): Boolean;
var
  FileName: ShortString;
  FileDesc: Integer;
  ReturnCode: Longint;
begin
  FileName := '/sys/class/gpio/gpio' + StrPas(Pin) + '/direction';
  Log('SetGPIOPinIn: FileName: ' + FileName);

  Result := True;
  try
    FileDesc := FpOpen(FileName, O_WrOnly);
    if FileDesc <= 0 then
    begin
      Result := False;
      Log('SetGPIOPinIn-fpopen: ' + SysErrorMessage(FpGetErrNo()));
    end;

    if Result then
    begin
      ReturnCode := fpwrite(FileDesc, 'in', 2);
      if ReturnCode = -1 then
      begin
        Result := False;
        Log('SetGPIOPinIn-fpwrite: ' + SysErrorMessage(FpGetErrNo()));
      end;
    end;
  finally
    ReturnCode := fpclose(fileDesc);
    if ReturnCode <> 0 then
    begin
      Log('SetGPIOPinIn-fpclose: ' + SysErrorMessage(FpGetErrNo()));
    end;
  end;
end;


initialization
  BasePath := ExtractFilePath(Application.ExeName);
  if not DirectoryExists(BasePath + '/log') then ForceDirectories(BasePath + '/log');
  Log('uGPIO initialization');

end.

Trying to setup PIN23 and PIN24 using:
Code: [Select]
// On FormCreate()
  SetupGPIO(PIN_23, PIN_OUT);
  SetupGPIO(PIN_24, PIN_IN);

// On FormDestroy
  ReleaseGPIO(PIN_23);
  ReleaseGPIO(PIN_24);

Gives me following error lines in my log file:
Code: [Select]
2017-03-06 09:37:01.658   uGPIO initialization
2017-03-06 09:37:01.913   SetGPIOPinOut: FileName: /sys/class/gpio/gpio23/direction
2017-03-06 09:37:01.913   InitGPIOPin-fpopen: Permission denied
2017-03-06 09:37:01.914   SetGPIOPinOut-fpclose: Bad file number
2017-03-06 09:37:26.457   ReleaseGPIO: Pin: 23 - success
2017-03-06 09:37:26.458   ReleaseGPIO: Pin: 24 - success

My /sys/class/gpio output is:
Code: [Select]
pi@raspberrypi:~ $ ls -l /sys/class/gpio/
toplam 0
-rwxrwx--- 1 root gpio 4096 Mar  2 09:05 export
lrwxrwxrwx 1 root gpio    0 Mar  6 09:37 gpio23 -> ../../devices/platform/soc/3f200000.gpio/gpio/gpio23
lrwxrwxrwx 1 root gpio    0 Mar  2 09:05 gpiochip0 -> ../../devices/platform/soc/3f200000.gpio/gpio/gpiochip0
lrwxrwxrwx 1 root gpio    0 Mar  2 09:05 gpiochip100 -> ../../devices/platform/soc/soc:virtgpio/gpio/gpiochip100
-rwxrwx--- 1 root gpio 4096 Mar  2 09:05 unexport
pi@raspberrypi:~ $

pi@raspberrypi:~ $ ls -l /sys/class/gpio/gpio23/
toplam 0
-rwxrwx--- 1 root gpio 4096 Mar  6 09:37 active_low
lrwxrwxrwx 1 root gpio    0 Mar  6 09:37 device -> ../../../3f200000.gpio
-rwxrwx--- 1 root gpio 4096 Mar  6 09:37 direction
-rwxrwx--- 1 root gpio 4096 Mar  6 09:37 edge
drwxrwx--- 2 root gpio    0 Mar  6 09:37 power
lrwxrwxrwx 1 root gpio    0 Mar  6 09:37 subsystem -> ../../../../../../class/gpio
-rwxrwx--- 1 root gpio 4096 Mar  6 09:37 uevent
-rwxrwx--- 1 root gpio 4096 Mar  6 09:37 value
pi@raspberrypi:~ $

So, my question is:
System creates a symbolic link once I try to set a pin for export?

My observations:
- As document indicates nothing about permissions. However, it seems I need to be at least a member of gpio group in order to be able to set directions. Though, python scripts has no trouble using IO ports with same user (pi) as my application runs. I cant understand why exporting port is successfull but not setting it for output or input is a fail?
- PXL (Platform eXtended Library) for low level native access to GPIO raised an exception for me with message "Cannot open file /dev/mem for memory mapping. Can this be related with permissions?

I do not want to run my application as root or some other privileged user. As you are also working with Raspberry Pi, I appreciate if you share your experience, please.

Thanks & regards,
-Ertan
« Last Edit: March 06, 2017, 07:53:50 am by ertank »

Thaddy

  • Hero Member
  • *****
  • Posts: 3049
I cant test right now but Will try your code later.
One thing that is immediately obvious is that you test the file handles wrong. You compare <=0... that's wrong. You should explicitly test for -1, not minus whatever, but -1. and then get the errorinfo.
It is always -1 if it fails. 0 is the special case NULL device handle and is always valid, and will never be returned for a file or device handle request..
Second thing: You open only for write...? Is that correct?
« Last Edit: March 06, 2017, 02:27:45 pm by Thaddy »

ertank

  • Jr. Member
  • **
  • Posts: 88
Hi Thaddy,

I am using examples from wiki page and on top of that I am simply trying to make them easier to use. I think doing the pin setup, I have to open for writing. Value reading, I can do read only.

On the other hand, codes turn out to be correct. If you put aside some wrong "if" controls in my code, problem was me not using root account. It seems that in order to be able to use GPIO, I absolutely need to be root.

Once I fixed that PLX components also started to work. As they are fast access (using /dev/mem) I am currently sticking to them.

Thanks for all your helps.

Regards,
-Ertan

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus