Detect when new USB device is connected (plug'n'play) - python

Using this answer, we are able to query all the USB devices connected at a precise moment.
I have a Python program running on Linux (Debian or RaspBian) that does a specific task, but I also want that this program listens if new USB device is connected, and when this happens, trigger a specific action.
I'm thinking about doing a new thread that does:
while True:
list_USB_devices() # using https://stackoverflow.com/a/8265634/1422096
see_if_new_devices_in_this_list()
time.sleep(2) # wait 2 seconds
but I don't find this solution very elegant.
What's a cleaner solution to detect in the background of a Python program if a new USB device is connected?
Example of application for my program: listen if a new USB-MIDI keyboard/device is connected, and if so, attach it with rtmidi-python "Plug and play!"

Look into the gio library (part of glib). You can attach watches and connect callbacks when devices are created. This way you don't have to poll at all. Set a watch on the devices directory, look for file creation. Filter out files not interested in.
You can probably also look at 'udev' system itself, and write a rule to execute something on apparition of a new usb device.

Related

Using Python and pyserial to run python scripts on a pico (RP2040)

My ultimate goal is to control a number of peripheral chips on a PCB from a GUI interface on a PC. To do so, my plan was to incorporate a RP2040 (and memory) on the PCB in order to hold all the python scripts and to program/monitor all the peripheral chips. Then, using a PC to interface with the RP2040, send commands over the serial port to execute specific python files on the pico.
I realize that is a bit confusing, so the attached block diagram should help.
Block diagram
Starting on the left of the block diagram, I have a PC running a tkinter GUI. I am currently running the tkinter gui in Thonny. (eventually i would like it to be an executable, but that is beyond the scope of this post) The gui has a number of buttons to choose which python scripts to run. The PC is connected to the PCB through the USB cable. The USB data lines are routed to the RP2040's USB inputs (pins 47,48). The memory on the PCB holds a number of python scrips that correspond to the buttons in the GUI. Ideally, pressing a button on the PC would execute the corresponding py file on the pcb.
What I've got working so far:
My real expertise lies in peripheral chips and PCB design, in this case the front end for a 2-18GHz transceiver, so bare with me if some of my python questions seem basic or misinformed. I've written and tested all the .py files on the pico's memory. To test those scripts I used Thonny to connect to my pico and simply ran (f5) the scripts with the peripherals connected to the right GPIOs. I was also able to get tkinter working and create functioning buttons that can execute commands. Using the pyserial module, I am also able to connected to the pico through the USB and write... strings. Not very useful, but a start.
import serial
ser = serial.Serial('COM3', 38400, timeout=1, parity=serial.PARITY_EVEN, rtscts=1)
s = ser.read(100) # read up to one hundred bytes or as much is in the buffer
print(ser.name) # check which port was really used
ser.write(b'ToggleLED.py') # write a string
ser.close() # close port
Remaining task: The final task I have been failing miserably at the past 2 days has been actually trying to execute the .py files located on the pico's memory through the serial port. My unexperienced/naïve notion was to simply send a string with the files name, obviously not correct. Any thoughts on how to execute those py files using that pyserial module?
BTW, if there is a better solution, please feel free to share! Perhaps the files should be located on the PC and i send 1 command at time?
I can't say anything about your serial problems until you clarify just what is running on the pi (see my comment: I'll update this answer when you do), but re 'is there a better way': possibly.
Since the Pi is running a full operating system, there are a few options. You are basically creating a network connection to the Pi. Whilst this can be done over serial (with the Pi, presumably, acting as a fake USB serial device), it can also be done more conventionally over wifi or ethernet. Lastly, you could host your interface on the Pi and interact with it in a webbrowser, cutting the second computer out of the picture. Exactly which option you decide to take is up to you, and is really off topic here (though it might be on topic elsewhere on SE).
Sending commands to the pi and having it run scripts is Remote Procedure Call. You might want to lookup some of the protocols (like JSON-RPC) generally used to do that, but the basic approach will have code running on the pi:
def do_something():
pass
def do_something_else():
pass
functions = {"something": do_something, "something_else": do_something_else}
while True:
cmd = get_input() # blocks until input comes
if cmd in functions:
reply(f"Running {cmd}")
output = functions[cmd]()
reply(f"{cmd} returned with output {output}")
else:
reply(f"Invalid command {cmd}")
This is schematic: exactly what get_input() on the pi is will depend on how you end up connecting, and what protocol (if any) you end up using. Notice that I have built in confirmation: you want to know if things work or fail.
Whilst you could store these commands in separate scripts and invoke them, if they are just python scripts there is no reason not to call the functions directly from the code running on the pi.
I've seen two different solutions to the question:
With the Pi pico running the REPL (python shell), text strings can simply be sent from a python program running on the host (Windows or Raspberry Pi, etc., connected to the USB com port, with Thonny window closed), and sent to the pico as python commands. This approach is detailed in:
https://blog.rareschool.com/2021/01/controlling-raspberry-pi-pico-using.html
On the pico side, one just need to define a number of functions that will execute from the REPL. E.g.,:
from machine import Pin
# use onboard LED which is controlled by Pin 25
led = Pin(25, Pin.OUT)
# Turn the LED on
def on():
led.value(1)
# Turn the LED off
def off():
led.value(0)
Any string sent to the pico, such as
on()
will be executed accordingly.
In the 2nd approach, the pico will read each line from the USB serial port and parse the line into command and additional parameters, to be compared against a list of predefined commands and executed accordingly. I did that using the Arduino Due, and I'm in the process of porting it to the pico (= no code to show yet :-().

Is it possible to start barcode scanning from python on a barcode scanner?

I have a barcode scanner, and currently, it is working as a keyboard so if the scanner successfully scans by pushing the trigger the scanned code goes to the computer as input.
Now, I want to write a python program on my Raspberry Pi 3B which connects to the scanner and start the scanning process without the need of pushing the trigger on the scanner. Meaning that I make a GUI where only by just clicking on a button the user starts the scanning process and the scanned code (if the scan was successful) gets outputted.
The question is: how to do it?
I have tried pyusb but it can't send a command to the scanner to scan (Or I don't know how).
Even worse if it turned out that there is nothing like Python-Scanner communication, only the primitive connected || not connected type.
Depending on which operating system you use, you should look into the SDKs for the scanner you're using. It seems they provide some tools to control the scanner. They a re not very informative on what exactly they support though. ( for example: https://www.zebra.com/us/en/support-downloads/software/developer-tools/scanner-sdk-for-linux.html)
The reference manual for the serial interface I found here:
https://www.zebra.com/content/dam/zebra_new_ia/en-us/manuals/barcode-scanners/Simple%20Serial%20Interface%20Programmer's%20Guide.pdf

usb automatic detection in python for linux env

I'm using polling command(glob('/dev/tty[A-Za-z]*')) in python to detect usb devices connected to my linux pc in regular interval for my application. Is there any way to detect usb devices connected automatically?
Here is a start. You can find your usb vendor here. You got to code yourself a current_list_usb, set a time interval to check so you can compare and see if a new device is attached or not. Some code to use when importing usb module:
import usb, usb.core, usb.util, usb.backend.libusb1
...snippet...
# usb.core.find()
# find our device
dev = usb.core.find(idVendor= ...., idProduct= ....)
#dev_1 = usb.util.find_descriptor(cfg, find_all =True)
# was it found?
if dev is None:
raise ValueError('Device not found')
#x = dev.set_configuration()
#print (dev)
#print (help(usb.core))
if usb.core.find(find_all=True, bDeviceClass=7) is None:
raise ValueError('No printer found')
The normal way to do this is to make a udev rule that tells your program a new tty exists.
A custom udev rule may look something like this(let's call it /etc/udev/rules.d/50-custom-tty.rules:
KERNEL=="ttyUSB[0-9]+", RUN+="/usr/bin/my-program"
Here's a good guide on writing udev rules.
In this case, the program /usr/bin/my-program will run whenever a new ttyUSB device is created in /dev; udev will set a bunch of environment variables to tell you exactly what was just plugged in. You can then notify your main program that a new ttyUSB exists, and it should use it. Note that whatever program you run should be small, as otherwise the udev daemon will kill it if it takes too long.
I'd suggest using libudev and creating a udev monitor object to detect hotplugged devices. Here is a starting point for you to learn about libudev and its monitor feature:
https://www.freedesktop.org/software/systemd/man/libudev.html
There might be a good Python library already that wraps udev so you can use its features without writing C code.

Simulate keyboard input linux

I work on a project to control my PC with a remote, and a infrared receptor on an Arduino.
I need to simulate keyboard input with a process on linux who will listen arduino output and simulate keyboard input. I can dev it with Python or C++, but i think python is more easy.
After many search, i found many result for... windows u_u
Anyone have a library for this ?
thanks
EDIT: I found that /dev/input/event3 is my keyboard. I think write in to simulate keyboard, i'm searching how do that
To insert input events into the Linux input subsystem, use the user-mode input device driver, uinput. This might help: http://thiemonge.org/getting-started-with-uinput (Note that while the tutorial references /dev/input/uinput, the correct file on my Ubuntu 12.04 PC is /dev/uinput.
The most generic solution is to use pseudo-terminals: you connect tttyn to the standard in and standard out of the program you want to monitor, and use pttyn to read and write to it.
Alternatively, you can create two pipes, which you connect to the standard in and standard out of the program to be monitored before doing the exec. This is much simpler, but the pipes look more like a file than a terminal to the program being monitored.

Can a device have two UMDF Drivers?

This may seem like a bit of an odd question, but I was wondering if it is possible to associate two drivers with a peripheral device?
The reason I ask is that I am building an input device for Maya using an Arduino microcontroller. The Arduino already has its own device driver, but I was thinking of developing a UMDF driver to take the data that comes in from Arduino over the serial port and pre-process it ready to go into Maya.
Right now, I have two Python programs running - a 32-bit Python program running outside of Maya which listens to the serial port and converts the data to a form which a second 64-bit program inside the 64-bit version of Maya can understand and use in the Maya scene. This works fine, but it is a bit annoying having to start that external server program every time I want to use this device in Maya. If I can have a UMDF driver ready to jump into action when the appropriate type of data comes in off the Arduino then this would help immensely. Will this approach work?
It's more a comment/suggestion than an answer, but maybe it would be worth to invest some time and check if the filter driver would do the job for you. In WDM you can put it above the kernel device driver on the driver stack for that device, and use it to pre-process your device data. I think it is also possible in UMDF.
See Creating a New Filter Driver (MSDN).
You may always try to use Teensy/Teensyduino instead of Arduino to implement a virtual keyboard, mouse, joystick or other HID device. This does not require Windows drivers, and accessing the keyboard or joystick from Maya may be easier that the serial port.

Categories