-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Linux / rPi not working with Pro Micro 3.3V 8MHz #1
Comments
I think it should work with usbhid-ups in theory as, on the lower level, this is just a USB HID Power Device interface specs implementation. May be the problem is in configuration of usbhid-ups as it picks up only specific models (vid/pid). I did not spend much time on it as the idea was to have the Arduino UPS working out-of-the-box on a vanilla system (Mac, Win or Linux), without deploying any additional packages like NUT. |
Fully agree, runs out of the box with "one" local machine as intended out-of-the-box on Mac, Win and Linux ! Thanks for that ! But .. can not get it working on Raspberry or QNAP NAS directly - both to monitor and react on UPS message. Even managing with vid/pid in ups.conf and rules.d is not getting the job done. If it would work on QNAP NAS as HID device - so my basic idea - it would be monitored for other devices in IP range by QNAP via NUT msg protocol. If anyone has an idea ? More than welcome. |
I am having the same problem. From what I am seeing it might require creating a custom sub driver for the usbhid-ups driver in NUT. https://networkupstools.org/docs/developer-guide.chunked/ar01s04.html#hid-subdrivers |
After some further digging if found that the usbhid-ups driver for NUT reports this when trying to connect to the arduino: 0.174690 Checking device (2341/8036) (002/018) Does anyone know why the driver would not be able to retrieve the HID descriptor? |
Well I guess the magic happens (or doesn't happen) in here where the NUT is trying to retrieve the HID descriptor for the UPS:
First it tries to send the control message to get the descriptor to the wrong pipe. I believe this is because they assume the 1st interface (Interface 0) in the device descriptor to be the one they need to talk to. Unfortunately, this is not the case - Arduino Leonardo/Micro reports itself through USB as a composite device and uses the Interface 2 for all HID projects, including HID UPS. Hence the "Broken Pipe" message. Their second method of retrieving the correct descriptor is very special and is to cater cases of "broken" UPS like Tripp lite Smart1000 LCD - they search for HID descriptor in the "extra" bytes of the Interface, while (wrongly) assuming the Interface 0 as the one they need to look at.
I guess making NUT to work with Arduino UPS requires fixing of the libusb. This whole piece of code there is quite ugly - they hardcode VID/PID for Eaton, hardcode interface numbers etc. May be, too many people hacked it quick and dirty for specific UPS models. |
Ok, so I found a parameter called usb_set_altinterface. According to the documentation this should allow me to change the USB interface that NUT is using. However, when I set this to interface 1 or interface 2 I get this output. 0.173726 Checking device (2341/8036) (002/004) 0.175940 - VendorID: 2341 0.175988 - ProductID: 8036 0.176028 - Manufacturer: Arduino LLC 0.176056 - Product: Arduino Leonardo 0.176079 - Serial Number: UPS10 0.176103 - Bus: 002 0.176127 - Device release number: 0100 0.176145 Trying to match device 0.176225 Device matches 0.176313 nut_usb_set_altinterface: calling usb_set_altinterface(udev, 1) 0.176386 nut_usb_set_altinterface: usb_set_altinterface(udev, 1) returned -22 (could not set alt intf 0/1: Invalid argument) 0.176420 nut_usb_set_altinterface: usb_set_altinterface() should not be necessary - please email the nut-upsdev list with information about your UPS. 0.176688 Unable to get HID descriptor (error sending control message: Broken pipe) 0.176760 HID descriptor length (method 1) -1 0.176797 i=0, extra[i]=05, extra[i+1]=24 0.176823 i=5, extra[i]=05, extra[i+1]=24 0.176849 i=10, extra[i]=04, extra[i+1]=24 0.176875 i=14, extra[i]=05, extra[i+1]=24 0.176900 HID descriptor length (method 2) -1 Could this be a problem with NUT or am I just getting things wrong |
This is the related line of code in libusb /*! If needed, set the USB alternate interface. * * In NUT 2.7.2 and earlier, the following call was made unconditionally: * usb_set_altinterface(udev, 0); * * Although harmless on Linux and *BSD, this extra call prevents old Tripp Lite * devices from working on Mac OS X (presumably the OS is already setting * altinterface to 0). */ static int nut_usb_set_altinterface(usb_dev_handle *udev) { int altinterface = 0, ret = 0; char *alt_string, *endp = NULL; if(testvar("usb_set_altinterface")) { alt_string = getval("usb_set_altinterface"); if(alt_string) { altinterface = (int)strtol(alt_string, &endp, 10); if(endp && !(endp[0] == 0)) { upslogx(LOG_WARNING, "%s: '%s' is not a valid number", __func__, alt_string); } if(altinterface < 0 || altinterface > 255) { upslogx(LOG_WARNING, "%s: setting bAlternateInterface to %d will probably not work", __func__, altinterface); } } /* set default interface */ upsdebugx(2, "%s: calling usb_set_altinterface(udev, %d)", __func__, altinterface); ret = usb_set_altinterface(udev, altinterface); if(ret != 0) { upslogx(LOG_WARNING, "%s: usb_set_altinterface(udev, %d) returned %d (%s)", __func__, altinterface, ret, usb_strerror() ); } upslogx(LOG_NOTICE, "%s: usb_set_altinterface() should not be necessary - please email the nut-upsdev list with information about your UPS.", __func__); } else { upsdebugx(3, "%s: skipped usb_set_altinterface(udev, 0)", __func__); } return ret; } |
I do not think the altinterface will help. As far as I understand, this is an alternative set of parameters, which can be defined for an interface (Interface 0 in this case). However, we need to make the NUT using the different interface number - Interface 2 in case of Arduino HID. Based on what I see in the code of usbhid-ups, I do not believe it is possible to configure this without forking NUT. |
Thank you for the article. Is there a way we could swap the interface numbers so that HID projects use interface 0 rather than 2. Forgive me if this is a dumb question I am new to all of this. |
I managed to change the interface number of CDC ACM and CDC Data by changing two definitions in the USBDesc.h file for the Arduino IDE. However, I can't figure out how to change the HID interface number. Original: #define CDC_ACM_INTERFACE 0 // CDC ACM #define CDC_DATA_INTERFACE 1 // CDC Data New: #define CDC_ACM_INTERFACE 3 // CDC ACM #define CDC_DATA_INTERFACE 4 // CDC Data lsusb -v output Original: Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 239 Miscellaneous Device bDeviceSubClass 2 bDeviceProtocol 1 Interface Association bMaxPacketSize0 64 idVendor 0x2341 Arduino SA idProduct 0x8036 Leonardo (CDC ACM, HID) bcdDevice 1.00 iManufacturer 1 iProduct 2 iSerial 3 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 0x006b bNumInterfaces 3 bConfigurationValue 1 iConfiguration 0 bmAttributes 0xa0 (Bus Powered) Remote Wakeup MaxPower 500mA Interface Association: bLength 8 bDescriptorType 11 bFirstInterface 0 bInterfaceCount 2 bFunctionClass 2 Communications bFunctionSubClass 2 Abstract (modem) bFunctionProtocol 0 iFunction 0 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 1 bInterfaceClass 2 Communications bInterfaceSubClass 2 Abstract (modem) bInterfaceProtocol 0 iInterface 0 CDC Header: bcdCDC 1.10 CDC Call Management: bmCapabilities 0x01 call management bDataInterface 1 CDC ACM: bmCapabilities 0x06 sends break line coding and serial state CDC Union: bMasterInterface 3 bSlaveInterface 4 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0010 1x 16 bytes bInterval 64 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 1 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 10 CDC Data bInterfaceSubClass 0 bInterfaceProtocol 0 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x02 EP 2 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x83 EP 3 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 5 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 3 Human Interface Device bInterfaceSubClass 0 bInterfaceProtocol 0 iInterface 0 HID Device Descriptor: bLength 9 bDescriptorType 33 bcdHID 1.01 bCountryCode 33 US bNumDescriptors 1 bDescriptorType 34 Report wDescriptorLength 400 New: Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 239 Miscellaneous Device bDeviceSubClass 2 bDeviceProtocol 1 Interface Association bMaxPacketSize0 64 idVendor 0x2341 Arduino SA idProduct 0x8036 Leonardo (CDC ACM, HID) bcdDevice 1.00 iManufacturer 1 iProduct 2 iSerial 3 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 0x006b bNumInterfaces 3 bConfigurationValue 1 iConfiguration 0 bmAttributes 0xa0 (Bus Powered) Remote Wakeup MaxPower 500mA Interface Association: bLength 8 bDescriptorType 11 bFirstInterface 0 bInterfaceCount 2 bFunctionClass 2 Communications bFunctionSubClass 2 Abstract (modem) bFunctionProtocol 0 iFunction 0 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 3 bAlternateSetting 0 bNumEndpoints 1 bInterfaceClass 2 Communications bInterfaceSubClass 2 Abstract (modem) bInterfaceProtocol 0 iInterface 0 CDC Header: bcdCDC 1.10 CDC Call Management: bmCapabilities 0x01 call management bDataInterface 1 CDC ACM: bmCapabilities 0x06 sends break line coding and serial state CDC Union: bMasterInterface 3 bSlaveInterface 4 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0010 1x 16 bytes bInterval 64 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 4 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 10 CDC Data bInterfaceSubClass 0 bInterfaceProtocol 0 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x02 EP 2 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x83 EP 3 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 0 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 5 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 3 Human Interface Device bInterfaceSubClass 0 bInterfaceProtocol 0 iInterface 0 HID Device Descriptor: bLength 9 bDescriptorType 33 bcdHID 1.01 bCountryCode 33 US bNumDescriptors 1 bDescriptorType 34 Report wDescriptorLength 400 |
I think manipulating with CDC interfaces is not very good idea as it may brick your Arduino board so that you will need to re-flash the boot loader to make it programmable again. However, if you still want to try make the HID UPS the first interface, you may look at PluggableUSB module. As far as I understand, the HID interface number is assigned in the PluggableUSB_::plug() method as num of interfaces + 1:
That is why HID is coming always as a 3rd interface after CDC. I guess you may tweak the Arduino USBCore and remove the CDC completely - this way you will get HID as your first interface. After that, you will need to re-flash the boot-loader if you want to re-program as the Arduino IDE wont be able to connect to the board without CDC. |
Managed to get this working with NUT (on TrueNas SCALE, tho it should work on any other os too). Quick note that this solution is really just multiple hacks, but hey, if it works it ain't stupid.
The NUT config file looks something like this:
(pollfreq, vendorid and productid can be added in "Auxiliary Parameters (ups.conf)" field in TrueNas) From here on, it's just like with any other UPS. |
@shadow578 wow, this is impressive work, thank you. I think this is a very valid approach if a specific model of UPS need to be simulated for testing purposes. |
@shadow578 ! Nicely done. Thanks for the update. Although I got it running on Ubuntu 20.x with the PID changes posted, I will also give this try. |
I ended up forking NUT as this seems to be more correct way of fixing this issue. You may try to build nut build from here - https://github.com/abratchik/nut. It should be compatible with Arduino UPS sketches out of the box. Also created PR to NUT so there is a (small) chance that they merge this change and it will arrive to Synology boxes naturally, along with system upgrades. |
Thank you all for this investigation and PR. As for "natural arrival", we in NUT project have some loose threads to catch up with before making a satisfactory release, so sadly cutting an official release number gets delayed time and again. But indeed, when that happens, distributions should have it disseminated and so have new drivers, features and fixes available to everybody. |
I was able to successfully connect arduino Leonardo to my Synology DS220+ (DSM 7.1.1-42962 Update 2)
Thank you! |
Hi @dosmenbsd , great effort, thanks for sharing! BTW, the NUT library fix to support UPS with the USB Interface > 1 has been merged already to the master, many thanks to @jimklimov . So hopefully it will be picked up by Sinology and other distros in the future, to ensure better compatibility, and support of Arduino UPS as a side effect, without losing the CDC. |
Reading up into the thread, I see experiments setting other vendors' VID/PID. I understand it can help in proof-of-concepts to get at least something running, but since few vendors follow specs precisely and exactly (and/or particular firmware releases are problematic), some drivers happen to have "if ID then" quirks in the middle of the codebase that helped someone sometime. If there is (a possibility to get) an Arduino UPS specific set of IDs, we can code those into the drivers ("this NUT driver is good for these VID:PID pairs: M:N, ... , X:Z"), it may be much more reliable for integrations like this in the long run. |
Hi @jimklimov, thank you for your feedback! Most of Arduino boards equipped with the USB port are here. |
Thanks for the link, but so far I could only look cursorily (commuting). Are there two VIDs in play (0x2341 and 0x2A03), and a few PIDs? NUT-wise can all of these, or only some, be assumed good fits for |
Notably, those ones are already listed at https://github.com/networkupstools/nut/blob/a6ad5c9463a404b02a686614cd83d267fe0abc85/drivers/arduino-hid.c#L38-L55 so might "just work" out of the box |
The function of the Arduino "perceived" by the host is entirely dependent on the HID descriptor, which is uploaded to the Arduino board along with the sketch code. In theory, the same VID/PID can be used for a custom keyboard, a mouse or any other HID device. Ideally the usbhid-ups should only rely on HID Power Device specs and HID descriptor, and not on VID/PID at all. In some Linux distros (Ubuntu, in example), there is an (obsolete) concept in udev manager, which relies on VID/PID for determining device family and purpose. If some VID/PID are not registered properly, they will be ignored by the udev. The workaround is manual registration, through the config files, but it is not documented very well. It took quite a lot of time for me to understand why Windows and OSX could "see" the Arduino UPS out of the box while Ubuntu was ignoring it completely. |
Reluctantly agree - in a perfect world where everyone followed the specs and did not introduce extensions, this would work :) One "issue" with this as regards NUT in particular is that there are many non-HID devices connected by USB. Most others are effectively serial-over-USB variants of Megatec QX protocol family, though some others exist too. Matching by VID:PID mostly allows to rule out which drivers to not try (except for completely sloppy vendors who think they can get away with IDs like 0000, 0001, ffff and a random protocol behind that). Similarly, this allows Another issue with drivers for vendors that follow some standard (USB HID, SNMP, Megatec QX...) is that everyone extends it. Often for a good reason: common standards tend to be the lowest common denominator, like "I'm a battery and I'm online", so ignoring extensible nuances like number of manageable outputs and their power-metering counters that some devices have and others do not. So for every generic "family" driver we end up with a lot of subdrivers that map vendor-specific extensions into a common framework. And generally just relying on "this is an USB HID UPS" without caring for further detail (usually guessed per VID:PID) would only see/manage trivial info even if it can in fact do more. Yet another issue that we woe with lately is that many vendors do not care to implement even the basic USB protocol correctly. We have lots of of issues tagged https://github.com/networkupstools/nut/labels/USB-HID%20encoding%2FLogMin%2FLogMax for cases where even big-name vendors botched the numbers encoding. So knowing who is who helps apply "reasonable hotfixes" to the bit stream. |
And you can do it better, I didn’t quite understand what to fix and where exactly, and which file to flash? I want to connect UPS arduino to Synology NAS 218+ from this instruction https://projecthub.arduino.cc/abratchik/3e597a31-9844-4981-bede-fdac7944ad71#section5 |
I went through the interesting process of building Network Ups Tool (configure part was interesting) and got the arduino detected within ubuntu:
The thing is that i'm missing some parameters, like voltage and remaining%, and I can't find a way to change the "OB" (OnBattery) status. I think have the right usb behavior:
(reported type=ups) ups.conf is like so:
edit, adding output from upsdrvctl -DD start
Anyone got a different output from upsc ? Edit : I found out why nut did not pick the properties from the UPS : default driver collects a very reduced set of properties. Now trying to get values for current ( namely HID_PD_CURRENT ) to work, can't seem to get them collected by the usbhid driver.... |
I've got my own sketch working with MacOS and Windows, but I can't get my Linux-based QNAP to recognize the Arduino as a UPS HID device. So, I decided to just try and get this working on an RPi running Ubuntu 23.10 (I'm NUT v2.8.0 with the @abratchik's PR in it will help) So far, I am failing to get NUT to recognize the Arduino. I haver tried both with CDC enabled and disabled (just doing Running
Any help is appreciated. Looking at the udev rules files that the NUT package installed ( Any help is appreciated. Or maybe I should be posting to a NUT support channel? |
Re the above comment, I've traced the problem to I think a bug in NUT's libusb1. I'll report back here once I'm sure. |
@abratchik can you please confirm what the minimum version of nut is that has this fix incorporated? |
I can confirm NUT 2.8.0, not sure if 2.7.8 has it. Regardless, there is a bug in NUT's libusb1 (at least in v2.8.0, I think in 2.7.8). I submitted a PR for it and it had been merged, but that won't be available until NUT 2.8.2. The 2.8.1 release happened on 10/31/23, and my PR was merged after that. I've seen talk that there's a problem with 2.8.1 so 2.8.2 may be released pretty soon. I don't know if it will include my PR or not, the team may decide to just patch the 2.8.1 release with the smallest possible fix for the (unrelated to my PR) problem. The bug is that in libusb1's
This is wrong because hid_rep_index is the index of the HID power interface, but most USB devices have only a single config descriptor and it is at index 0. In the USB specs, interface descriptors belong to config descriptors, which belong to device descriptors. So on the rare devices with more than one config descriptor, config descriptor 0 may have 3 interfaces and config descriptor 1 may have 2 interfaces (or something else) The end result for me is that for my Arduino, hid_rep_index was discovered to be 2, but the above code really needed to be:
Because my Arduino (like nearly all USB devices, AFAICT) has only a single config descriptor. |
@kellybyrd Thank you for the quick reply. I got 2.8.0 installed on Ubuntu 22.04, and wow now my Arduino Leonardo is seen correctly by nut! But now I am still slightly confused because previously I was able to see and interrogate the Leonardo using Since I hadn't found this thread until recently (the title didn't have me thinking it was relevant for NUT / Leonardo / Synology :-), I created issue #7 where I have a bit more info on my confusion. This treasure trove of info in Issue #1 is too broad! I am not following your comments exactly about the bug in |
Just to clarify: not 2.7.8 but 2.7.4 (at least in upstream versioning) - this one was last of 2.7.x and released some 7 years ago. 2.8.0 was in Apr 22, and 2.8.1 a week ago (already with an errata though). As for 2.8.2 - releases are slightly tidied up snapshots of master, so merged PRs would be there. There are no plans for one-off patch releases (hm, maybe we flex semver a bit too much), though distro packages are likely to add curated chains of patches over the release tarballs ;) I believe @kellybyrd meant NUT's |
Just following up with the success of getting this library working with NUT. It DOES WORK without having to remove CDC or play any tricks with usb vendor and product IDs. I have a post-2.8.1 source tree of NUT running on a Raspberry Pi. I have this library, plus a heavily modified version of the example sketch in this repo (I added hardware to do proper AC mains on/off sensing as well as accurate DC battery voltage sensing and reporting. I'm relying on this setup to have my NAS (which connects over the network to the NUT server on the RPi) shutdown in various conditions. Once NUT 2.8.2 is released, or whatever version has my fixes for NUT's libusb1, I'll try to get something compiled and running on my QNAP NAS directly. That may be more trouble than it is worth, though... |
Positive 👍
Got it working on Pro Micro Clone with 3.3V @8MHz & 5V @16mhz
issue :
on linux system one has to add / change the VID & PID related to used clone / firmware
For SparkFun Pro Micro 3.3V @8MHz is as follows :
ATTRS{idVendor}=="1b4f", ATTRS{idProduct}=="9204", ENV{UPOWER_BATTERY_TYPE}="ups"
But still (Ubuntu 20) on rPI4 refuses to load usbhid-lib for NUT support.
Double checked all blacklist.rules and did find other vid/pid to be blocked, but not this one.
Any idea ?
Did anyone get it running with NUT & usbhid-ups?
Do I have to use another driver interface?
Or other SW package ?
The text was updated successfully, but these errors were encountered: