HID UPS based on Arduino Leonardo/MIcro doesnt work on Ubuntu 18.04 LTS

I'm developing a custom HID-compliant UPS based on Arduino Micro. When I connect it to Apple OSX (I'm using Macbook as a host machine) or Windows 10 VM machine, my "UPS" is correctly determined by the operating system and I can also report remaining battery capacity and other parameters back to the host.
However, when I plug it to Ubuntu 18.04 LTS, it is not working (not showing up in the Power settings). At the same time, the commercial APC UPS works just fine under Ubuntu. Posting of lsusb is below:

abratchik@ubuntu-parallels-vm:~$ lsusb
Bus 001 Device 003: ID 203a:fffa  
Bus 001 Device 002: ID 203a:fffa  
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 004 Device 002: ID 203a:fff9  
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 004: ID 2341:8036 Arduino SA Leonardo (CDC ACM, HID)
Bus 002 Device 006: ID 051d:0003 American Power Conversion UPS
Bus 002 Device 003: ID 203a:fffe  
Bus 002 Device 002: ID 203a:fffc  
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub


abratchik@ubuntu-parallels-vm:~$ lsusb -t
/:  Bus 04.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/12p, 5000M
    |__ Port 1: Dev 2, If 0, Class=Video, Driver=uvcvideo, 5000M
    |__ Port 1: Dev 2, If 1, Class=Video, Driver=uvcvideo, 5000M
/:  Bus 03.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/2p, 480M
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=uhci_hcd/2p, 12M
    |__ Port 1: Dev 2, If 0, Class=Human Interface Device, Driver=usbhid, 12M
    |__ Port 1: Dev 2, If 1, Class=Human Interface Device, Driver=usbhid, 12M
    |__ Port 2: Dev 3, If 0, Class=Hub, Driver=hub/15p, 12M
        |__ Port 5: Dev 4, If 2, Class=Human Interface Device, Driver=usbhid, 12M
        |__ Port 5: Dev 4, If 0, Class=Communications, Driver=cdc_acm, 12M
        |__ Port 5: Dev 4, If 1, Class=CDC Data, Driver=cdc_acm, 12M
        |__ Port 4: Dev 6, If 0, Class=Human Interface Device, Driver=usbhid, 12M
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/15p, 480M
    |__ Port 1: Dev 2, If 0, Class=Printer, Driver=usblp, 480M
    |__ Port 2: Dev 3, If 0, Class=Printer, Driver=usblp, 480M

In the first printout, my custom UPS is coming with ID 2341:8036 and APC UPS is 051d:0003. Second printout also shows both of them identified correctly as HID device (Bus 02 port 5 interface 2 for my UPS and port 4 interface 0 for APC) and both are using usbhid driver, which is also correct and as expected.
However, APC is correctly detected by Ubuntu as UPS while my UPS is not showing up at all:

parallels-vm:~$ upower -e
/org/freedesktop/UPower/devices/line_power_ADP0
/org/freedesktop/UPower/devices/battery_BAT0
/org/freedesktop/UPower/devices/ups_hiddev3
/org/freedesktop/UPower/devices/DisplayDevice

abratchik@ubuntu-parallels-vm:~$ upower -i /org/freedesktop/UPower/devices/ups_hiddev3
  native-path:          /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-2/2-2.4/2-2.4:1.0/usbmisc/hiddev3
  vendor:               American Power Conversion 
  model:                Smart-UPS C 1500 FW:UPS 10.0 / ID=1005
  serial:               3S1838X02676  
  power supply:         yes
  updated:              Thu 03 Dec 2020 10:00:04 AM +04 (-11 seconds ago)
  has history:          yes
  has statistics:       yes
  ups
    present:             yes
    state:               fully-charged
    warning-level:       none
    time to empty:       2.0 hours
    percentage:          100%
    icon-name:          'battery-full-charged-symbolic'

Also RX/TX LEDs are not blinking on Arduino board so looks like there is no exchange between the board and the VM. The same board is working just fine under OSX and Windows 10 as I mentioned above.
When I check communication during the handshake between my device and the host with Wireshark, it seems like all events are stopping after usbhid driver gets the HID descriptor. But the descriptor itself can be correctly dumped using the usbhid-dump utility and parsing of it also works perfectly fine. I suspect if the descriptor were an issue it would not work under OSX and Windows as well.
dmesg also gives nothing unnusual about my device - messages during the handshake look the same as APC:

[239474.147682] usb 2-2.6: New USB device found, idVendor=2341, idProduct=8036, bcdDevice= 1.00
[239474.147684] usb 2-2.6: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[239474.147685] usb 2-2.6: Product: Arduino Leonardo
[239474.147686] usb 2-2.6: Manufacturer: Arduino LLC
[239474.147687] usb 2-2.6: SerialNumber: HIDKN
[239474.194582] cdc_acm 2-2.6:1.0: ttyACM0: USB ACM device
[239474.209496] hid-generic 0003:2341:8036.0038: hiddev2,hidraw2: USB HID v1.01 Device [Arduino LLC Arduino Leonardo] on usb-0000:00:1d.0-2.6/input2
[239581.283918] usb 2-2.4: new full-speed USB device number 35 using uhci_hcd
[239581.397670] usb 2-2.4: New USB device found, idVendor=051d, idProduct=0003, bcdDevice= 1.06
[239581.397674] usb 2-2.4: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[239581.397675] usb 2-2.4: Product: Smart-UPS C 1500 FW:UPS 10.0 / ID=1005
[239581.397676] usb 2-2.4: Manufacturer: American Power Conversion 
[239581.397676] usb 2-2.4: SerialNumber: 3S1838X02676  
[239581.481215] hid-generic 0003:051D:0003.0039: hiddev3,hidraw3: USB HID v1.00 Device [American Power Conversion  Smart-UPS C 1500 FW:UPS 10.0 / ID=1005] on

I googled a bit and found that many articles on this subject are referring to usbhid-ups driver, which is not installed by default and requires some configuration as I understood. I really would like to avoid this and make my UPS fully "plug-and-play", similar to APC UPS. Already lost of ideas what could be different for my UPS, except may be Vendor ID/Product ID but that would be my last option to try and change these. Any hint what I'm missing is much appreciated.

Finally I understood how it works. The usbhid driver in Linux was not guilty, it is just low-level parser of HID protocol and don't really do any intelligent job other than de-ciphering the device responses and passing them over to the managing layer, which is udev (Linux Kernel Device Manager) and UPower manager, which is sitting on top of udev.
The UPower actually defines a list of all supported Vendor ID/Product ID and feeds it to udev in form of udev rules. So I was right - Vendor ID/Product ID does matter in this case - if you are APC then your UPS will be detected automatically, otherwise the device is simply ignored by the UPower even if it is HID-compliant.
Fortunately, there is a workaround. The udev rules are stored in the /etc/udev/rules.d/ folder in text format so additional rules can be defined easily. One have to create the file 98-upower-hid.rules (I assume exact file name is not that important) in this folder and add the following lines:

ATTRS{idVendor}=="2341", ENV{UPOWER_VENDOR}="Arduino"
ATTRS{idVendor}=="2341", ATTRS{idProduct}=="8036", ENV{UPOWER_BATTERY_TYPE}="ups"

Reboot. After that the Arduino Board with the UPS sketch is recognized by Ubuntu just fine.

Hello abratchik,

I found your post by searching the web for "arduino usbhid ups".

As I developed an off grid UPS for my devices, I also have the problem to implement a usbhid-ups on a microcontroller to have a more convenient implementation.

Did get any further with your project ? Is it available anywhere ?

Many thanks
Chris

P.S: Sorry . Did read your other posts and found your Github. Will give it a try ! Many thanks.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.