Reputation: 33
I would like to be able to access an FTDI serial-to-usb bridge device plugged into a host computer from within a Docker container. From there, I am using a Python script with the pyusb and libusb libraries to process the USB output. That way, I can plug the FTDI board with its attached devices into some computer, run the docker container, and process the data. The container is built using docker-compose.
How we got here
As an earlier test on my host computer, I wrote the following Python script which will run at the start of my design:
import usb.core
import usb.util
# Find the USB device
device = usb.core.find(idVendor = 0x0403, idProduct = 0x601c)
# Check if the device was found. If not, raise an error. If the device was found, print out its info.
if (device == None):
raise ValueError('Device not found')
else:
print(device)
This printed out all the information on the board as expected - manufacturer, interfaces, endpoints, ect. (I was able to find the vendor and product IDs using lsusb -D /dev/bus/usb/003/007
, where the numbers were the bus and device numbers given by lsusb
).
I then went into my docker container in vscode, installed the two libraries, and ran the same script. This time, I got an error:
usb.core.NoBackendError: No backend available
I went to the pyusb FAQ at https://github.com/pyusb/pyusb/blob/master/docs/faq.rst
and made sure that I didn't have one of the common causes of the error. The error persisted even after I used usb.backend.libusb1.get_backend(...)
to specify the backend library by hand.
I came to the realization that a root cause of the problem was that the docker container had no way to access the FTDI USB device in the first place. With a week's worth of experience in Docker, I think that I need to mount the USB device on my host computer to the container using
What has been tried
In my service in the docker-compose file, I've tried to specify the mounting location of the device using the following:
devices:
- "/dev/serial/by-id/<link>:/dev/ttyUSB0"
privileged: true
To find , I went into the /dev/serial/by-id/
directory and used dmesg | grep tty
. It displayed a new entry whenever I plugged in a different USB device (Arduino), but did not have any new entries when I plugged in the FTDI board. Because of this, I doubt that my FTDI board is a TTY device, which most of the existing threads seem to focus around. I am not sure how else to give docker-compose what it needs to mount the device.
Because the pyusb library will find my device if it's given several USB devices, simply mounting all of the host USB ports should also solve my problem. I searched around this set of keywords as well, but didn't find much useful information.
TL:DR
How can I mount either an individual FTDI Serial-to-USB bridge device or all of the USB devices on my host computer to a docker container? I'd like to avoid using privileged
if possible. I've been working with Ubuntu, Docker, and Python for about a week so I may need it spelled out. Let me know if any more information is needed.
Thanks!
Upvotes: 3
Views: 2188
Reputation: 621
(Edited a few times as I've learned more.)
Did you ever figure this out? I just undertook a very similar exercise and it's working for me! My board is the FT232H (using the breakout from Adafruit). I mostly just followed the directions on Adafruit here.
I think the "no backend" error is about not having libusb installed. Not sure why that is, as you say you installed it... But here's the steps I took, as far as I think they're relevant:
RUN apk update && apk add libusb
. On full Ubuntu it would be RUN apt update && apt install libusb-1.0
as in the Adafruit guide.dev/serial/by-id
? (Now I'm not actually sure this step is necessary unless you also need non-root access in the container.) On the host, create the file /etc/udev/rules.d/99-ftdi.rules
with the contents:# /etc/udev/rules.d/99-ftdi.rules
SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6001", GROUP="plugdev", MODE="0666"
SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6011", GROUP="plugdev", MODE="0666"
SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6010", GROUP="plugdev", MODE="0666"
SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6014", GROUP="plugdev", MODE="0666"
SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6015", GROUP="plugdev", MODE="0666"
/dev/serial/by-usb
that I can add to the compose file as you expected. Those lines in my file are:devices:
- "/dev/serial/by-id/usb-0403_6014-if00-port0:/dev/ftdi"
Now the device is visible to pyusb in the container (for me).
RUN groupadd plugdev && usermod -a -G plugdev abc
I also don't think you need privileged: true
since you're using devices
. Haven't tested this yet, but I plan to.
You might also check out the rest of the Adafruit guide linked above. It uses a couple more Python libraries (pyftdi and adafruit-blinka) that were exactly what I needed to control a GPIO pin with a single line of Python. But your use case might be much more complicated than mine!
Upvotes: 0