[Nut-upsdev] Fwd: RE New xanto driver for NUT

Andreas Thienemann andreas at bawue.net
Fri May 12 14:28:03 UTC 2006


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 at bawue.net>
+
+   based on sms.c:
+
+   Copyright (C) 2001  Marcio Gomes  <tecnica at 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 at 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 at exploits.org>,
+   Fenton UPS Driver
+
+   Copyright (C) 2005  Andreas Thienemann  <andreas at 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 }
+};


More information about the Nut-upsdev mailing list