Skip to content
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

apc_modbus: Support for APC Modbus protocol #2063

Merged
merged 1 commit into from
Oct 20, 2023

Conversation

EchterAgo
Copy link
Contributor

@EchterAgo EchterAgo commented Sep 18, 2023

Current status

This adds APC Modbus support to address isssue #139. For USB support, this needs a patch for libmodbus.

To install the patched libmodbus, you need to get the code and install it into a prefix somewhere:

./autogen.sh
./configure --prefix=/path/to/prefix
make install

to compile NUT you need something like this:

./configure --with-drivers=apc_modbus --with-usb --with-modbus --with-modbus-includes=-I/path/to/prefix/include/modbus --with-modbus-libs="-L/path/to/prefix/lib -lmodbus"

There is still a bit of work to be done:

  • Actually fill in all the values in upsdrv_updateinfo.
  • Get a list of all possible USB VID/PIDs for APC Modbus UPS, currently I only have 051d:0003, any help appreciated.
  • USB open_dev callback that decodes the HID report like in usbhid-ups, checking for the APC Modbus usages to get their ids.
  • Implement device reopen with USB reopen_matcher.
  • The new driver is both Modbus and USB. It should be enabled even if there is no USB support, only Modbus. Needs some conditional compilation and maybe some work on Makefile.am.
  • Make sure it works with different libusb versions.
  • Implement host/port parsing for TCP Modbus connections.
  • Check the default settings (baud rate etc) for serial Modbus connections, maybe make it configurable?
  • Make the Modbus slave id configurable through a variable, this can be used to have multiple Modbus UPS on one bus.
  • Get libmodbus changes integrated in upstream or add support for custom backends. Also see CI: add support for "custom" dependencies in the codebase? Case in question: how to best handle modbus extensions for NUT drivers? #2076 Should be a separate issue.
  • Can we get the HID report ids in libmodbus somehow? It needs a way to parse the hid descriptor. Ideally we'd detect this in libmodbus but, if we can't, we should provide a way to set the report ids.
  • libmodbus device selection. Is it OK to just pass a libusb device handle? Can we somehow open a specific device? We do want to use the USB regex matcher.
  • Combine register reads, we can read the whole block and interpret it after. This reduces the amount of roundtrips and the protocol overhead.
  • Commands I'd prefer to add commands in a later PR.
  • Handle InputStatus_BF
  • Add device_path based auto detection of port type. If unsure it should still default to USB.
  • Cleanup for USB

PR template

General points

  • Described the changes in the PR submission or a separate issue, e.g.
    known published or discovered protocols, applicable hardware (expected
    compatible and actually tested/developed against), limitations, etc.

  • There may be multiple commits in the PR, aligned and commented with
    a functional change. Notably, coding style changes better belong in a
    separate PR, but certainly in a dedicated commit to simplify reviews
    of "real" changes in the other commits. Similarly for typo fixes in
    comments or text documents.

Frequent "underwater rocks" for driver addition/update PRs

  • Revised existing driver families and added a sub-driver if applicable
    (nutdrv_qx, usbhid-ups...) or added a brand new driver in the other
    case.

  • Did not extend obsoleted drivers with new hardware support features
    (notably blazer and other single-device family drivers for Qx protocols,
    except the new nutdrv_qx which should cover them all).

  • For updated existing device drivers, bumped the DRIVER_VERSION macro
    or its equivalent.

  • For USB devices (HID or not), revised that the driver uses unique
    VID/PID combinations, or raised discussions when this is not the case
    (several vendors do use same interface chips for unrelated protocols).

  • For new USB devices, built and committed the changes for the
    scripts/upower/95-upower-hid.hwdb file

  • Proposed NUT data mapping is aligned with existing docs/nut-names.txt
    file. If the device exposes useful data points not listed in the file, the
    experimental.* namespace can be used as documented there, and discussion
    should be raised on the NUT Developers mailing list to standardize the new
    concept.

  • Updated data/driver.list.in if applicable (new tested device info)

Frequent "underwater rocks" for general C code PRs

  • Did not "blindly assume" default integer type sizes and value ranges,
    structure layout and alignment in memory, endianness (layout of bytes and
    bits in memory for multi-byte numeric types), or use of generic int where
    language or libraries dictate the use of size_t (or ssize_t sometimes).
  • Progress and errors are handled with upsdebugx(), upslogx(),
    fatalx() and related methods, not with direct printf() or exit().
    Similarly, NUT helpers are used for error-checked memory allocation and
    string operations (except where customized error handling is needed,
    such as unlocking device ports, etc.)

  • Coding style (including whitespace for indentations) follows precedent
    in the code of the file, and examples/guide in docs/developers.txt file.

  • For newly added files, the Makefile.am recipes were updated and the
    make distcheck target passes.

General documentation updates

  • Updated docs/acknowledgements.txt (for vendor-backed device support)

  • Added or updated manual page information in docs/man/*.txt files
    and corresponding recipe lists in docs/man/Makefile.am for new pages

  • Passed make spellcheck, updated spell-checking dictionary in the
    docs/nut.dict file if needed (did not remove any words -- the make
    rule printout in case of changes suggests how to maintain it).

Additional work may be needed after posting this PR

  • Propose a PR for NUT DDL with detailed device data dumps from tests
    against real hardware (the more models, the better).

  • Address NUT CI farm build failures for the PR: testing on numerous
    platforms and toolkits can expose issues not seen on just one system.

  • Revise suggestions from LGTM.COM analysis about "new issues" with
    the changed codebase.

resolves #139

@EchterAgo EchterAgo changed the title Support for new APC Modbus protocol Support for "new" APC Modbus protocol Sep 18, 2023
@EchterAgo EchterAgo changed the title Support for "new" APC Modbus protocol Support for APC Modbus protocol Sep 18, 2023
@AppVeyorBot
Copy link

@AppVeyorBot
Copy link

@AppVeyorBot
Copy link

@EchterAgo
Copy link
Contributor Author

   0.017737     [D5] send_to_all: SETINFO driver.state "init.quiet"
   0.017756     [D5] send_to_all: SETINFO driver.version "2.8.0-2515-g897c267b6"
   0.017768     [D5] send_to_all: SETINFO driver.version.internal "0.01"
   0.017780     [D5] send_to_all: SETINFO driver.name "apc_modbus"
   0.017790     [D5] send_to_all: SETINFO driver.state "init.info"
   0.017802     [D5] send_to_all: SETINFO device.mfr "APC"
   0.017814     [D5] send_to_all: SETINFO ups.mfr "APC"
   0.066132     [D5] send_to_all: SETINFO ups.firmware "UPS 09.6"
   0.114007     [D5] send_to_all: SETINFO device.model "Smart-UPS 1500"
   0.162028     [D5] send_to_all: SETINFO ups.model "Smart-UPS 1500"
   0.226050     [D5] send_to_all: SETINFO device.serial "XXXXXXXXXXXX"
   0.226077     [D5] send_to_all: SETINFO driver.state "init.updateinfo"
   0.322054     [D5] send_to_all: SETINFO battery.charge "100.00"
   0.386061     [D5] send_to_all: SETINFO battery.voltage "27.12"
   0.434055     [D5] send_to_all: SETINFO input.voltage "231.80"
   0.434078     [D5] send_to_all: SETINFO ups.status "OL HE"
   0.434086     [D5] send_to_all: DATAOK
   0.434096     [D5] send_to_all: SETINFO driver.state "init.quiet"

@AppVeyorBot
Copy link

@EchterAgo
Copy link
Contributor Author

I found more documentation for the Modbus values: https://download.schneider-electric.com/files?p_enDocType=System+user+guide&p_File_Name=990-9840B-EN.pdf&p_Doc_Ref=SPD_LFLG-A32G3L_EN

This allowed me to get the efficiency percentage working, which was not properly documented in MPAO-98KJ7F_R1_EN. There is also documentation for multiple outputs, multiple phases and upper/lower voltages for transfer.

@EchterAgo
Copy link
Contributor Author

I'd also like to add the "total energy consumed" counter into the output. Can I just chose a custom value name or does it need to be defined? Currently I use output.energy, measured in watt-hours, for that.

@EchterAgo
Copy link
Contributor Author

   0.017872     [D5] send_to_all: SETINFO driver.state "init.quiet"
   0.017877     [D5] send_to_all: SETINFO driver.version "2.8.0-2519-gcf6f63355"
   0.017882     [D5] send_to_all: SETINFO driver.version.internal "0.01"
   0.017887     [D5] send_to_all: SETINFO driver.name "apc_modbus"
   0.017890     [D5] send_to_all: SETINFO driver.state "init.info"
   0.017897     [D5] send_to_all: SETINFO device.mfr "APC"
   0.017904     [D5] send_to_all: SETINFO ups.mfr "APC"
   0.083626     [D5] send_to_all: SETINFO ups.firmware "UPS 09.6"
   0.147571     [D5] send_to_all: SETINFO device.model "Smart-UPS 1500"
   0.211635     [D5] send_to_all: SETINFO ups.model "Smart-UPS 1500"
   0.275638     [D5] send_to_all: SETINFO device.serial "XXXXXXXXXXXX"
   0.339600     [D5] send_to_all: SETINFO ups.id "APCUPS"
   0.339627     [D5] send_to_all: SETINFO driver.state "init.updateinfo"
   0.467652     [D5] send_to_all: SETINFO battery.runtime "1560"
   0.515669     [D5] send_to_all: SETINFO battery.charge "100.00"
   0.579679     [D5] send_to_all: SETINFO battery.voltage "27.12"
   0.627722     [D5] send_to_all: SETINFO battery.temperature "27.39"
   0.691692     [D5] send_to_all: SETINFO ups.load "24.00"
   0.739685     [D5] send_to_all: SETINFO ups.realpower "24.00"
   0.803750     [D5] send_to_all: SETINFO ups.power "21.39"
   0.851699     [D5] send_to_all: SETINFO output.current "1.38"
   0.915729     [D5] send_to_all: SETINFO output.voltage "233.19"
   0.963744     [D5] send_to_all: SETINFO output.frequency "49.95"
   1.027739     [D5] send_to_all: SETINFO output.energy "2953947"
   1.075718     [D5] send_to_all: SETINFO input.voltage "233.19"
   1.139775     [D5] send_to_all: SETINFO ups.efficiency "96.0"
   1.187805     [D5] send_to_all: SETINFO input.transfer.high "253"
   1.251782     [D5] send_to_all: SETINFO input.transfer.low "207"
   1.251820     [D5] send_to_all: SETINFO ups.status "OL HE"
   1.251832     [D5] send_to_all: DATAOK
   1.251847     [D5] send_to_all: SETINFO driver.state "init.quiet"

@jimklimov
Copy link
Member

For total energy, I think the point was raised before elsewhere, there is currently no name defined in https://github.com/networkupstools/nut/blob/master/docs/nut-names.txt for that. Try bringing it up in the nut-upsdev mailing list, if anyone has specific ideas for such a value's name and units. Just in case, CCing @aquette and @clepple directly. For the time being, experimental.* namespace is always an option with NUT 2.8.0+ - valid for the standard patterns while stressing the possibility of change when a better name comes up.

Generally the problem with such "total" readings is that almost no devices provide them, especially as lifetime counters persisted on the device. There are some devices which provide recent average "apparent power" (usually in VA, see precedents in nut-names.txt) which is similar to Watts (modulo phase shift factor), and the monitoring systems roughly account the Watt-Hours by counting the samples at known intervals. Similar information can be derived from known amperage and voltage (and phase shift factor) of the input or outlet, measured at some intervals. Whenever a monitoring system (e.g. NUT driver, if it had such ability) restarts, the counter would start from zero unless recorded somewhere. Probably same for monitored system restarts (UPS or its mgmt board). Typically SNMP systems have a concept for measuring such gauges over time, and sometimes accept that occasionally the "lifetime" value may drop to zero, so it is not unheard of.

Overall, if you are sure the device can provide the total counter of energy it has brokered over its lifetime (or even just over the uptime), it makes sense to relay that into NUT and come up with naming schemes for that. In fact I would have expected the drivers for metered/managed ePDUs to have something like that, but it seems the current ones rely on V*A*PhaseShift.
If not, maybe leave it to smarter monitoring systems (which NUT reports into) to summarize the trends over time, or just as well(come) - propose a way to make NUT smarter in this area, ideally some way that can be applied to all drivers (shared methods in main.c?) and probably a focus for some separate PR then.

@AppVeyorBot
Copy link

@jimklimov
Copy link
Member

FYI: I've asked around for precedents and planned work on energy accounting. It seems there were quite a few ideas, including on vendors' side, but so far nothing got committed and "standardized" in NUT mainline.

One of the useful notes was that this is usually done for billing purposes (either by infrastructure providers, or even home users deciding which of their boxes eats too much) - and for that context it is usually prudent to:

  • track "since when" the reading is accumulated (if the attached "fed" device changes, need to restart counting);
  • on NUT side - have commands to tell the device to reset the counter (when it does so);
  • maybe have a way to relocate the reading to another device/outlet (if moving, changing PDUs etc.) for say tracking a lifetime appetite of a server - consider resetting to a non-zero value;
  • disclaim that these numbers are estimates, when they are not some legally binding values guaranteed by a HW manufacturer who is accounting in fine detail :)

@aquette
Copy link
Member

aquette commented Sep 21, 2023

https://github.com/networkupstools/nut/blob/master/drivers/eaton-pdu-marlin-mib.c#L1128

The mib should describe the rest...
Not sure if I brain dumped more, in tickets, docs or comments

@jimklimov jimklimov added the documentation-protocol Submitted vendor-provided or user-discovered protocol information, or similar data (measurements...) label Sep 21, 2023
@AppVeyorBot
Copy link

@EchterAgo
Copy link
Contributor Author

EchterAgo commented Sep 24, 2023

Updated with many new variables, even though there are many new values lacking in the HID driver, there are still some missing:

battery.charge.low: 10
battery.charge.warning: 50
battery.runtime.low: 120
battery.type: PbAc
battery.voltage.nominal: 24.0
ups.beeper.status: enabled

I haven't found these in the modbus registers yet, although I have some idea for the beeper status.

I've also improved the code quite a bit with converters for some types.

@EchterAgo
Copy link
Contributor Author

EchterAgo commented Sep 24, 2023

This is what we currently get:

   0.016217     [D5] send_to_all: SETINFO driver.state "init.quiet"
   0.016222     [D5] send_to_all: SETINFO driver.version "2.8.0-2559-g7ae94be9a"
   0.016226     [D5] send_to_all: SETINFO driver.version.internal "0.01"
   0.016230     [D5] send_to_all: SETINFO driver.name "apc_modbus"
   0.016234     [D5] send_to_all: SETINFO driver.state "init.info"
   0.016238     [D5] send_to_all: SETINFO ups.mfr "American Power Conversion"
   0.078040     [D5] send_to_all: SETINFO ups.firmware "UPS 09.6"
   0.142407     [D5] send_to_all: SETINFO ups.model "Smart-UPS 1500"
   0.205998     [D5] send_to_all: SETINFO ups.serial "XXXXXXXXXXXX"
   0.270056     [D5] send_to_all: SETINFO ups.mfr.date "2015-11-06"
   0.334022     [D5] send_to_all: SETINFO ups.id "APCUPS"
   0.334045     [D5] send_to_all: SETINFO driver.state "init.updateinfo"
   0.462042     [D5] send_to_all: SETINFO battery.runtime "1920"
   0.526073     [D5] send_to_all: SETINFO battery.charge "100.00"
   0.574103     [D5] send_to_all: SETINFO battery.voltage "27.12"
   0.638058     [D5] send_to_all: SETINFO battery.date.maintenance "2025-12-10"
   0.702095     [D5] send_to_all: SETINFO battery.temperature "27.00"
   0.750104     [D5] send_to_all: SETINFO ups.load "22.09"
   0.814110     [D5] send_to_all: SETINFO ups.realpower "21.39"
   0.862108     [D5] send_to_all: SETINFO ups.power "18.19"
   0.910135     [D5] send_to_all: SETINFO output.current "1.19"
   0.974111     [D5] send_to_all: SETINFO output.voltage "230.39"
   1.038128     [D5] send_to_all: SETINFO output.frequency "49.95"
   1.086132     [D5] send_to_all: SETINFO experimental.output.energy "2980575"
   1.150154     [D5] send_to_all: SETINFO input.voltage "230.39"
   1.198157     [D5] send_to_all: SETINFO ups.efficiency "95.0"
   1.262196     [D5] send_to_all: SETINFO ups.timer.shutdown "-1"
   1.310140     [D5] send_to_all: SETINFO ups.timer.start "-1"
   1.374369     [D5] send_to_all: SETINFO ups.timer.reboot "-1"
   1.422189     [D5] send_to_all: SETINFO input.transfer.high "253"
   1.486195     [D5] send_to_all: SETINFO input.transfer.low "207"
   1.534194     [D5] send_to_all: SETINFO ups.delay.shutdown "0"
   1.598197     [D5] send_to_all: SETINFO ups.delay.start "0"
   1.646211     [D5] send_to_all: SETINFO ups.delay.reboot "8"
   1.646236     [D5] send_to_all: SETINFO ups.status "OL HE"

@EchterAgo
Copy link
Contributor Author

Added HID report descriptor scan for RX/TX usages to get their report ids. This also needs the latest changes from my libmodbus rtu_usb branch.

@EchterAgo
Copy link
Contributor Author

  • Register reads are now combined into only 3 reads per update
  • Added battery.date
  • Added BYPASS and OVER status
  • USB cleanup

@jimklimov
Copy link
Member

jimklimov commented Mar 20, 2024

Oh, I revised your earlier post and think that configure is also the one recommended for NUT while done in libmodbus sources. So it likely ignored the NUT-specific options and e.g. installed libmodbus under default --prefix=/usr (not /usr/local presumed by NUT build config example).

@zDEFz
Copy link

zDEFz commented Mar 20, 2024

Oh, I revised your earlier post and think that configure is also the one recommended for NUT while done in libmodbus sources. So it likely ignored the NUT-specific options and e.g. installed libmodbus under default --prefix=/usr (not /usr/local presumed by NUT build config example).

Does that consequently mean libmodbus needs to be built like this?

./configure --with-drivers=apc_modbus --with-usb --with-modbus \
   --with-modbus-includes=-I/usr/include/modbus \
   --with-modbus-libs="-L/usr -lmodbus"
make
sudo make install

But I think maybe I should just wait till you have revised the wiki page.

@jimklimov
Copy link
Member

Slightly revised the wiki page (to cd ~/ before each git clone and subsequent cd into a workspace), but generally the instructions were okay -- your copy-paste from wiki misfired (if the copies back into the comments above are trustworthy).

@zDEFz
Copy link

zDEFz commented Mar 21, 2024

Slightly revised the wiki page (to cd ~/ before each git clone and subsequent cd into a workspace), but generally the instructions were okay -- your copy-paste from wiki misfired (if the copies back into the comments above are trustworthy).

It's still failing for me.

: 1711001492:0;cd ~/git
: 1711001493:0;cd libmodbus
: 1711001497:0;./autogen.sh
: 1711001524:0;./configure --prefix=/usr/local
: 1711001531:0;sudo make install
: 1711001541:0;cd ~/git/nut
: 1711001542:0;ls
: 1711001545:0;./autogen.sh
: 1711001577:0;./configure --with-drivers=apc_modbus --with-usb --with-modbus \
--with-modbus-includes=-I/usr/local/include/modbus \
--with-modbus-libs="-L/usr/local/lib -lmodbus"

configure: error: modbus library not found, required for Modbus driver

@jimklimov
Copy link
Member

jimklimov commented Mar 21, 2024

Aw, schucks! Apparently the code evolved after instructions were written: for libmodbus you also need to configure --with-libusb ... now (defaults to no according to its help).

Without the flag current code indeed results in:

...
checking for libmodbus version via pkg-config... 3.1.6 found
checking for libmodbus cflags... -I/tmp/include/modbus
checking for libmodbus ldflags... -L/tmp/lib -lmodbus
checking for modbus.h... no
checking for modbus_new_rtu... yes
checking for modbus_new_tcp... yes
checking for modbus_set_byte_timeout... yes
checking for modbus_set_response_timeout... yes
checking for modbus_new_rtu_usb... no
...
^^^ This message should be made clearer perhaps
... more text for other prereqs
...
configure: error: modbus library not found, required for Modbus driver

With that option in libmodbus build I now see its configure mention USB:

...
checking whether TIOCSRS485 is declared... yes
checking whether TIOCM_RTS is declared... yes
checking for libusb_init in -lusb-1.0... yes
checking that generated files are newer than configure... done
configure: creating ./config.status
...

and a new object is made that does not happen otherwise:

Making install in src
  CC       modbus.lo
  CC       modbus-data.lo
  CC       modbus-rtu.lo
  CC       modbus-tcp.lo
  CC       modbus-rtu-usb.lo
  CCLD     libmodbus.la
 /usr/bin/mkdir -p '/tmp/lib'
...

...and the NUT build succeeds.

UPDATE: Checking a hiccup with apc_modbus.8 man page reported once but generated upon a second make :
UP-UPDATE: Seems that was due to my test config settings and lack of rules to build docs by default.

@zDEFz
Copy link

zDEFz commented Mar 21, 2024

cd ~/git/libmodbus
./autogen.sh
./configure --with-libusb --prefix=/usr/local
sudo make install
cd ~/git/nut
./autogen.sh
./configure --with-drivers=apc_modbus --with-usb --with-modbus \
   --with-modbus-includes=-I/usr/local/include/modbus \
   --with-modbus-libs="-L/usr/local/lib -lmodbus"
sudo make install

success.

However... I had to edit zshrc and add

export PATH="$PATH:/usr/local/ups/sbin"
sudo cp  /usr/local/ups/etc/ups.conf.sample   /usr/local/ups/etc/ups.conf
sudo nano /usr/local/ups/etc/ups.conf

 [SMT1500IC]
       driver = apc_modbus
       port = auto

comment out retries=

But then....

% sudo upsdrvctl start
Network UPS Tools - UPS driver controller 2.8.1-980-gc05ca1fc3
Network UPS Tools - NUT APC Modbus driver 0.10 (2.8.1-980-gc05ca1fc3)
modbus_connect: unable to connect: No such device
upsnotify: notify about state 4 with libsystemd: was requested, but not running as a service unit now, will not spam more about it
upsnotify: failed to notify about state 4: no notification tech defined, will not spam more about it
Driver failed to start (exit status=1)

I am using the USB cable from my SMT1500IC and want the modbus stuff, too. It's enabled.

a couple tries later:

130 % sudo upsdrvctl start
Network UPS Tools - UPS driver controller 2.8.1-980-gc05ca1fc3
Network UPS Tools - Generic HID driver 0.52 (2.8.1-980-gc05ca1fc3)
USB communication driver (libusb 1.0) 0.47
libusb1: Could not open any HID devices: insufficient permissions on everything
No matching HID UPS found
upsnotify: notify about state 4 with libsystemd: was requested, but not running as a service unit now, will not spam more about it
upsnotify: failed to notify about state 4: no notification tech defined, will not spam more about it
Driver failed to start (exit status=1)
(3) /usr/local/ups/bin
% ls
adelsystem_cbi  belkinunv     etapro          masterguard      nutdrv_siemens-sitop   richcomm_usb  tripplitesu
al175           bestfcom      everups         metasys          NUT-Monitor            riello_ser    tripplite_usb
apc_modbus      bestfortress  gamatronic      mge-shut         nut-scanner            riello_usb    upsc
apcsmart        bestuferrups  generic_modbus  mge-utalk        oneac                  safenet       upscmd
apcsmart-old    bestups       genericups      microdowell      optiups                skel          upscode2
apcupsd-ups     blazer_ser    huawei-ups2000  microsol-apc     phoenixcontact_modbus  sms_ser       upslog
asem            blazer_usb    isbmex          netxml-ups       pijuice                snmp-ups      upsrw
bcmxcp          clone         ivtscd          nutconf          powercom               socomec_jbus  upssched-cmd
bcmxcp_usb      clone-outlet  liebert         nutdrv_atcl_usb  powerpanel             solis         usbhid-ups
belkin          dummy-ups     liebert-esp2    nutdrv_qx        rhino                  tripplite     victronups

(3) /usr/local/ups/bin
% sudo ./nut-scanner -U
Scanning USB bus.
[nutdev-usb1]
	driver = "usbhid-ups"	# alternately: apc_modbus
	port = "auto"
	vendorid = "051D"
	productid = "0003"
	product = "Smart-UPS_1500 FW:UPS 03.5 / ID=1015"
	serial = "redacted"
	vendor = "American Power Conversion"
	# bus = "003"
	# device = "007"
	# busport = "002"

sudo nano ../etc/ups.conf
[nutdev-usb1]
        driver = "usbhid-ups"   # alternately: apc_modbus
        port = "auto"

same issue
....

@jimklimov
Copy link
Member

jimklimov commented Mar 21, 2024

Regarding /usr/local/ups - that's the default NUT root since forever to avoid conflicts with packaged builds.

Probably your build also suffers from having different user/group names applied (default nobody) from those via packaging (and embedded into default udev settings), and likely a different sysconfdir than common /etc...

Check wiki about "in-place builds" to inherit as much as possible to replace a packaged version if used. Or restart the relevant udev/upower/... implementation service (or reboot) to apply devfs permissions for the supported UPS USB IDs.

@zDEFz
Copy link

zDEFz commented Mar 21, 2024

Regarding /usr/local/ups - that's the default NUT root since forever to avoid conflicts with packaged builds.

Probably your build also suffers from having different user/group names applied (default nobody) from those via packaging (and embedded into default udev settings), and likely a different sysconfdir than common /etc...

Check wiki about "in-place builds" to inherit as much as possible to replace a packaged version if used. Or restart the relevant udev/upower/... implementation service (or reboot) to apply devfs permissions for the supported UPS USB IDs.

I tried those options... with unfavorable results. Is there an easier way to use it? Some of my usb devices are working through Wine even.
I just want to get it done somehow.

jimklimov added a commit to jimklimov/nut that referenced this pull request Mar 21, 2024
…nd, but does not support libusb while we want it (and will fail NUT configure later) [networkupstools#2063]

Signed-off-by: Jim Klimov <[email protected]>
jimklimov added a commit to jimklimov/nut that referenced this pull request Mar 21, 2024
jimklimov added a commit to jimklimov/nut that referenced this pull request Mar 21, 2024
jimklimov added a commit to jimklimov/nut that referenced this pull request Mar 21, 2024
@jimklimov
Copy link
Member

"Through Wine" that's probably passed through as root? Or owned by your interactive console user (many types of USB and other device supports default to that, flipping as different users log in).

Speaking of which, you can try user=root in the ups.conf settings for the driver to bypass at least this nag (assuming you start the driver process as root - via sudo or as a service - and it drops privileges to built-in nobody unless you passed another name as configure --with-user).

I also did not see recently a log of driver start-up attempt with raised debug verbosity (it would reveal the built-in and attempted user name, which devices it tried and what causes it had to skip them, etc.)

@zDEFz
Copy link

zDEFz commented Mar 22, 2024

"Through Wine" that's probably passed through as root? Or owned by your interactive console user (many types of USB and other device supports default to that, flipping as different users log in).

I have for instance my RME ADI-2 DAC FS on Linux running a Wine Application called "RME Remote" and it works properly. Basically, it communicates through MIDI with the host.

In any case, something is awfully wrong with running the compiled nut things on SMT1500IC.
To start with, I never read any data.
But let me go back a bit.

I have apcupsd running, and then I wanna run nut.
With a custom built nut, I always get connection refused, no matter what.

However, if I use nut from the extra repositories, I get at least data in.
But not apc_modbus.
Look at https://gitlab.archlinux.org/archlinux/packaging/packages/nut/-/blob/main/PKGBUILD?ref_type=heads

Could it be that I need in addition all of those make flags for nut? I'll git clone the repo and try building my own.
I will try it that way and combine it.

So that would be

./configure --prefix=/usr \
    --datadir=/usr/share/nut \
    --libexecdir=/usr/lib/nut \
    --sbindir=/usr/bin \
    --sysconfdir=/etc/nut \
    --disable-static \
    --with-user=nut \
    --with-group=nut \
    --with-altpidpath=/run/nut \
    --with-cgipath=/usr/share/nut/cgi \
    --with-drvpath=/usr/lib/nut \
    --with-htmlpath=/usr/share/nut/html \
    --with-pidpath=/run/nut \
    --with-statepath=/var/lib/nut \
    --with-systemdsystemunitdir=/usr/lib/systemd/system \
    --with-udev-dir=/usr/lib/udev \
    --with-cgi \
    --with-dev \
    --with-doc=man \
    --with-libltdl \
    --with-neon \
    --with-openssl \
    --with-serial \
    --with-snmp \
    --with-usb \
    --without-avahi \
    --without-ipmi \
    --without-freeipmi \
    --without-powerman \
    --without-wrap \
    --with-drivers=apc_modbus \
    --with-modbus \
    --with-modbus-includes=-I/usr/include/modbus \
    --with-modbus-libs="-L/usr/lib -lmodbus"

I did boot into Windows 10 now and installed PowerChute Serial Shutdown to find that the program only displays useful values when modbus is disabled on the device itself. So I was able to get current load in amps.
But I am lacking that on Linux so far.

@zDEFz
Copy link

zDEFz commented Mar 22, 2024

That is so weird! I can't....
Not to mention that with those flags, a lot of binaries appear to be missing in the proper folder.
Some way to untangle this?

/
130 % sudo upsdrvctl start
Network UPS Tools - UPS driver controller 2.8.1
Can't start /usr/lib/nut/usbhid-ups: No such file or directory

/
1 % sudo cp home/blu/Downloads/bin/usbhid-ups /usr/lib/nut/

/
% sudo upsdrvctl start
Network UPS Tools - UPS driver controller 2.8.1
Network UPS Tools - Generic HID driver 0.52 (2.8.1-980-gc05ca1fc3)
USB communication driver (libusb 1.0) 0.47
Error: Section nutdev-usb1 not found in ups.conf
upsnotify: notify about state 4 with libsystemd: was requested, but not running as a service unit now, will not spam more about it
upsnotify: failed to notify about state 4: no notification tech defined, will not spam more about it
Driver failed to start (exit status=1)

/
1 % sudo nano /etc/nut/ups.conf

cat  /etc/nut/ups.conf

[nutdev-usb1]
	driver = "usbhid-ups"	# alternately: apc_modbus
	port = "auto"

If I put it to apc_modbus, I get this:

% sudo upsdrvctl start
Network UPS Tools - UPS driver controller 2.8.1
Network UPS Tools - NUT APC Modbus driver 0.01 (2.8.1)
_apc_modbus_read_registers: Read of 516:604 failed: Connection timed out (auto)
Can't read inventory information from the UPS
upsnotify: notify about state 4 with libsystemd: was requested, but not running as a service unit now, will not spam more about it
upsnotify: failed to notify about state 4: no notification tech defined, will not spam more about it
Driver failed to start (exit status=1)

Meanwhile in apcupsd... - and note I want more info like LOAD etc which comes with the apc_modbus...

130 % apcaccess
APC      : 001,027,0678
DATE     : 2024-03-22 05:00:28 +0100
HOSTNAME : mainrig-MS-7D51
VERSION  : 3.14.14 (31 May 2016) unknown
UPSNAME  : mainrig-MS-7D51
CABLE    : USB Cable
DRIVER   : USB UPS Driver
UPSMODE  : Stand Alone
STARTTIME: 2024-03-22 04:50:45 +0100
MODEL    : Smart-UPS_1500
STATUS   : ONLINE
BCHARGE  : 100.0 Percent
TIMELEFT : 53.6 Minutes
MBATTCHG : 5 Percent
MINTIMEL : 3 Minutes
MAXTIME  : 0 Seconds
ALARMDEL : 30 Seconds
BATTV    : 26.3 Volts
NUMXFERS : 0
TONBATT  : 0 Seconds
CUMONBATT: 0 Seconds
XOFFBATT : N/A
STATFLAG : 0x05000008
MANDATE  : 2018-06-05
SERIALNO : AS1823261711
NOMBATTV : 24.0 Volts
FIRMWARE : UPS 03.5 / ID=1015
END APC  : 2024-03-22 05:00:33 +0100

But this is ... just the usb mode.
UPSTYPE usb
DEVICE /dev/usb/hid/hiddev[0-9]

If I activate apc_modbus in apcupsd...
UPSCABLE usb
UPSTYPE modbus

Then I'd get invalid data. I mean, look at the BATTV value for instance. 465V at 230V in Germany?> Nope :D
Also the amperage of 464 amps is insanity.

% apcaccess
APC      : 001,032,0749
DATE     : 2024-03-22 05:04:57 +0100
HOSTNAME : mainrig-MS-7D51
VERSION  : 3.14.14 (31 May 2016) unknown
UPSNAME  : mainrig-MS-7D51
CABLE    : USB Cable
DRIVER   : MODBUS UPS Driver
UPSMODE  : Stand Alone
STARTTIME: 2024-03-22 05:04:55 +0100
STATUS   : ONLINE
LINEV    : 232.2 Volts
LOADPCT  : 0.0 Percent
LOADAPNT : 0.0 Percent
BCHARGE  : 1.6 Percent
MBATTCHG : 5 Percent
MINTIMEL : 3 Minutes
MAXTIME  : 0 Seconds
OUTPUTV  : 232.2 Volts
DWAKE    : -1 Seconds
DSHUTD   : -1 Seconds
ITEMP    : 0.4 C
BATTV    : 464.5 Volts
LINEFREQ : 116.1 Hz
OUTCURNT : 464.50 Amps
NUMXFERS : 0
TONBATT  : 0 Seconds
CUMONBATT: 0 Seconds
XOFFBATT : N/A
STATFLAG : 0x05000008
BATTDATE :
NOMOUTV  : -1 Volts
NOMAPNT  : 0 VA
END APC  : 2024-03-22 05:06:03 +0100

Then...

~/git/nut on master
% sudo systemctl start nut-server

~/git/nut on master
% sudo upsc upsname
battery.charge: 100
battery.charge.low: 10
battery.charge.warning: 50
battery.runtime: -1
battery.runtime.low: 150
battery.type: PbAc
battery.voltage: 26.3
battery.voltage.nominal: 24.0
device.mfr: American Power Conversion
device.model: Smart-UPS_1500
device.serial: AS1823261711
device.type: ups
driver.debug: 0
driver.flag.allow_killpower: 0
driver.name: usbhid-ups
driver.parameter.pollfreq: 30
driver.parameter.pollinterval: 2
driver.parameter.port: auto
driver.parameter.synchronous: auto
driver.state: quiet
driver.version: 2.8.1
driver.version.data: APC HID 0.100
driver.version.internal: 0.52
driver.version.usb: libusb-1.0.27 (API: 0x100010a)
ups.beeper.status: enabled
ups.delay.shutdown: 20
ups.firmware: UPS 03.5 / ID=1015
ups.mfr: American Power Conversion
ups.mfr.date: 2018/06/05
ups.model: Smart-UPS_1500
ups.productid: 0003
ups.serial: redacted
ups.status: OL
ups.timer.reboot: -1
ups.timer.shutdown: 65535
ups.vendorid: 051d

@zDEFz
Copy link

zDEFz commented Mar 22, 2024

Update.

I had temporary success...

  1. nut-server was not started. sudo systemctl start nut-server
  2. I used the wrong options / settings. For archlinux I had to rebuild a modified https://gitlab.archlinux.org/archlinux/packaging/packages/nut/-/blob/main/PKGBUILD?ref_type=heads

Now I get:
sudo upsc SMT1500IC

battery.charge: 100.00
battery.date: 2024-03-15
battery.date.maintenance: 2028-09-12
battery.runtime: 3081
battery.temperature: 29.47
battery.voltage: 26.31
device.mfr: American Power Conversion
device.model: Smart-UPS 1500
device.serial: redacted
device.type: ups
driver.debug: 0
driver.flag.allow_killpower: 0
driver.name: apc_modbus
driver.parameter.pollinterval: 2
driver.parameter.port: auto
driver.parameter.synchronous: auto
driver.state: quiet
driver.version: 2.8.1
driver.version.internal: 0.01
driver.version.usb: libusb-1.0.27 (API: 0x100010a)
experimental.output.energy: 534619
input.transfer.high: 253
input.transfer.low: 207
input.transfer.reason: AcceptableInput
input.voltage: 232.41
output.current: 1.47
output.frequency: 50.00
output.voltage: 232.41
ups.delay.reboot: 8
ups.delay.shutdown: 0
ups.delay.start: 0
ups.efficiency: 97.2
ups.firmware: UPS 03.5
ups.id: APC UPS
ups.load: 27.75
ups.mfr: American Power Conversion
ups.mfr.date: 2018-06-05
ups.model: Smart-UPS 1500
ups.power: 344.59
ups.power.nominal: 1500
ups.productid: 0003
ups.realpower: 277.54
ups.realpower.nominal: 1000
ups.serial: redacted
ups.status: OL HE
ups.timer.reboot: -1
ups.timer.shutdown: -1
ups.timer.start: -1
ups.vendorid: 051d

with setting:

 [SMT1500IC]
       driver = apc_modbus
         port = auto

in /etc/nut/ups.conf.

It would be really helpful when I am debugging to know that nut-server has to be running in order to retrieve any data. E.g in the archwiki it is pretty far down. https://wiki.archlinux.org/title/Network_UPS_Tools

@zDEFz
Copy link

zDEFz commented Mar 22, 2024

But now I get .... "Error Driver not connected"
Then I run

% sudo upsdrvctl start
Network UPS Tools - UPS driver controller 2.8.1
Network UPS Tools - NUT APC Modbus driver 0.01 (2.8.1)
_apc_modbus_read_registers: Read of 516:604 failed: Connection timed out (auto)
Can't read inventory information from the UPS
upsnotify: notify about state 4 with libsystemd: was requested, but not running as a service unit now, will not spam more about it
upsnotify: failed to notify about state 4: no notification tech defined, will not spam more about it
Driver failed to start (exit status=1)

meanwhile apcaccess still gets all the data.
If I reconnect the usb cable, then run sudo upsdrvctl -D start followed by sudo systemctl start nut-server.service, then I get all the data again!

@zDEFz
Copy link

zDEFz commented Mar 22, 2024

I have found something similar.
So the issue is now: upsc ups Error: Data stale

I've already added MAXAGE 25, but to no avail.
Going through https://github.com/networkupstools/nut/wiki/Troubleshooting-eventual-disconnections-(Data-stale)

Tried https://github.com/netinvent/usb_resetter but with no success.
Maybe I cannot use modbus properly on SMT1500IC - as even on Windows, there are disconnects if it is ON.
My device is also not in the compatibility list https://networkupstools.org/stable-hcl.html

Retrying apcupsd

Now I found adding the following to apcupsd.conf

## apcupsd.conf v1.1 ##

UPSCABLE usb
UPSTYPE modbus
DEVICE
LOCKFILE /var/lock
UPSCLASS standalone
UPSMODE disable

Followed by replugging the device enabled me to see more values.
Resetting USB doesn't do anything for me.
To note: I need to do this every time, so I attached my UPS USB to my KVM switch which I use multiple times a day. Should be OK... but still...

APC did a poor job for not following USB standard apparently.

Edit this is hit or miss. Unreliable USB on SMT1500IC units? YES
But only if modbus is being used.

jimklimov added a commit to jimklimov/nut that referenced this pull request Mar 22, 2024
jimklimov added a commit to jimklimov/nut that referenced this pull request Mar 22, 2024
…nd, but does not support libusb while we want it (and will fail NUT configure later) [networkupstools#2063]

Signed-off-by: Jim Klimov <[email protected]>
jimklimov added a commit to jimklimov/nut that referenced this pull request Mar 22, 2024
@jimklimov
Copy link
Member

jimklimov commented Mar 22, 2024

Thanks for the info! Also, just in case for other readers: be sure to run EITHER apcupsd OR one instance of a NUT driver. Trying to connect to a libusb node while some other program holds it (or for that matter, kernel or if virtualization pass-through is involved - on either side of the tunnel) can also manifest as "Insufficient permissions" or similar inability to fully open the USB device.

jimklimov added a commit to jimklimov/nut that referenced this pull request Mar 22, 2024
@jimklimov
Copy link
Member

FYI: Further tested and updated the Wiki page (and pending man page update) about building NUT vs. custom libmodbus with USB capability, to suggest using a static build of libmodbus (so its object data gets linked directly into the apc_modbus binary) in order to rule out dynamic-linking conflicts at run-time, or installing custom library+headers in some way that might confuse other consumers of the OS/distro packaged variant.

@jimklimov
Copy link
Member

@EchterAgo : note that the libmodbus project seems to have woken up from a slumber, and posted that pending CLA requests were accepted. Some PRs are already getting merged as they clear the backlog. I did not see this nickname of yours in the current https://github.com/stephane/libmodbus/blob/master/.clabot list, so maybe you would have to re-submit the form and perhaps post a new issue to expedite the acceptance, and a PR while there (feel free to update your libmodbus PR source branch from https://github.com/networkupstools/libmodbus/tree/rtu_usb with a README update).

Could you please act on this quickly before they go into hibernation again? :)

@karelkryda
Copy link

karelkryda commented Aug 14, 2024

@EchterAgo, I would like to ask if you plan to create PR for upstream. I would like to see official support for USB modbus directly in the NUT release 🙂.

Thank you

@TinyShark
Copy link

^ +1

Just run into this and not getting load % via the USBHID-UPS driver for an APC SMT1000i2U. Would be awesome if modbus was available as a driver option natively.

@jimklimov
Copy link
Member

NOTE: There are several distinct issues discussed in this after-party. One is about getting the custom build right - hopefully addressed by evolution of the Wiki page with instructions (including the points on using a static library and so avoiding conflicts and confusion at run-time).

Another is about the driver crashing during start - further investigation tracked in #2609

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
APC documentation-protocol Submitted vendor-provided or user-discovered protocol information, or similar data (measurements...) enhancement modbus ready / gonna merge The PR is in final cycles leading to merge unless someone logs an objection before we hit the button serial port USB
Projects
Status: Todo
Development

Successfully merging this pull request may close these issues.

Add support for new APC Modbus protocol