It was necessary to control a Riden power supply for some lab automation purposes. The stock software was useless for the purpose.
Fortunately, the protocol is documented enough, and based on MODBUS.
All available software is in this or that form dependent on some MODBUS library, which relies on the assumption of a fully functional local or at least virtual serial port of /dev/ttySx or /dev/ttyUSBx or such nature.
The wired ports require... umm... wires. USB to a nearby computer. Which is not always feasible. The virtual COM ports require OS-specific (and often capricious) port emulating software on the computer side. All when all that's necessary is a pipe for bytes in, bytes out.
An expedient plain serial-over-TCP ("TasmoCOM") solution was chosen, leveraging ESP8266
and Tasmota
, a proven cheap and opensource approach.
For the hardware, see hw_RD6024powersupply.
For the control code for an artificial load, see sw_dl24.
Tested with:
rd60.py, a python-based (for portability) script, was written. The software allows both using a tty-style port and a raw TCP socket, with no fancy RFC2217 support. If the latter is needed, URI-style pyserial syntax is available with the port.
The software defines a hierarchy of classes:
The software tries to minimize dependencies.
The mandatory ones, and mostly standard ones, are:
RD60 Riden RD60xx power supply control Usage: ./rd60.py <command> [command]... Commands: ON enable output OFF disable output nn.nnV set output voltage nn.nnMA set output current nn.nnA set output current nn.nnVO set overvoltage protection nn.nnMAO set overcurrent protection nn.nnAO set overcurrent protection QV query actual voltage QA query actual current QBV query battery voltage QTE query external temperature QTI query internal temperature QREGxx or Qxx query register xx (name or decimal) STATE[:opts] print setting state in JSON format STATEJ[:opts] print setting state in JSON format, like opts=J opts: J=JSON, S=short (V/A only), T=show time, U=show UTC time, B=force battery REGxx=yy write register xx to value yy (decimal) REGS dump registers REGSALL dump registers including memories and calibration REGSALL:n dump n registers REGSDIFF show registers difference TCP=addr[:port] set connection via TCP PORT=/dev/ttyport[@baud] set connection via serial port ROBUST increase timeouts and retries BAT enable battery charging related registers NOBAT disable battery charging related registers STDIN read commands from stdin LOOP:[xx] loop for xx time or endless if not specified SLEEPxx sleep for xx seconds VERB or -V list MODBUS transactions; affects subsequent commands LINE output the Q-queries as space-separated instead of newline-separated SETMEMx=v,a,vo,ao set memory x to values MEMS dump memories SETCLOCK set clock to current datetime CFGFILE generate config file template to stdout For volt and amp setting, prefixing the value with + or - marks it as relative, to be added/subtracted to the current value Commands are executed in sequence. Writes are cached and grouped together to minimize bus transactions. Commands are case-insensitive. Command "-" forces a newline into output.
The host:port or serport:baudrate are saved in ~/.rd60.cfg (or other name, where filename is derived from the command by stripping the .py suffix and prefixing a home directory and a dot). This variability allows to use several symlinks for different power supplies simultaneously used, eg. as rd60a, rd60b,...
The configfile template can be generated on demand by command CFGFILE.
Directly, the devices may be specified as TCP=<host>[:port] or PORT=/dev/ttyUSBx@baudrate, eg. TCP=10.0.1.15:8888 or PORT=/dev/ttyUSB1@115200 or PORT=/dev/ttyUSB1 (default speed is 115200).
The PORT directive, both in command and in config, also supports the URL form.
The script takes a sequence of commands from commandline, separated by spaces. Each command is a single token, optionally containing separator characters.
The commands can be a fixed string (STATE, REGS, QV, ...) or a prefix with value, or value with suffix (12.5V, SLEEP1.5, REG12=8123, QVIN...)
Register names can be sent as decimal address or as a name. Q3 and QFW are the same.
Some commands look like register commands but refer directly to values:
The REGS and REGSALL commands read and dump the power supply's MODBS registers. REGS only first 42, REGSALL take in 122 (which includes calibration registers (do not touch unless you MUST) and the memories (M0 is for the currently set overvoltage and overcurrent protections (OVP and OCP), M1..M9 are for presets).
The registers dump shows register address in hex and decimal, value in decimal and hex, r/rw for readonly and readwrite, short name, and description. Registers with zero value and no known name are assumed unused and are skipped.
x00 0 60241 0xeb51 r ID type ID x01 1 0 0x0000 r SN_H serial number H x02 2 10542 0xXXXX r SN_L serial number L x03 3 138 0x008a r FW firmware version *100 x04 4 0 0x0000 r INT_C_S INT_C_S x05 5 44 0x002c r INT_C internal temperature C x06 6 0 0x0000 r INT_F_S INT_F_S x07 7 111 0x006f r INT_F internal temperature F x08 8 1000 0x03e8 rw V_SET set voltage x09 9 210 0x00d2 rw I_SET set current x0a 10 998 0x03e6 r V_OUT actual voltage x0b 11 0 0x0000 r I_OUT actual current x0c 12 0 0x0000 r AH AH x0d 13 0 0x0000 r P_OUT actual power x0e 14 6789 0x1a85 r V_IN input voltage x0f 15 1 0x0001 r KEYPAD keyboard locked x10 16 0 0x0000 r OVP_OCP OVP-OCP active x11 17 0 0x0000 r CV_CC output at constant current x12 18 1 0x0001 rw OUTPUT output enabled x13 19 0 0x0000 rw PRESET preset number x14 20 0 0x0000 r I_RANGE I_RANGE x20 32 0 0x0000 r BAT_MODE battery mode enabled x21 33 0 0x0000 r V_BAT battery voltage x22 34 1 0x0001 r EXT_C_S EXT_C_S x23 35 89 0x0059 r EXT_C battery temperature C x24 36 1 0x0001 r EXT_F_S EXT_F_S x25 37 129 0x0081 r EXT_F battery temperature F x26 38 0 0x0000 r AH_H amphour H x27 39 0 0x0000 r AH_L amphour L x28 40 0 0x0000 r WH_H watthour H x29 41 0 0x0000 r WH_L watthour L
or, for REGSALL, also
x30 48 2023 0x07e7 rw YEAR clock year x31 49 12 0x000c rw MONTH clock month x32 50 16 0x0010 rw DAY clock day x33 51 0 0x0000 rw HOUR clock hour x34 52 20 0x0014 rw MINUTE clock minute x35 53 44 0x002c rw SECOND clock second x37 55 24 0xXXXX rw V_OUT_ZERO calibration V_OUT_ZERO x38 56 24859 0xXXXX rw V_OUT_SCALE calibration V_OUT_SCALE x39 57 33 0xXXXX rw V_BACK_ZERO calibration V_BACK_ZERO x3a 58 24543 0xXXXX rw V_BACK_SCALE calibration V_BACK_SCALE x3b 59 334 0xXXXX rw I_OUT_ZERO calibration I_OUT_ZERO x3c 60 4327 0xXXXX rw I_OUT_SCALE calibration I_OUT_SCALE x3d 61 59 0xXXXX rw I_BACK_ZERO calibration I_BACK_ZERO x3e 62 4663 0xXXXX rw I_BACK_SCALE calibration I_BACK_SCALE x42 66 1 0x0001 rw OPT_TAKE_OK OPT_TAKE_OK x43 67 0 0x0000 rw OPT_TAKE_OUT output enabled on memory select x44 68 0 0x0000 rw OPT_BOOT_POW output enabled on boot x45 69 1 0x0001 rw OPT_BUZZ buzzer enabled x46 70 1 0x0001 rw OPT_LOGO logo enabled on boot x47 71 0 0x0000 rw OPT_LANG interface language x48 72 3 0x0003 rw OPT_LIGHT display backlight level x50 80 300 0x012c rw M0_V currently set V x51 81 200 0x00c8 rw M0_I currently set I x52 82 2000 0x07d0 rw M0_OVP currently set OVP x53 83 220 0x00dc rw M0_OCP currently set OCP x54 84 600 0x0258 rw M1_V memory M1 V x55 85 200 0x00c8 rw M1_I memory M1 I x56 86 700 0x02bc rw M1_OVP memory M1 OVP x57 87 500 0x01f4 rw M1_OCP memory M1 OCP x58 88 500 0x01f4 rw M2_V memory M2 V x59 89 2410 0x096a rw M2_I memory M2 I x5a 90 6200 0x1838 rw M2_OVP memory M2 OVP x5b 91 2420 0x0974 rw M2_OCP memory M2 OCP x5c 92 500 0x01f4 rw M3_V memory M3 V x5d 93 2410 0x096a rw M3_I memory M3 I x5e 94 6200 0x1838 rw M3_OVP memory M3 OVP x5f 95 2420 0x0974 rw M3_OCP memory M3 OCP x60 96 500 0x01f4 rw M4_V memory M4 V x61 97 2410 0x096a rw M4_I memory M4 I x62 98 6200 0x1838 rw M4_OVP memory M4 OVP x63 99 2420 0x0974 rw M4_OCP memory M4 OCP x64 100 500 0x01f4 rw M5_V memory M5 V x65 101 2410 0x096a rw M5_I memory M5 I x66 102 6200 0x1838 rw M5_OVP memory M5 OVP x67 103 2420 0x0974 rw M5_OCP memory M5 OCP x68 104 500 0x01f4 rw M6_V memory M6 V x69 105 2410 0x096a rw M6_I memory M6 I x6a 106 6200 0x1838 rw M6_OVP memory M6 OVP x6b 107 2420 0x0974 rw M6_OCP memory M6 OCP x6c 108 500 0x01f4 rw M7_V memory M7 V x6d 109 2410 0x096a rw M7_I memory M7 I x6e 110 6200 0x1838 rw M7_OVP memory M7 OVP x6f 111 2420 0x0974 rw M7_OCP memory M7 OCP x70 112 500 0x01f4 rw M8_V memory M8 V x71 113 2410 0x096a rw M8_I memory M8 I x72 114 6200 0x1838 rw M8_OVP memory M8 OVP x73 115 2420 0x0974 rw M8_OCP memory M8 OCP x74 116 500 0x01f4 rw M9_V memory M9 V x75 117 2410 0x096a rw M9_I memory M9 I x76 118 6200 0x1838 rw M9_OVP memory M9 OVP x77 119 2420 0x0974 rw M9_OCP memory M9 OCP
The voltage, current, and overvoltage/overcurrent protection can be set with suffix-based commands. For the value of 1.23, the commands are
The battery-related registers are not refreshed during usual operations. If the power supply is to be used in battery charging duty, enable related reporting with command BAT.
The commands are executed in order. To minimize bus transactions, the ones from the low memory area (first 42 registers) are grouped into a single write.
The LOOP: statement can be used for repeating of commands. The subsequent command set is repeated forever, or for specified number of times.
The commands can be sent from another script, via stdin. The STDIN statement has to be the last on the command line, everything after it is ignored.
The LINE command sets the separator character between Q-values from default newline to a space. Groups of values then can be sent as single lines.
The connection to the port is opened when first needed, then kept open until the process closes.
In some cases this may be detrimental to reliability (connection fail crashes the process). Running it anew each time may be beneficial then.
To see the port/socket opening/closing, and the bus transactions dumped in hex, use VERB as the first command.
CONFIGFILE:filename: /home/user/.rd60.cfg CONFIGFILE: {'host': 'rd60', 'port': '8888'} SOCK:connecting to rd60 : 8888 SOCK:connected MODBUS:CMND:READREGS 0 42 MODBUS:SEND 01:03:00:00:00:2a:c4:15 (correct) MODBUS:RECV 01:03:54:eb:51:00:00:XX:XX:00:8a:00:00:00:2c:00:00:00:70:03:e8:00:d2:03:e6:00:00:00:00:00:00:1a:84:00:00:00:00:00:00:00:01:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:01:00:59:00:01:00:81:00:00:00:00:00:00:00:00:XX:cd (correct) MODBUS:CMND:READREGS 82 2 MODBUS:SEND 01:03:00:52:00:02:65:da (correct) MODBUS:RECV 01:03:04:07:d0:00:dc:fb:27 (correct) Vin =67.88 v tempin =44 'c tempext=89 'c Vset=10.00 v OVP =20.00 v Iset= 2.10 a OCP = 2.20 a OUTPUT ENABLED Vout= 9.98 v Iout= 0.00 a SOCK:closed
The INT_C and EXT_C registers contain the power supply board temperature and the external NTC probe temperature in degrees C. (The corresponding _F registers use the Fahrenheit abomination.)
Caution, if the external probe is not attached the register value contains swinging nonsense.
The power supply has a range of registers for specific use with battery charging (battery voltage measurements, watthours, amphours). To conserve socket/port transfers volume, this registers block is read only if the power supply is in battery mode.
The battery mode is autodetected when the program starts; for prolonged loop-based operation, this will miss the battery detection event and the mode won't be changed, as the register is in the area that's read only during first start (unless in battery mode).
The battery mode can be also manually forced by BAT or disabled by NOBAT.
The software borrows a trick (and table of devices) from Sigrok's libsigrok, the rdtech-dps
component. From the table of devices, based on the device ID (register 0), the type is identified and the voltage and amperage multipliers are loaded.
(The registers provide data as 16bit integers, which have to be converted to the floats. With multiplier of 100, a 1.28 amp is set or read as 128.
The multiplier has to be known, and is type-specific, otherwise the voltages/currents can be off by a magnitude or two. 100 and 100 is default when the
type is unknown.