Hello Hans-Peter,
On Fri, 12 May 2006, Hans-Peter Jansen wrote:
> some googling revealed, you created a driver for the xanto series of
> online-usv.de. In what state it is currently?
It is working quite good actually.
Polling the device information works, I figured out the correct data in
order to predict the remaining battery time quite well and setting the usv
to bypass etc. works as well.
> I've to manage a S2000 and would like to use nut for it, is it usable
by
> now? Do you need another tester?
It is usuable. I only haven't gotten it submitted to the nut people
because of a lack of time and I'd love to fix up one part of the code
before.
I am parsing the data from the USV via sscanf. That could be problematic
if one passes carefully constructed data to the driver.
I hadn't had the time yet, to fix that as my C knowledge is rather
limited.
However, the patch to nut is attached and I'd love to hear how it works
for you. We are using it on two S2000 with one battery pack each.
bye,
andreas
-------------- next part --------------
--- nut-2.0.1/drivers/Makefile.in.xanto 2004-07-15 08:30:18.000000000 +0200
+++ nut-2.0.1/drivers/Makefile.in 2005-11-01 01:23:33.484801800 +0100
@@ -12,7 +12,8 @@
PROGS = bestups fentonups apcsmart everups belkin masterguard powercom \
cyberpower tripplite victronups genericups mge-utalk bestuferrups \
isbmex etapro liebert sms esupssmart tripplitesu blazer safenet \
- belkinunv oneac ippon cpsups mustek metasys bestfcom powermust
+ belkinunv oneac ippon cpsups mustek metasys bestfcom powermust \
+ xanto
LIBDEP = @LIBOBJ@
LIBOBJ = $(LIBDEP) @NETLIBS@ @SERLIBS@
--- nut-2.0.1/drivers/Makefile.drvbuild.xanto 2005-02-24 07:57:57.000000000
+0100
+++ nut-2.0.1/drivers/Makefile.drvbuild 2005-11-01 01:23:33.485801648 +0100
@@ -61,3 +61,6 @@
$(CC) $(CFLAGS) -o bestfcom bestfcom.o main.o serial.o dstate.o
../common/state.o ../common/upsconf.o ../common/parseconf.o $(LIBOBJ)
powermust: powermust.o main.o serial.o dstate.o ../common/state.o
../common/upsconf.o ../common/parseconf.o $(LIBDEP)
$(CC) $(CFLAGS) -o powermust powermust.o main.o serial.o dstate.o
../common/state.o ../common/upsconf.o ../common/parseconf.o $(LIBOBJ)
+xanto: xanto.o main.o serial.o dstate.o ../common/state.o ../common/upsconf.o
../common/parseconf.o $(LIBDEP)
+ $(CC) $(CFLAGS) -o xanto xanto.o main.o serial.o dstate.o ../common/state.o
../common/upsconf.o ../common/parseconf.o $(LIBOBJ)
+
--- nut-2.0.1/drivers/xanto.c.xanto 2005-11-01 01:23:33.488801192 +0100
+++ nut-2.0.1/drivers/xanto.c 2005-11-01 01:28:10.563679408 +0100
@@ -0,0 +1,376 @@
+/* xanto.c - driver for Online XANTO UPS hardware
+
+ Copyright (C) 2005 Andreas Thienemann <andreas@bawue.net>
+
+ based on sms.c:
+
+ Copyright (C) 2001 Marcio Gomes <tecnica@microlink.com.br>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ 2005/10/31 - Version 0.10 - Initial release
+
+
+*/
+
+#define DRV_VERSION "0.10"
+
+#include "main.h"
+#include "serial.h"
+#include "xanto.h"
+
+#define ENDCHAR 13 /* replies end with CR */
+
+static int vin_min, vin_max, poll_failures = 0;
+static int upsmodel = -1;
+static float lowvolt = 0;
+static float voltrange = 0;
+
+
+/* show all possible UPS types */
+static void listtypes(void)
+{
+ int i;
+
+ printf("Valid UPS types:\n\n");
+
+ for (i = 0; modeltab[i].mtext != NULL; i++)
+ printf("%2i: %s\n", i, modeltab[i].desc);
+}
+
+
+static void set_ups_model(void)
+{
+ int i;
+
+ if (!getval("upsmodel"))
+ fatalx("No upsmodel set - see help text / man
page!\n");
+
+ upsmodel = atoi(getval("upsmodel"));
+
+ for (i = 0; modeltab[i].mtext != NULL; i++) {
+ if (upsmodel == i) {
+ printf("UPS type: %s\n", modeltab[i].mtext);
+ lowvolt = modeltab[i].lowvolt;
+ voltrange = modeltab[i].voltrange;
+ vin_min = modeltab[i].vin_min;
+ vin_max = modeltab[i].vin_max;
+ return;
+ }
+ }
+
+ printf("\nFatal error: unknown UPS model\n");
+ listtypes();
+
+ exit(EXIT_FAILURE);
+}
+
+static void getbaseinfo(void)
+{
+
+ dstate_setinfo("ups.mfr", "%s", "Online");
+
+ dstate_setinfo("ups.model", "%s",
modeltab[upsmodel].desc);
+
+ /* now add instant command support info */
+ dstate_addcmd("test.battery.start");
+ dstate_addcmd("test.battery.stop");
+ dstate_addcmd("test.failure.start");
+ dstate_addcmd("test.failure.stop");
+ dstate_addcmd("shutdown.return");
+ dstate_addcmd("shutdown.stayoff");
+ dstate_addcmd("shutdown.stop");
+ dstate_addcmd("bypass.start");
+ dstate_addcmd("bypass.stop");
+
+ printf("Configured %s on %s\n",
dstate_getinfo("ups.model"), device_path);
+
+ /* paranoia - cancel any shutdown that might already be running */
+ ser_send(upsfd, "C\r");
+}
+
+
+static int instcmd(const char *cmdname, const char *extra)
+{
+ if (!strcasecmp(cmdname, "test.battery.stop")) {
+ ser_send(upsfd, "CT\r");
+ return STAT_INSTCMD_HANDLED;
+ }
+
+ if (!strcasecmp(cmdname, "test.battery.start")) {
+ ser_send(upsfd, "TL\r"); /* start battery test until bat low */
+ return STAT_INSTCMD_HANDLED;
+ }
+
+ if (!strcasecmp(cmdname, "test.failure.start")) {
+ ser_send(upsfd, "T\r"); /* start battery test for 10sec */
+ return STAT_INSTCMD_HANDLED;
+ }
+
+ if (!strcasecmp(cmdname, "test.failure.stop")) {
+ ser_send(upsfd, "T\r");
+ return STAT_INSTCMD_HANDLED;
+ }
+
+ if (!strcasecmp(cmdname, "shutdown.return")) {
+ /* shutdown and restart */
+ ser_send(upsfd, "C\r");
+ ser_send(upsfd, "S.2R0003\r");
+ return STAT_INSTCMD_HANDLED;
+ }
+
+ if (!strcasecmp(cmdname, "shutdown.stayoff")) {
+ /* shutdown now (one way) */
+ ser_send(upsfd, "C\r");
+ ser_send(upsfd, "S.2\r");
+ return STAT_INSTCMD_HANDLED;
+ }
+
+ if (!strcasecmp(cmdname, "shutdown.stop")) {
+ /* Cancel Shutdown */
+ ser_send(upsfd, "C\r");
+ return STAT_INSTCMD_HANDLED;
+ }
+
+ if(!strcasecmp(cmdname, "bypass.stop")) {
+ ser_send(upsfd, "FON\r");
+ return STAT_INSTCMD_HANDLED;
+ }
+
+ if(!strcasecmp(cmdname, "bypass.start")) {
+ ser_send(upsfd, "FOFF00\r");
+ return STAT_INSTCMD_HANDLED;
+ }
+
+ upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname);
+ return STAT_INSTCMD_UNKNOWN;
+}
+
+void upsdrv_initinfo(void)
+{
+ set_ups_model();
+ getbaseinfo();
+
+ upsh.instcmd = instcmd;
+ dstate_setinfo("driver.version.internal", "%s",
DRV_VERSION);
+}
+
+static void pollfail(const char *why)
+{
+ poll_failures++;
+
+ usleep(1000);
+
+ /* don't spew into the syslog forever */
+ if (poll_failures < 3)
+ upslogx(LOG_ERR, why);
+
+ return;
+}
+
+void upsdrv_updateinfo(void)
+{
+ char temp[256], utility[16], loadpct[16], acfreq[16], battvolt[16],
+ upstemp[16], pstat[16], outvolt[16];
+ int util, ret;
+ double bvoltp;
+
+ ser_send(upsfd, "Q1\r");
+
+ /* Sleep a moment, to give the UPS a chance to answer the readings */
+ usleep(500);
+
+ ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, "", 3, 0);
+
+ /* sanity checks for poll data */
+ if (strlen(temp) < 46) {
+ pollfail("Poll failed: short read from UPS");
+ dstate_datastale();
+ return;
+ }
+
+ if (strlen(temp) > 46) {
+ pollfail("Poll failed: oversized read from UPS");
+ dstate_datastale();
+ return;
+ }
+
+ if (temp[0] != '(') {
+ pollfail("Poll failed: invalid start character");
+ dstate_datastale();
+ return;
+ }
+
+ if (poll_failures > 0)
+ upslogx(LOG_NOTICE, "UPS poll succeeded");
+
+ poll_failures = 0;
+
+ /* (MMM.M NNN.N PPP.P QQQ RR.R S.SS TT.T b7b6b5b4b3b2b1b0<cr>
+ *
+ * MMM.M : input voltage (utility)
+ * NNN.N : fault voltage (ignored)
+ * PPP.P : output voltage
+ * QQQ : output load
+ * RR.R : input frequency
+ * S.SS : battery voltage
+ * TT.T : temperature
+ */
+
+ sscanf(temp, "%*c%s %*s %s %s %s %s %s %s", utility, outvolt,
loadpct,
+ acfreq, battvolt, upstemp, pstat);
+
+ dstate_setinfo("output.voltage", "%s", outvolt);
+ dstate_setinfo("input.voltage", "%s", utility);
+ dstate_setinfo("battery.voltage", "%s", battvolt);
+
+ bvoltp = ((atof(battvolt) - lowvolt) / voltrange) * 100.0;
+
+// bvoltp = atof(battvolt);
+
+ if (bvoltp > 100.0)
+ bvoltp = 100.0;
+
+ dstate_setinfo("battery.charge", "%02.1f", bvoltp);
+
+ status_init();
+
+ util = atoi(utility);
+
+
+/*
+ Bit: Description
+ 7 0: Utility power okay
+ 1: Utility power failed
+ 6 0: Battery charged
+ 1: Battery low
+ 5 0: Inverter active
+ 1: Bypass/Boost active
+ 4 0: UPS active
+ 1: UPS failed
+ 3 0: UPS online
+ 1: UPS standby
+ 2 0: Normal mode
+ 1: Test mode
+ 1 0: Normal mode
+ 1: Shutdown active
+ 0 0: (Reserved)
+*/
+
+
+ if (pstat[0] == '0') {
+ status_set("OL"); /* on line */
+
+ /* only allow these when OL since they're bogus when OB */
+ if (pstat[2] == '1') { /* boost or trim in effect */
+ if (util < vin_min) {
+ status_set("BOOST");
+ } else if (util > vin_max) {
+ status_set("TRIM");
+ } else {
+ status_set("BYPASS");
+ }
+ }
+
+ } else {
+ status_set("OB"); /* on battery */
+ }
+
+ if (pstat[1] == '1')
+ status_set("LB"); /* low battery */
+
+ // Not sure, if this is the correct interpretation of UPS Failed.
+ if (pstat[3] == '1')
+ status_set("RB"); /* Replace battery */
+
+ if (pstat[4] == '1')
+ status_set("OFF"); /* ups offline */
+
+ if (pstat[5] == '1')
+ status_set("CAL"); /* calibration or test */
+
+/*
+ * if (pstat[6] == '1')
+ * status_set(""); */ /* shutdown */
+
+
+ if (atof(loadpct) > 100)
+ status_set("OVER"); /* overload */
+
+ status_commit();
+
+ dstate_setinfo("ups.temperature", "%s", upstemp);
+
+ dstate_setinfo("input.frequency", "%s", acfreq);
+ dstate_setinfo("ups.load", "%s", loadpct);
+
+ dstate_dataok();
+}
+
+/* power down the attached load immediately */
+void upsdrv_shutdown(void)
+{
+ char temp[256], pstat[32];
+
+ /* basic idea: find out line status and send appropriate command */
+
+ ser_send(upsfd, "Q1\r");
+ ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, "", 3, 0);
+ sscanf (temp, "%*s %*s %*s %*s %*s %*s %*s %s", pstat);
+
+ /* on battery: send S01<cr>, ups will return by itself on utility */
+ /* on line: send S01R0003<cr>, ups will cycle and return soon */
+
+ ser_send(upsfd, "S01");
+
+ if (pstat[0] == '0') { /* on line */
+ printf("On line, sending shutdown+return command...\n");
+ ser_send(upsfd, "R0003");
+ } else
+ printf("On battery, sending normal shutdown command...\n");
+
+ ser_send_char(upsfd, 13); /* end sequence */
+}
+
+void upsdrv_help(void)
+{
+ printf("Acceptable values for -x or ups.conf in this driver:\n\n");
+ printf("Set UPS type (required) : -x upsmodel=<value>\n");
+ listtypes();
+ printf("Example: xanto -x upsmodel=7 /dev/ttyS0\n\n");
+}
+
+void upsdrv_makevartable(void)
+{
+ addvar(VAR_VALUE, "upsmodel", "Set UPS type (required)");
+}
+
+void upsdrv_banner(void)
+{
+ printf("Network UPS Tools - Online UPS XANTO driver %s (%s)\n",
+ DRV_VERSION, UPS_VERSION);
+ printf("by Andreas Thienemann - andreas@bawue.net\n\n");
+}
+
+void upsdrv_initups(void)
+{
+ upsfd = ser_open(device_path);
+ ser_set_speed(upsfd, device_path, B2400);
+}
+
+void upsdrv_cleanup(void)
+{
+ ser_close(upsfd, device_path);
+}
--- nut-2.0.1/drivers/xanto.h.xanto 2005-11-01 01:23:33.489801040 +0100
+++ nut-2.0.1/drivers/xanto.h 2005-11-01 01:23:33.488801192 +0100
@@ -0,0 +1,66 @@
+/* xanto.h - Online XANTO model capability table
+
+ This code is derived from Russell Kroll <rkroll@exploits.org>,
+ Fenton UPS Driver
+
+ Copyright (C) 2005 Andreas Thienemann <andreas@bawue.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ The Online UPSs do not offer a version string. Thus the user has to
+ manually choose his UPS.
+
+ 2005/10/31 - Version 0.01 - Initial Version from fentonups
+
+*/
+
+struct {
+ const char *mtext; // UPS Modelname
+ const char *desc; // UPS Description
+ int maxpwr; // Maximum Powervalue in VA
+ int holdtime; // Holdtime in minutes at 100% Load
+ int rechargetime; // Rechargetime in hours from 0% to 100% battery charge
+ int vin_min; // Minimum input voltage before switching to batteries
+ int vin_max; // Maximum input voltage before switchting to batteries
+ int temp_min; // Minimum operating temperature
+ int temp_max; // Maximum operating temperature
+ int battcell; // Number of UPS battery cells. Multiplicator of
batterycellvoltage
+ float lowvolt; // Minimum battery voltage, indicating the battery low status.
+ float voltrange; // Voltage range.
+}
+
+
+ modeltab[] =
+{
+{"XANTO_S_700_R", "XANTO S 700 R" , 700, 6, 8, 161,
276, 0, 65, 12, 1.65, 0.60},
+{"XANTO_S_1000_R", "XANTO S 1000 R" , 1000, 7, 8, 161,
276, 0, 65, 18, 1.65, 0.60},
+{"XANTO_S_1000_R_1B", "XANTO S 1000 R + 1 Battery" ,
1000, 35, 24, 161, 276, 0, 65, 18, 1.65, 0.60},
+{"XANTO_S_1000_R_2B", "XANTO S 1000 R + 2 Batteries" ,
1000, 53, 24, 161, 276, 0, 65, 18, 1.65, 0.60},
+{"XANTO_S_1500_R", "XANTO S 1500 R", 1500, 5, 8, 161,
276, 0, 65, 24, 1.65, 0.60},
+{"XANTO_S_1500_R_1B", "XANTO S 1500 R + 1 Battery" ,
1500, 26, 24, 161, 276, 0, 65, 24, 1.65, 0.60},
+{"XANTO_S_1500_R_2B", "XANTO S 1500 R + 2 Batteries" ,
1500, 46, 42, 161, 276, 0, 65, 24, 1.65, 0.60},
+{"XANTO_S_2000_R_1B", "XANTO S 2000 R + 1 Battery", 2000,
10, 24, 161, 276, 0, 65, 24, 1.65, 0.60},
+{"XANTO_S_2000_R_2B", "XANTO S 2000 R + 2 Batteries",
2000, 24, 42, 161, 276, 0, 65, 48, 1.65, 0.60},
+{"XANTO_S_2000_R_3B", "XANTO S 2000 R + 3 Batteries",
2000, 45, 42, 161, 276, 0, 65, 48, 1.65, 0.60},
+{"XANTO_S_2000_R_4B", "XANTO S 2000 R + 4 Batteries",
2000, 58, 42, 161, 276, 0, 65, 48, 1.65, 0.60},
+{"XANTO_S_2000_R_5B", "XANTO S 2000 R + 5 Batteries",
2000, 80, 42, 161, 276, 0, 65, 48, 1.65, 0.60},
+{"XANTO_S_3000_R_1B", "XANTO S 3000 R + 1 Battery", 3000,
6, 24, 161, 276, 0, 65, 48, 1.65, 0.60},
+{"XANTO_S_3000_R_2B", "XANTO S 3000 R + 2 Batteries",
3000, 13, 42, 161, 276, 0, 65, 48, 1.65, 0.60},
+{"XANTO_S_3000_R_3B", "XANTO S 3000 R + 3 Batteries",
3000, 25, 42, 161, 276, 0, 65, 48, 1.65, 0.60},
+{"XANTO_S_3000_R_4B", "XANTO S 3000 R + 4 Batteries",
3000, 36, 42, 161, 276, 0, 65, 48, 1.65, 0.60},
+{"XANTO_S_3000_R_5B", "XANTO S 3000 R + 5 Batteries",
3000, 52, 42, 161, 276, 0, 65, 48, 1.65, 0.60},
+{ NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0.00, 0.00 }
+};