1. We are not accepting any new account registrations at this time - watch out for announcements on Discord.

RasPI 3 - Trouble accessing HID custom made device.

Discussion in 'HelpDesk' started by Especialista, Oct 5, 2018.

  1. Especialista

    Especialista is a Trusted Warez PosterEspecialista B4A Hitman Staff Member Super Moderator DEV Guild Reverser Translator

    Joined:
    Jul 15, 2011
    Messages:
    15,079
    Likes Received:
    44,087
    I have a thing disturbing me and since I'm not that "Linux Guy", I ask for help from you.

    Workbench Situation:
    • Raspberry PI 3-B, with Raspbian Stretch updated and running Ok.
    • Free Pascal Compiler version 2.6.4+dfsg-4+rpi1 [2014/10/21] for arm
    • Lazarus 1.2.4+dfsg2-1
    • Custom Made Device based on PIC18F2550, connected via USB to RasPi 3. (For information, it performs as predicted if I connect it to Windows, and I can access it via sw made with Delphi X10.2)
    What I'm sure at the moment:
    • RasPi reconizes my custom made device:
    [​IMG]

    DMesg Command Output:
    Code:
    https://www67.zippyshare.com/v/0kNgBPVm/file.html
    
    • I've created a file named /etc/udev/rules.d/55-pic-usbhid.rules with this contents:
    Code:
    # This is a sample udev file for HIDAPI devices which changes the permissions
    # to 0666 (world readable/writable) for a specified device on Linux systems.
    
    
    # If you are using the libusb implementation of hidapi (hid-libusb.c), then
    # use something like the following line, substituting the VID and PID with
    # those of your device. Note that for kernels before 2.6.24, you will need
    # to substitute "usb" with "usb_device". It shouldn't hurt to use two lines
    # (one each way) for compatibility with older systems.
    
    # HIDAPI/libusb
    #SUBSYSTEM=="usb", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="000b", MODE="0666", GROUP="plugdev"
    SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", SYSFS{idVendor}=="04d8" , SYSFS{idProduct}=="000b", MODE="0666", GROUP="plugdev"
    
    # If you are using the hidraw implementation, then do something like the
    # following, substituting the VID and PID with your device. Busnum 1 is USB.
    
    # HIDAPI/hidraw
    KERNEL=="hidraw*", ATTRS{busnum}=="1", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="000b", MODE="0666", GROUP="plugdev*
    
    # Once done, optionally rename this file for your device, and drop it into
    # /etc/udev/rules.d and unplug and re-plug your device. This is all that is
    # necessary to see the new permissions. Udev does not have to be restarted.
    
    # Note that the hexadecimal values for VID and PID are case sensitive and
    # must be lower case.
    
    # If you think permissions of 0666 are too loose, then see:
    # http://reactivated.net/writing_udev_rules.html for more information on finer
    # grained permission setting. For example, it might be sufficient to just
    # set the group or user owner for specific devices (for example the plugdev
    # group on some systems).
    
    
    • Using LibUsb Wrapper for Lazarus.
    • My Lazarus Test program compiles with no warnings.
    Problem:

    When I run my test program:
    Code:
    procedure OpenLibusb();
    begin
       ctx := nil;
    
      returncode:=libusb_init(@ctx);       //init libusb API
      libusb_set_debug(ctx,3);             //set debug
    
      cnt := libusb_get_device_list(ctx,@devs);
      dev_handle:= libusb_open_device_with_vid_pid(ctx, $04D8, $000B); //hid lib test
      libusb_free_device_list(devs, 1); //free the list, unref the devices in it
    
       //kernel driver attaching problem
       if (libusb_kernel_driver_active(dev_handle, 0) = 1) then  //find out if kernel driver is attached >>> Error Occurs Here!!!
    
    ....
    
    variable cnt gets 4 (meaning it found 4 USB related devices - as shown in lsusb command) but the variable dev_handle receives NIL, causing an Acces Violation on the next line - libusb_kernel_driver_active(dev_handle, 0)

    Questions:
    1. How to solve this?
    2. Is there any easier way to make it run in Lazarus?
    Please, remember I'm not that "Linux Guy" and any advice is welcomed!

    Thanks in advance!

    PS: @Ph!d , you've saved me before.... Maybe you can pull another rabbit from that old hat of yours.. ;)
     
  2. Ph!d

    Ph!d is a Trusted Warez PosterPh!d Board Protector Staff Member Administrator DEV Guild Reverser Translator

    Joined:
    Sep 11, 2008
    Messages:
    62,410
    Likes Received:
    126,950
    At first sight, it seems like a permissions issue.
    Can you verify you have the rights to read/write to it? Did you run the test program with sudo?

    Searching on the net I read that libusb_open_device_with_vid_pid finds and opens the device but doesn't return an error message if it fails. Maybe you could use another function which could return a more detailed error message and hence have a better idea of what could be the issue?
     
  3. Especialista

    Especialista is a Trusted Warez PosterEspecialista B4A Hitman Staff Member Super Moderator DEV Guild Reverser Translator

    Joined:
    Jul 15, 2011
    Messages:
    15,079
    Likes Received:
    44,087
    "Non Linux Guy" Question: How to do it? o_O

    I've opened a terminal and used "gksudo" to run it. Seems it found and attached my device to the program, but gave me anoter acces violation in another part I cannot verify right now, since I did not run it from Lazarus.

    Another question: How shall I proceed to run Lazarus with root permissions, so I'll be able to debug?
    Or how shall I proceed to give permitions to the device to non root users?
     
  4. Especialista

    Especialista is a Trusted Warez PosterEspecialista B4A Hitman Staff Member Super Moderator DEV Guild Reverser Translator

    Joined:
    Jul 15, 2011
    Messages:
    15,079
    Likes Received:
    44,087
    Hey, @Ph!d !

    After 9837 coffes, It seems I got it workng.
    There was a typo in my udev rules file. Here's the correct one:
    Code:
    ###########################################################################
    ## this file must be stored in: /etc/udev/rules.d
    ###########################################################################
    
    # This is a sample udev file for HIDAPI devices which changes the permissions
    # to 0666 (world readable/writable) for a specified device on Linux systems.
    
    # If you are using the libusb implementation of hidapi (hid-libusb.c), then
    # use something like the following line, substituting the VID and PID with
    # those of your device. Note that for kernels before 2.6.24, you will need
    # to substitute "usb" with "usb_device". It shouldn't hurt to use two lines
    # (one each way) for compatibility with older systems.
    
    # HIDAPI/libusb
    SUBSYSTEM=="usb", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="000b", MODE="0666", GROUP="plugdev"
    
    # If you are using the hidraw implementation, then do something like the
    # following, substituting the VID and PID with your device. Busnum 1 is USB.
    
    # HIDAPI/hidraw
    KERNEL=="hidraw*", ATTRS{busnum}=="1", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="000b", MODE="0666", GROUP="plugdev"
     
    # Once done, optionally rename this file for your device, and drop it into
    # /etc/udev/rules.d and unplug and re-plug your device. This is all that is
    # necessary to see the new permissions. Udev does not have to be restarted.
    
    # Note that the hexadecimal values for VID and PID are case sensitive and
    # must be lower case.
    
    # If you think permissions of 0666 are too loose, then see:
    # http://reactivated.net/writing_udev_rules.html for more information on finer
    # grained permission setting. For example, it might be sufficient to just
    # set the group or user owner for specific devices (for example the plugdev
    # group on some systems).
    
    Now I do not need sudo anymore to access the device.
    I'll test it reading/writing stuff and see if it is really working!

    Thanks!
     
  5. Ph!d

    Ph!d is a Trusted Warez PosterPh!d Board Protector Staff Member Administrator DEV Guild Reverser Translator

    Joined:
    Sep 11, 2008
    Messages:
    62,410
    Likes Received:
    126,950
    That's a lot of coffees from 10:20 to 15:36... :eek:
    Anyway, glad you got it working now! I mentioned running as sudo since it's the higher privilege, you make sure it is communicating with the device and go down from there. ;)
     
  6. Especialista

    Especialista is a Trusted Warez PosterEspecialista B4A Hitman Staff Member Super Moderator DEV Guild Reverser Translator

    Joined:
    Jul 15, 2011
    Messages:
    15,079
    Likes Received:
    44,087
    Since I'm using a 5" touch display and it consumes most of GPIO pins, I tought it would be the smart way to do that.
    But the data exchange between RPi and PIC18 is very simple, something like "How are you?" and "I'm fine thanks".

    Technically speaking, I'll use the PIC18 as a Digital Input card. RPi asks it the status of its ports and it will send back to RPi 4 bytes, one for each port.

    I'm sure it is not like rocket science, but I'm failing to find where I'm missing the point.
     
    Chesire Cat likes this.
  7. Especialista

    Especialista is a Trusted Warez PosterEspecialista B4A Hitman Staff Member Super Moderator DEV Guild Reverser Translator

    Joined:
    Jul 15, 2011
    Messages:
    15,079
    Likes Received:
    44,087
    For those who arrived at this moment with the same problem, here's the solution I developed to commuticate a linux device with a PIC18F2550/4550 HID device:

    1 - Install libusb (and it's dependencies) in your linux device (I use Debian/Raspbian Strech) using :
    Code:
    sudo apt-get update
    sudo apt-get upgrade
    sudo apt-get install libusb-dev
    2 - For those who use Lazarus (FPC), here's how to send/receive bytes to/from the uController:

    2.1 - Download HidApi.pas from here and put the file in your search path

    2.2 - Use this thread to read/write data continuosly from/to the PIC:

    Code:
    unit uUSBThread;  // Brought to you by Especialista@board4all.biz
    
    {$mode objfpc}{$H+}
    
    interface
    
    uses
      Classes, SysUtils, hidapi;
    
    const
      // HID Vendor/Product Constants
      cVendorID           = $1234;  // use your vendor ID
      cProductID          = $1234;  // use your product ID
    
    type
      // Declare a TReport type - used to communicate via USB to the PIC18
      TReport = packed record
        ReportID: Byte;
        Bytes:    array [0..63] of Byte;
      end;
    
      // exceptions
      EUSBNotCreatedException = Class(Exception);
      EUSBSendException = Class(Exception);
    
      // procedures
      TNewUsbDataEvent = procedure(NewData: TReport) of Object;
    
      TUSBThread = class(TThread)
      private
        FOnNewUsbData: TNewUsbDataEvent;
        trUSBData: TReport;
        udUSBDevice: PHidDevice;
        procedure NewUSBData;
      protected
        procedure Execute; override;
      public
        constructor Create(CreateSuspended : Boolean);
        property OnNewUSBData: TNewUsbDataEvent read FOnNewUsbData write FOnNewUsbData;
      end;
    
    implementation
    
    constructor TUSBThread.Create(CreateSuspended : boolean);
    begin
      // create thread
      FreeOnTerminate := True;
      Priority := tpNormal;
      inherited Create(CreateSuspended);
    
      // create USB device
      udUSBDevice := THidDevice.Open(cVendorID, cProductID, '');
    
      // check if USB device has been created
      if (udUSBDevice = nil) then begin
        raise EUSBNotCreatedException.Create(Format('Error opening USB device %0.4X:%0.4X!', [cVendorID, cProductID]));
      end;
    
      // set unblock reads
      udUSBDevice^.SetNonBlocking(1);
    end;
    
    // this method is executed by the mainthread and can therefore access all GUI elements.
    procedure TUSBThread.NewUSBData;
    begin
      if Assigned(FOnNewUsbData) then begin
        FOnNewUsbData(trUSBData);
      end;
    end;
    
    procedure TUSBThread.Execute;
    var
      iRead:        SizeInt;
      iWritten:     SizeInt;
      rReport:      TReport;
    begin
      // fill report with nulls
      FillChar(rReport, SizeOf(rReport), $00);
    
      // start data transfer
      rReport.ReportID := 0;
      rReport.Bytes[0] := First_byte_to_be_sent;
      ...
      rReport.Bytes[63] := Last_byte_to_be_sent;
      iWritten := udUSBDevice^.Write(rReport, SizeOf(rReport));
    
      // check if USB has received data
      if (iWritten <= 0) then begin
        raise EUSBSendException.Create(Format('Error sending data to the USB Device %0.4X:%0.4X!', [cVendorID, cProductID]));
      end;
    
      // thread main loop
      while (not Terminated) do begin
        // read data
        iRead := udUSBDevice^.Read(trUSBData, SizeOf(trUSBData));
    
        // check for new data
        if iRead > 0 then begin
          Synchronize(@NewUSBData);
        end;
      end;
    end;
    
    end.
    2.3 - In your main form:
    Code:
    procedure TfrmMain.FormCreate(Sender: TObject);
    begin
      // create USB thread
      utUSBThread := TUSBThread.Create(True);
    
      // set thread event function
      utUSBThread.OnNewUSBData := @Self.NewUSBDataArrived;
    
      // start thread
      utUSBThread.Start;
    end;
    
    procedure TfrmMain.NewUSBDataArrived(NewData: TReport);
    var
      I:               Integer;
      Str:             String;
    begin
      // initialize string
      Str := '';
    
      // convert from TReport
      for I := 0 to SizeOf(NewData)-1 do begin
       Str := Str + String(Chr(NewData.Bytes[I]));
      end;
    
      // Variable 'Str' contains all data sent by the uController.
    end;
    
    3 - Ace in the Hole: for PIC18F USB devices, the function HID_Write is a blocking function, i.e., it stops the flow of the program until it is finished. So, to circunvent possible freezes in the uC program, make a timer using interruption system and put the function (I'm using mikroC IDE) USB_Break when this timer reaches 0. Reset the timer (I'm using 30ms on a 10ms interrupt system) every time you get a successful HID_Write command.

    4 - Final Notes:

    4.1 - You can repeat the "start data transfer" part every time you need to send data do the uController.

    4.2 - Every time data arrives from uController, function "TfrmMain.NewUSBDataArrived(NewData: TReport)" will be trigered.

    4.3 - Use functions HID_Read and HID_Write on the uController to receive/send data over USB.

    That's all for now.
     
    Last edited: Oct 26, 2018
    tonyweb, Chesire Cat and Youpi like this.
  8. Especialista

    Especialista is a Trusted Warez PosterEspecialista B4A Hitman Staff Member Super Moderator DEV Guild Reverser Translator

    Joined:
    Jul 15, 2011
    Messages:
    15,079
    Likes Received:
    44,087
    Sorry Pal... It's just sketch. You'll have to adapt it to your needs.
    uGlobais is a personal file and has nothing related to the rest.
     
    Chesire Cat likes this.