DiluMarku_2-1645694636882.png

Using the libusb library within a snap to access USB devices connected to ctrlX CORE

DiluMarku
Established Member


Overview

This blog will show you how to setup snapcraft.yaml to access USB devices connected to ctrlX CORE. In addition to that, it shows an example of a Python snap, which uses the PyUSB module [wrapper for libusb] to read in data from a USB mouse.

You can also download the full project, which includes the providing of the mouse data on the ctrlX Data Layer, by clicking here.

Prerequisites
  • Setup development environment for ctrlX AUTOMATION 
  • Python USB access module
    pip install pyusb​
  • Some basic understanding of Snapcraft
  • The Vendor ID and the Product ID of the connected USB device are needed to be able to access the specific device you want to read in data from. The easiest way to find these is to connect the USB device to your PC and look it up according to your OS.
    • On Linux: Type in the following command in a terminal window.
      usb-devices​

      This will show you the information about all of your connected USB devices. Just look for the corresponding device. For this example: 

      T:  Bus=01 Lev=01 Prnt=01 Port=01 Cnt=02 Dev#=  3 Spd=1.5 MxCh= 0
      D:  Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
      P:  Vendor=17ef ProdID=608d Rev=01.00
      S:  Manufacturer=PixArt
      S:  Product=Lenovo USB Optical Mouse
      C:  #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=100mA
      I:  If#=0x0 Alt= 0 #EPs= 1 Cls=03(HID  ) Sub=01 Prot=02 Driver=usbhid
    • On Windows: How do I find the USB Vendor ID and Product ID? 

Implementation

Here, the setup of the snapcraft.yaml file is done before the implementation of the Python script. But you can do it the other way around as well.

Snapcraft YAML File Setup
  1. Open a terminal window, change the working directory to where you want to build the snap and type in the following command:
    snapcraft init

    This should give a YAML file similiar to this:

    name: my-snap-name # you probably want to 'snapcraft register <name>'
    base: core20 # the base snap is the execution environment for this snap
    version: '0.1' # just for humans, typically '1.2+git' or '1.3.2'
    summary: Single-line elevator pitch for your amazing snap # 79 char long summary
    description: |
      This is my-snap's description. You have a paragraph or two to tell the
      most important story about your snap. Keep it under 100 words though,
      we live in tweetspace and your description wants to look good in the snap
      store.
    
    grade: devel # must be 'stable' to release into candidate/stable channels
    confinement: devmode # use 'strict' once you have the right plugs and slots
    
    parts:
      my-part:
        # See 'snapcraft plugins'
        plugin: nil
  2. Change grade and confinement of the snap:
    grade: stable
    confinement: strict 
    
  3. Define the parts from which the snap is built and make sure to include the following packages:
    parts:
      provider:
        plugin: python
        source: .
        build-packages:
          - libusb-1.0-0
        stage-packages:
          - libusb-1.0-0
        python-packages:
          - pyusb
  4. Define the apps provided by the snap and make sure to include at least these three plugs in order to give this snap the permissions needed to access the USB device:
    apps:
      provider:
        command: bin/main.py
        plugs:
          - network
          - raw-usb
          - hardware-observe
        daemon: simple
        passthrough:
          restart-condition: always
          restart-delay: 10s
  5. In addition to the snapcraft.yaml file, you need to create a setup.py script for the Python plugin to work properly and define in it which Python script is supposed to be used for this project. Simple setup.py for this example:
    from setuptools import setup
    
    setup(scripts = ['main.py'])​
 
Python Script
  1. Import the following modules:
    import usb.core
    import usb.util
    import usb.backend.libusb1
  2. Reference explicitly the libusb library within the snap to get access to hardware:
    backend = usb.backend.libusb1.get_backend(
        find_library=lambda x: "/snap/my-snap-name/current/lib/aarch64-linux-gnu/libusb-1.0.so.0")
  3. Find the connected USB device using the Vendor ID and the Product ID from above:
    dev = usb.core.find(idVendor=0x17ef, idProduct=0x608d, backend=backend)
  4. Get the endpoint of the device and make sure to detach the connection to the OS kernel in order to claim it for this app:
    interface = 0
    endpoint = dev[0].interfaces()[0].endpoints()[0]
    dev.reset()
    if dev.is_kernel_driver_active(interface):
        dev.detach_kernel_driver(interface)
        usb.util.claim_interface(dev, interface)​​
  5. Loop to read in data from the USB device more than once:
    while True:
        try:
            data = dev.read(endpoint.bEndpointAddress,endpoint.wMaxPacketSize)
            print(data)
            # put your own code here
            # for example datalayer access to present the data
        except usb.core.USBError as e:
            data = None
            if e.args == ('Operation timed out',):
                continue​
 
Related Topics
Must Read
Icon--AD-black-48x48Icon--address-consumer-data-black-48x48Icon--appointment-black-48x48Icon--back-left-black-48x48Icon--calendar-black-48x48Icon--center-alignedIcon--Checkbox-checkIcon--clock-black-48x48Icon--close-black-48x48Icon--compare-black-48x48Icon--confirmation-black-48x48Icon--dealer-details-black-48x48Icon--delete-black-48x48Icon--delivery-black-48x48Icon--down-black-48x48Icon--download-black-48x48Ic-OverlayAlertIcon--externallink-black-48x48Icon-Filledforward-right_adjustedIcon--grid-view-black-48x48IC_gd_Check-Circle170821_Icons_Community170823_Bosch_Icons170823_Bosch_Icons170821_Icons_CommunityIC-logout170821_Icons_Community170825_Bosch_Icons170821_Icons_CommunityIC-shopping-cart2170821_Icons_CommunityIC-upIC_UserIcon--imageIcon--info-i-black-48x48Icon--left-alignedIcon--Less-minimize-black-48x48Icon-FilledIcon--List-Check-grennIcon--List-Check-blackIcon--List-Cross-blackIcon--list-view-mobile-black-48x48Icon--list-view-black-48x48Icon--More-Maximize-black-48x48Icon--my-product-black-48x48Icon--newsletter-black-48x48Icon--payment-black-48x48Icon--print-black-48x48Icon--promotion-black-48x48Icon--registration-black-48x48Icon--Reset-black-48x48Icon--right-alignedshare-circle1Icon--share-black-48x48Icon--shopping-bag-black-48x48Icon-shopping-cartIcon--start-play-black-48x48Icon--store-locator-black-48x48Ic-OverlayAlertIcon--summary-black-48x48tumblrIcon-FilledvineIc-OverlayAlertwhishlist