Skip to content

apc_modbus: Support for APC Modbus protocol#2063

Merged
jimklimov merged 1 commit intonetworkupstools:masterfrom
EchterAgo:apc_modbus
Oct 20, 2023
Merged

apc_modbus: Support for APC Modbus protocol#2063
jimklimov merged 1 commit intonetworkupstools:masterfrom
EchterAgo:apc_modbus

Conversation

@EchterAgo
Copy link
Copy Markdown
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
Copy Markdown

@AppVeyorBot
Copy link
Copy Markdown

@AppVeyorBot
Copy link
Copy Markdown

@EchterAgo
Copy link
Copy Markdown
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
Copy Markdown

@EchterAgo
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown

@jimklimov
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown

@EchterAgo
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown

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
Copy Markdown

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
Copy Markdown

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
Copy Markdown

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
Copy Markdown

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
Copy link
Copy Markdown
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
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown

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
Copy Markdown

^ +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
Copy Markdown
Member

jimklimov commented Sep 24, 2024

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 and #2289

@UltrashRicco
Copy link
Copy Markdown

Hi there!

I just moved from apcupsd to nut to monitor my APC SMC1500IC. This is a rather recent modbus-capable UPS.

I was previously using apcupsd as mentionned, altough with the basic USB driver, as the modbus driver gave more data indeed (like the UPS load), but was quite unstable (worked for a random short while, then fed garbage data and false low battery alerts, triggering shutdowns, etc.).
I had been using this setup for a couple of years and recently decided to check whether apcupsd had been updated and/or the modbus issues resolved (it does not seems so).

So I decided to give NUT a try! Working totally fine with the usbhid-ups driver, reporting virtually the same information as apsupsd does, easy to setup, etc.

I would have loved to try the modbus driver, but I could see that it is not yet built in, and requires building a specific version of libmodbus, then build NUT with it. I could also read that for the time being, the driver can only read the UPS settings, not modify the settings in firmware. This is a hurdle for a beginner/enthusiast, but I might still give it a try if I am able to find some time.

I just wanted to ask: has anyone noticed a similar instable behaviour with their APC UPS over modbus with either apsupsd or NUT? I could never tell whether it was due to my UPS or to apcupsd.

I would love to help, but have limited knowledge of coding.
Thank you for the awesome work!
Regards,

@zDEFz
Copy link
Copy Markdown

zDEFz commented Jan 5, 2025

I just wanted to ask: has anyone noticed a similar instable behaviour with their APC UPS over modbus with either apsupsd or NUT? I could never tell whether it was due to my UPS or to apcupsd.

Yes. SMT1500IC modbus driver here makes the APC UPS disconnect every few minutes. Totally unusable. Even when the custom modbus driver is built.

@UltrashRicco
Copy link
Copy Markdown

I just wanted to ask: has anyone noticed a similar instable behaviour with their APC UPS over modbus with either apsupsd or NUT? I could never tell whether it was due to my UPS or to apcupsd.

Yes. SMT1500IC modbus driver here makes the APC UPS disconnect every few minutes. Totally unusable. Even when the custom modbus driver is built.

Thanks for the feedback!
Did you by any chance ever give apcupsd a try?

"Funny" thing, I checked APC's documentation, and the latest product datasheet (for my SMC1500IC) no longer mentions modbus capability.
The user manual does, it dates back to 2023 and was not updated.
image

This FAQ about modbus support clearly states now that SMC series UPSs do not support modbus. Your SMT unit supposedly does.
https://www.apc.com/us/en/faqs/FA402663/
image

I had such losses of communication with apcupsd and my SMC1500IC, but when it worked, it fetched quite a bit of extra information, compared to the USB driver.

Do we know whether APC/Schneider's own solutions use the modbus protocol? PowerChute Serial Shutdown may use it and can display things like load/power draw, and can modify firmware-stored settings (like grace period after shutdown before powering back on, time to wait after a low battery event before powering back on to allow the batteries to charge, etc.).

Fortunately, the basic features are there, and it is definitely possible to gracefully shutdown computers behind such an UPS with NUT, but it is still frustrating not being able to use it to its full potential.

Sorry for kinda hijacking the topic, I hope this helps somehow.
Regards!

@zDEFz
Copy link
Copy Markdown

zDEFz commented Jan 5, 2025

The only thing I am aware of that works is to run a virtual machine with windows installed and a pretty old version of the APC software. Then you would forward the USB device. From there you can also reset the battery replacement date, which is actually super important.
You will absolutely need that because once you take your APC unit a bit away from the power source, it is more than likely it will need another reset to report the full battery capacity after being charged...

@UltrashRicco
Copy link
Copy Markdown

UltrashRicco commented Aug 15, 2025

APC no longer intends to implement modbus over USB for the SMC1500-C and SMC1500-IC. The specifications were altered, etc.

Well I did manage to get my SMC1500-IC to work reliably with modbus... just over serial, not over USB.

I purchased the right "5G" smart signalling cable (RJ50 to DB9), and a USB to RS232 (DB9) cable, and guess what? Modbus works perfectly fine with that cable, providing extensive UPS information in a very stable fashion (contrary to over USB).

The right cable for your UPS should be listed here: https://www.apc.com/us/en/faqs/FA163632/
In my case, the correct one is either AP940-0625A or AP940-1525A (the difference is the lenght): https://www.se.com/us/en/product/AP940-0625A/cable-ups-communications-smart-signalling-6-2m-db9-to-rj45/
I got one from China.

In the meantime I also reverted to using apcupsd instead of NUT, so I was not able to test the apc_modbus driver with this cable, but it should work!

This is the available data with apcupsd with that particular UPS (SMC1500-IC):
image

I hope it helps someone having the same issues with modbus over USB with SMC devices.

@omarkhali
Copy link
Copy Markdown

Hello @UltrashRicco ,

First of all, thank you very much for your detailed and valuable comment. It was the key that guided me in the right direction after weeks of struggling with my APC SMC1500I.

I wanted to share an update on my situation based on your advice:

✅ What I have done so far (exactly as you recommended):

I purchased the exact same cables you mentioned:

APC AP940-0625A (Smart Signalling cable, RJ50 to DB9)

A USB to RS232 adapter with FTDI chipset (CableCreation brand, which uses the reliable FTDI chip)

I connected everything as you described:

RJ50 (UPS) ← [AP940-0625A] → DB9 Male ← [FTDI adapter] → USB ← my Armbian server

The FTDI adapter is recognized correctly (/dev/ttyUSB0), and I verified it with lsusb (ID 0403:6001).

I installed apcupsd and configured it based on your setup:

UPSCABLE smart
UPSTYPE apcsmart
DEVICE /dev/ttyUSB0
LOCKFILE /var/lock
I also updated the UPS firmware to version 15.1 (which was available for my unit).

Current situation / The remaining challenge:
Although apcupsd starts successfully, it keeps failing with:

PANIC! Cannot communicate with UPS via serial port.
I tried both BAUDRATE 2400 and 9600, but the result is the same. I also tested the serial port with minicom (/dev/ttyUSB0 at 2400 baud), but I see no data at all – the screen remains completely blank.

My question / What I suspect:

Is there a specific setting I need to enable on the UPS itself (via the LCD panel) to activate Modbus or serial communication?

Could the cable be faulty, or am I missing something in the physical connection?

I would really appreciate any further insights you might have. Your previous guidance was spot-on, and I feel I am just one step away from finally getting this to work.

Thank you again for your time and help!

@UltrashRicco
Copy link
Copy Markdown

UltrashRicco commented Feb 18, 2026

Hi @omarkhali thank you for your message, I am glad I could help!

I may have good news for you: yes indeed, there is a setting you need to enable from the UPS's user interface!

In my case (SMC1500IC), the setting is as follows: setting cP (Communication Protocol) has to show cP.1 (enabled).
image

I guess it should be very similar on your SMC1500I, check your user manual.
I hope this sorts it out for you!

Just FYI, as I have a couple of devices that do not have an APCUPSD client to run, I am also executing upsnutwrapper https://github.com/gitmachtl/various/tree/main/upsnutwrapper which connects to the apcupsd server and translates the values fetched by APCUPSD to the NUT protocol, and works as a NUT server on your network for other devices to connect to.
I use the APCUPSD client on Windows hosts, and the NUT client on linux hosts.
It may be useful to you!

Here is the essential settings in my /etc/apcupsd.conf below:
UPSNAME SMC1500
UPSCABLE 940-0625A
UPSTYPE modbus
DEVICE /dev/ttyUSB0
(sorry, I was not able to correclty format/display the above inside Code tags)

I can provide the full config file if needed but the rest is specific to how you want your UPS to behave and thus not relevant.

I do not remember having to configure the serial port anywhere, in fact I would not even know where/how to do that, but if you need to take a look at my serial config, feel free to let me know which config files you would like to check.

EDIT: the output of $ stty -F /dev/ttyUSB0 -a (serial port config) is:

speed 9600 baud; rows 0; columns 0; line = 176; intr = "; quit = A; erase = M-?; kill = U; eof = <undef>; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = <undef>; stop = <undef>; susp = <undef>; rprnt = <undef>; werase = <undef>; lnext = ; discard = <undef>; min = 0; time = 0; -parenb -parodd -cmspar cs8 -hupcl -cstopb cread clocal -crtscts -ignbrk -brkint ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff -iuclc -ixany -imaxbel -iutf8 -opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 -isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt -echoctl -echoke -flusho -extproc

So that is 9600 bauds, but I have no clue what stop and parity bits I am using. Everything is stock/out of the box, I do not think I ever configured it. Now I need to check the UPS's serial settings to make sure mine match! :)

EDIT: according to https://www.se.com/fr/fr/download/document/SPD_MPAO-98KJ7F_EN/ these are the settings for Modbus over serial:
image
image
image

baud rate: 9600
data bits: 8
no parity bit
one stop bit
no flow control

I was never able to get a console/command line interface to appear. I think there may not be any on these "smart" models.

Hope it helps, let me know!

Oh and also, since the internal batteries were pretty much dead, I replaced them with 2 external 12V car batteries for cheaper than the cheapest of original size replacement batteries. You can make your own replacement battery pack from any size 12V batteries, 2 batteries are needed in series to deliver 24V. There are video tutorials available on YouTube, etc. Now I have 5 times the capacity, hence 5 times the backup time.

@jimklimov
Copy link
Copy Markdown
Member

On a side note, an alternative can be NUT's own apcupsd-ups driver which relays (read-only) info from an apcupsd server.

@omarkhali
Copy link
Copy Markdown

@UltrashRicco Thank you for your kind and detailed reply. I seem to have given up. I don't know how to configure the CP settings. There are no options on the UPS screen, only the power button, display button, and volume button.

IMG_2264
C920BDE2684E043885257AE20065DA0E_EWAR_93EPSJ_f_v_hi_369

@omarkhali
Copy link
Copy Markdown

I'm currently working on this project. I believe it doesn't require CP activation.
syssi/esphome-apc-ups#40

@UltrashRicco
Copy link
Copy Markdown

@omarkhali sorry to read that.

Try and find out whether your UPS does support Modbus and/or smart signaling to begin with, and which cable type it requires to communicate over serial.

You can try to play around with the different cable types and UPS types in apcupsd's configuration.

If you are unable to get it to work, you may need the 940-0128D (Simple Signaling) serial cable instead, but I am not quite sure what kinds of data can be fetched over the simple signaling serial protocol.
You may prefer to stick to USB!

@UltrashRicco
Copy link
Copy Markdown

On a side note, an alternative can be NUT's own apcupsd-ups driver which relays (read-only) info from an apcupsd server.

Thank you!

In my case, I was not able to use the NUT apcupsd-ups driver on my NAS running TrueNAS, despite it being among the driver options.

I was not able to connect to the apcupsd server in that way, and since nut and apcupsd cannot be installed concurrently on the same debian-based machine, I had to resort to upsnutwrapper instead to create a nut server.

Your advice is perfectly valid though, as any other machine on the network could use nut with the apcupsd-ups driver and work as a nut server for other clients to connect to.

@zDEFz
Copy link
Copy Markdown

zDEFz commented Feb 21, 2026

Yo guys. After my SMT1500ic kept eating batteries to breakfast, I switched to Eaton 5PX G2 Netpack with additional EBM. Benefit is you can do SNMP walk now, and all you need is just there!

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