ragtech: new driver for Ragtech UPS (USB CDC-ACM, family 10)#3447
ragtech: new driver for Ragtech UPS (USB CDC-ACM, family 10)#3447juslex wants to merge 1 commit into
Conversation
58ce033 to
dd33df6
Compare
|
A ZIP file with standard source tarball and another tarball with pre-built docs for commit 92cd420 is temporarily available: NUT-tarballs-PR-3447.zip. |
f52069c to
03195a7
Compare
|
❌ Build nut 2.8.5.4730-master failed (commit be8146e980 by @juslex) |
03195a7 to
a844e27
Compare
a844e27 to
c7f1687
Compare
c7f1687 to
8f1fdea
Compare
Targets Brazilian-built Ragtech "Easy Pro" / NEP / TORO / INNERGIE / OneUP
devices that present themselves as USB CDC-ACM (VID 0x04D8, PID 0x000A,
Microchip PIC firmware). Validated end-to-end against an Easy 2000 TI
(reg 0x9A model id = 16) read out of a working OneUP Nitro 2000.
Protocol -- three opcodes observed in OEM traffic:
0x01 ADDR_HI ADDR_LO VALUE CKSUM write byte
0x02 ADDR_HI ADDR_LO MASK CKSUM AND mask (atomic bit-clear)
0x04 ADDR_HI ADDR_LO COUNT CKSUM read range
CKSUM = (ADDR_HI + ADDR_LO + VALUE) & 0xFF
The CDC-ACM channel ignores baud at the wire but DTR/RTS are interpreted
by some Ragtech families as a remote shutdown signal -- the driver forces
both low after open and does NOT call ser_set_speed() to avoid the
tcsetattr() DTR pulse that some Linux tty drivers perform.
The full 30-byte main range (0x80..0x9D) plus V_IOUTCALIB (0xF3) and the
oscillator calibration pair (0x202/0x203) are read; output frequency is
interpolated per devices.xml formula rather than hard-coded.
Twenty models from the family-10 device table populate ups.model,
ups.realpower.nominal, output.voltage scaling, output.current scaling and
battery voltage scaling. input.voltage.nominal switches between 115V and
220V dynamically based on the measured input.
Instcmds implemented:
shutdown.stayoff -- aa 02 00 80 fe (clear AUTOSTART) + aa 01 00 98 N.
Byte-for-byte identical to the OEM supsvc sequence.
Validated: cuts output and stays off until manual
power-on, in both AC and battery modes.
shutdown.return -- falls back to shutdown.stayoff with a warning.
The atomic OR opcode that would set F_AUTOSTART
has not been captured yet; read-modify-write with
0x01 sends the bytes but does not trip the
firmware's shutdown state machine.
shutdown.stop -- aa 01 00 98 00 (write V_SHUTDOWNTIMER = 0).
Confirmed to abort an armed countdown.
test.battery.start.deep -- bit writes on regs 0x90 / 0x95 (fullDischarge
in devices.xml). Flagged as unverified for the
same reason as shutdown.return.
Read primitive uses ser_get_buf() in a manual loop rather than
ser_get_buf_len() (which discards partial reads on timeout -- CDC fragments
replies into 1-8 byte chunks). ser_flush_io() is used before each TX to
clear the kernel CDC buffer (ser_flush_in() only does a select+read loop
that misses queued bytes under O_NONBLOCK).
Reverse engineering primarily from the OEM devices.xml table for register
layout / scaling / flags / actions, and from strace of the OEM supsvc
binary capturing both read polls and the LED/shutdown write sequences.
Cross-checked against UPS_ESP32_tinySrv (https://github.com/antunesls/UPS_ESP32_tinySrv).
Signed-off-by: juslex <66561713+juslex@users.noreply.github.com>
8f1fdea to
92cd420
Compare
|
✅ Build nut 2.8.5.4737-master completed (commit 6ac18c0a7b by @juslex)
|
|
✅ Build nut 2.8.5.4737-master completed (commit 6ac18c0a7b by @juslex) |
|
@jimklimov I used the claude code to initially get the communication information made by other projects, especially https://github.com/antunesls/UPS_ESP32_tinySrv. After that, I started new tests and ended up discovering more information. I'm currently running a version of nut with this proposed PR and it's working perfectly. I'm not a developer, I used the claude code for that. But I'm available to try to help. Thank you! |
Summary
Adds a new
ragtechdriver supporting the Ragtech "Easy Pro" family ofline-interactive UPS units (also sold under the NEP, TORO,
INNERGIE and OneUP brands in Brazil). Devices expose a USB CDC-ACM
serial interface (
VID 0x04D8/PID 0x000A, Microchip PIC firmware) andspeak a proprietary 6-byte register-access protocol.
drivers/ragtech.c(no sharedsub-driver layer — Ragtech is not a Megatec/Qx variant).
ups.model,ups.realpower.nominal, output voltage/current scaling and batteryvoltage scaling.
input.voltage.nominalswitches between 115V and 220V dynamically basedon the measured input (TI vs M2 variants).
straceof the OEMsupsvcbinary and the OEMdevices.xml, cross-checked againstantunesls/UPS_ESP32_tinySrv.
Protocol (decoded from OEM traffic)
Every command on the wire is 6 bytes:
0xAA OPCODE ADDR_HI ADDR_LO VALUE CHECKSUMwhere
CHECKSUM = (ADDR_HI + ADDR_LO + VALUE) & 0xFF.0x010x020x030x04Instant commands
shutdown.stayoff— byte-for-byte identical to the OEMsupsvcsequence.Verified on hardware: cuts output and stays off until manual power-on
in both AC and battery modes.
shutdown.stop— aborts an armed countdown. Verified on hardware.shutdown.return— implemented as ashutdown.stayoffwith aLOG_WARNING. The Easy 2000 TI firmware does not auto-restart onmains return; the operator must press the power button. The driver
surfaces this consistently to
upsmoncallers rather than silentlyfailing.
test.battery.start.deep— bit writes matching the OEMfullDischargeaction. Implemented but not yet verified end-to-end (would empty the
battery during testing).
Safety: shutdowns are opt-in by default
Because the firmware does not auto-restart, an unintended
shutdown.return(e.g. from
upsmonon a low-battery event) would leave the UPS off untilsomebody physically presses the power button. To avoid silent-fail
integrations, every
shutdown.*andtest.battery.start.deepinstcmd isgated behind
allow_shutdowninups.conf:rejected with
STAT_INSTCMD_INVALID, andupsdrv_shutdownexits withfailure rather than cutting output.
The default-deny posture is conservative on purpose; a future Ragtech model
with working auto-restart could carry a
firmware_returnscapability inthe model table to flip the default.
Wire-level notes
ser_get_buf_len()is all-or-nothing: it discards already-collectedbytes on timeout. CDC-ACM fragments replies into 1–8 byte chunks, so the
driver uses
ser_get_buf()in a manual loop.ser_flush_in()does not calltcflush(); underO_NONBLOCKUSB CDCbytes inside the tty layer survive its select+read loop. The driver uses
ser_flush_io()(which callstcflush(TCIOFLUSH)) before each TX.ser_set_speed()on the CDC-ACM endpoint —tcsetattr(TCSANOW)pulses DTR on some Linux tty drivers, which theRagtech firmware can interpret as a shutdown signal. CDC-ACM ignores
baud at the wire anyway.
ser_open(the UPS readsnon-zero levels as a remote-shutdown signal).
tcdrain()+ ~50 ms of slack after everyser_send_buf()to let thefirmware compose the reply.
Files changed
drivers/ragtech.c— new driver (single C file).drivers/Makefile.am— registered inSERIAL_DRIVERLIST,ragtech_SOURCES,ragtech_LDADD.docs/man/ragtech.txt— new AsciiDoc man page.docs/man/Makefile.am— three registrations (.txt,MAN_SECTION_CMD_SYS,.html).data/driver.list.in— 20 entries for the family-10 models (level "1",reverse engineering).
docs/nut.dict— addedINNERGIE,NEP,OneUP,Ragtech,TOROandbumped the word count.
NEWS.adoc— new-driver entry under 2.8.6.Driver status
DRV_EXPERIMENTALat version0.04. Only the Easy 2000 TI has beenvalidated end-to-end with hardware; the other 19 models share the same
register layout and scaling table but have not been hardware-tested yet.
Test plan
ups.*variables on Easy 2000 TI — verified.shutdown.stayoff— verified on hardware (AC and battery modes).shutdown.stop— verified on hardware (aborts armed countdown).iout_caliboverride viaups.conf— verified.allow_shutdowndefault-deny posture — verified (instcmds notregistered,
upsdrv_shutdownexits with failure).shutdown.returnend-to-end — not verified (firmware does notauto-restart; surfaces as
stayoff+ warning).test.battery.start.deep— implemented but not verified(deliberately not exercised during RE — would discharge battery).
Welcoming feedback on the
shutdown.returndesign (currently warns andstays off), and on whether to fold the model discrimination into a
per-model scaling table once a second model is verified.