[Nut-upsdev] Cpsups driver with a CyberPower OP1000E

Doug Reynolds mav at wastegate.net
Tue Jan 2 23:45:45 CET 2007


Marcel Ziswiler wrote:
>> please, if you can, download the newest trunk and try the new 
>> 'powerpanel' driver.  the last cpsups driver submitted is/was slightly 
>> broken.  the new powerpanel driver is the new, working one.  I am 
>> building it now myself.
>>
>> If you are still having trouble, I can send you the last good update to 
>> cpsups.c cpsups.h
>>     
>
> Sorry, still no luck:
> # drivers/powerpanel -a myups -DDD
> Network UPS Tools -  CyberPower text protocol UPS driver 0.10 (2.1.0)
> Warning: This is an experimental driver.
> Some features may not function correctly.
>
> debug level is '3'
> command:
> reply  : <none>
> command:
> reply  : <none>
> command:
> reply  : <none>
> command:
> reply  : <none>
> command:
> reply  : <none>
> Unable to detect a CyberPower text protocol UPS
>
> Can you please send me the latest cpsups one you have. I am wondering whether it has something todo with me using a serial to USB converter, but then that stuff should all be standard by now.
>
>   
it is rumored that the serial<->usb converters sometimes work, and 
sometimes don't... i've attached these the last cpsups drivers, give 
them a try.  is it possible to use a direct serial cable?

-------------- next part --------------
/* cpsups.h - Lookup tables for CyberPower text protocol UPSes

   Copyright (C) 2003  Walt Holman <waltabbyh at comcast.net>
   with thanks to Russell Kroll <rkroll at exploits.org>

   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
*/

/*
 * Offsets within the buffer that is returned after a "D\r" command
 */
#define POLL_INPUTVOLT	2	/* input voltage */
#define POLL_OUTPUTVOLT	8	/* output voltage */
#define	POLL_LOAD	14	/* load percentage */
#define	POLL_BATTCHARGE	18	/* battery charge */
#define POLL_TEMP	22	/* temperature */
#define	POLL_FREQUENCY	26	/* frequency */
#define	POLL_STATUS	32	/* status byte */

#define ENDCHAR		'\r'	/* replies end with CR */
#define MAXTRIES	5
#define UPSDELAY	50000	/* 50 ms delay required for reliable operation */

#define SER_WAIT_SEC	3	/* allow 3 sec for ser_get calls */
#define SER_WAIT_USEC	0

#define CPS_STAT_CAL	0x08
#define CPS_STAT_LB	0x20
#define CPS_STAT_OB	0x40
#define CPS_STAT_OL	0x90
-------------- next part --------------
/* cpsups.c - model specific routines for CyberPower text protocol UPSes 

   Copyright (C) 2007  Arjen de Korte <arjen at de-korte.org>
                       Doug Reynolds <mav at wastegate.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
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/ioctl.h>

#include "main.h"
#include "serial.h"
#include "cpsups.h"

#define DRV_VERSION "0.08"

static char cpsups_reply[SMALLBUF];

static int cpsups_command(const char *command)
{
	int 	dtr_bit = TIOCM_DTR;
	int 	ret = -1;

	upsdebugx(3, "command: %s", command);

	ioctl(upsfd, TIOCMBIS, &dtr_bit);
	tcflush(upsfd, TCIOFLUSH);

	if (ser_send_pace(upsfd, UPSDELAY, command) == strlen(command))
	{
		ret = ser_get_line(upsfd, cpsups_reply, sizeof(cpsups_reply), ENDCHAR, "",
			SER_WAIT_SEC, SER_WAIT_USEC);
	}

	ioctl(upsfd, TIOCMBIC, &dtr_bit);

	if (ret > 0)
		upsdebug_hex(3, "reply  ", (unsigned char *)cpsups_reply, ret);
	else
		upsdebugx(3, "reply  : <none>");

	if (cpsups_reply[0] != '#')
		return 0;

	return ret;
}

static int instcmd(const char *cmdname, const char *extra)
{
	int 	ret = -1;

	if (!strcasecmp(cmdname, "test.battery.start"))
		ret = cpsups_command("T\r");

	if (!strcasecmp(cmdname, "test.battery.stop"))
		ret = cpsups_command("CT\r");

	if (!strcasecmp(cmdname, "beeper.on"))
		ret = cpsups_command("C7:1\r");

	if (!strcasecmp(cmdname, "beeper.off"))
		ret = cpsups_command("C7:0\r");

	if (ret > 0)
		return STAT_INSTCMD_HANDLED;

	upslogx(LOG_NOTICE, "instcmd: command [%s] failed", cmdname);
	return STAT_INSTCMD_UNKNOWN;
}

static int setvar(const char *varname, const char *val)
{
	char	command[SMALLBUF];

	if (!strcasecmp(varname, "input.transfer.high"))
	{
		snprintf(command, sizeof(command), "C2:%s\r", val);

		if ((cpsups_command(command) > 0) && !strcasecmp(cpsups_reply, "#0"))
		{
			dstate_setinfo("input.transfer.high", val);
			return STAT_SET_HANDLED;
		}
	}

	if (!strcasecmp(varname, "input.transfer.low"))
	{
		snprintf(command, sizeof(command), "C3:%s\r", val);

		if ((cpsups_command(command) > 0) && !strcasecmp(cpsups_reply, "#0"))
		{
			dstate_setinfo("input.transfer.low", val);
			return STAT_SET_HANDLED;
		}
	}

	if (!strcasecmp(varname, "battery.charge.low"))
	{
		snprintf(command, sizeof(command), "C4:%s\r", val);

		if ((cpsups_command(command) > 0) && !strcasecmp(cpsups_reply, "#0"))
		{
			dstate_setinfo("battery.charge.low", val);
			return STAT_SET_HANDLED;
		}
	}

	upslogx(LOG_NOTICE, "setvar: setting variable [%s] to [%s] failed", varname, val);
	return STAT_SET_UNKNOWN;
}

static int get_ident(void)
{
	char	*s;
	int	retry = MAXTRIES;

	while (retry--)
	{
		if ((cpsups_command("\r") > 0) && (cpsups_reply[1] == '2') && (cpsups_command("P4\r") > 0))
		{
			if ((s = strtok(cpsups_reply+1, ",")) != NULL)
				dstate_setinfo("ups.model", s);
			if ((s = strtok(NULL, ",")) != NULL)
				dstate_setinfo("ups.firmware", s);
			if ((s = strtok(NULL, ",")) != NULL)
				upsdebugx(1, "serial number (?) %s", s);
			if ((s = strtok(NULL, ",")) != NULL)
				dstate_setinfo("ups.mfr", s);

			return 1;
		}
 	}

	return 0;
}

static int get_settings(void)
{
	char	*s;
	int	ret = 0;

	/*
	 * WRITE P3\r
	 * READ #12.0,002,008.0,00\r
	 */
	if (cpsups_command("P3\r") > 0)
	{
		if ((s = strtok(cpsups_reply+1, ",")) != NULL)
			dstate_setinfo("battery.voltage.nominal", s);
		if ((s = strtok(NULL, ",")) != NULL)
			dstate_setinfo("battery.packs", s);
		if ((s = strtok(NULL, ",")) != NULL)
			dstate_setinfo("battery.capacity.nominal", s);

		ret++;
	}

	/*
	 * WRITE P2\r
	 * READ #1200,0720,120,47,63\r
	 */
	if (cpsups_command("P2\r") > 0)
	{
		if ((s = strtok(cpsups_reply+1, ",")) != NULL)
			dstate_setinfo("ups.power.nominal", s);
		if ((s = strtok(NULL, ",")) != NULL)
			dstate_setinfo("ups.watts.nominal", s);
		if ((s = strtok(NULL, ",")) != NULL)
			dstate_setinfo("input.voltage.nominal", s);
		if ((s = strtok(NULL, ",")) != NULL)
			dstate_setinfo("input.frequency.low", s);
		if ((s = strtok(NULL, ",")) != NULL)
			dstate_setinfo("input.frequency.high", s);

		ret++;
	}

	/*
	 * WRITE P1\r
	 * READ #120,138,088,20\r
	 */
	if (cpsups_command("P1\r") > 0)
	{
		if ((s = strtok(cpsups_reply+1, ",")) != NULL)
			dstate_setinfo("input.voltage.nominal", s);
		if ((s = strtok(NULL, ",")) != NULL)
			dstate_setinfo("input.transfer.high", s);
		if ((s = strtok(NULL, ",")) != NULL)
			dstate_setinfo("input.transfer.low", s);
		if ((s = strtok(NULL, ",")) != NULL)
			dstate_setinfo("battery.charge.low", s);

		ret++;
	}

	/*
	 * WRITE P6\r
	 * READ #130,131,132,133,134,135,136,137,138,139,140\r
	 */
	if (dstate_getinfo("input.transfer.high") && (cpsups_command("P6\r") > 0))
	{
		if ((s = strtok(cpsups_reply+1, ",")) != NULL)
		{
			dstate_addenum("input.transfer.high", s);
			dstate_setflags("input.transfer.high", ST_FLAG_STRING | ST_FLAG_RW);
			dstate_setaux("input.transfer.high", 3);
		}
		while ((s = strtok(NULL, ",")) != NULL)
			dstate_addenum("input.transfer.high", s);

		ret++;
	}

	/*
	 * WRITE P7\r
	 * READ #080,081,082,083,084,085,086,087,088,089,090\r
	 */
	if (dstate_getinfo("input.transfer.low") && (cpsups_command("P7\r") > 0))
	{
		if ((s = strtok(cpsups_reply+1, ",")) != NULL)
		{
			dstate_addenum("input.transfer.low", s);
			dstate_setflags("input.transfer.low", ST_FLAG_STRING | ST_FLAG_RW);
			dstate_setaux("input.transfer.low", 3);
		}
		while ((s = strtok(NULL, ",")) != NULL)
			dstate_addenum("input.transfer.low", s);

		ret++;
	}

	/*
	 * WRITE P8\r\
	 * READ #20,25,30,35,40,45,50,55,60,65\r
	 */
	if (dstate_getinfo("battery.charge.low") && (cpsups_command("P8\r") > 0))
	{
		if ((s = strtok(cpsups_reply+1, ",")) != NULL)
		{
			dstate_addenum("battery.charge.low", s);
			dstate_setflags("battery.charge.low", ST_FLAG_STRING | ST_FLAG_RW);
			dstate_setaux("battery.charge.low", 2);
		}
		while ((s = strtok(NULL, ",")) != NULL)
			dstate_addenum("battery.charge.low", s);

		ret++;
	}

	return ret;
}

void upsdrv_initinfo(void)
{
	char	*s;

	dstate_setinfo("driver.version.internal", "%s", DRV_VERSION);

	if (get_ident() == 0)
		fatalx("Unable to detect a CyberPower text protocol UPS");

	printf("Detected %s %s on %s\n", dstate_getinfo("ups.mfr"),
		dstate_getinfo("ups.model"), device_path);

	if (get_settings() == 0)
		upslogx(LOG_WARNING, "Can't read settings from CyberPower text protocol UPS");

	/* paranoia - cancel any shutdown that might already be running */

	cpsups_command("D\r");
	cpsups_command("C\r");

	/*
	 * allow to override the following parameters
	 */
	if ((s = getval("manufacturer")) != NULL)
		dstate_setinfo("ups.mfr", s);
	if ((s = getval("model")) != NULL)
		dstate_setinfo("ups.model", s);
	if ((s = getval("serial")) != NULL)
		dstate_setinfo("ups.serial", s);

	dstate_addcmd("test.battery.start");
	dstate_addcmd("test.battery.stop");
	dstate_addcmd("beeper.on");
	dstate_addcmd("beeper.off");
	/* dstate_addcmd("shutdown.return"); */
	/* dstate_addcmd("shutdown.reboot"); */

	upsh.instcmd = instcmd;
	upsh.setvar = setvar;
}

void upsdrv_shutdown(void)
{
	int	retry = MAXTRIES;

	if (get_ident() == 0)
		fatalx("Unable to detect a CyberPower text protocol UPS");

	/*
	 * Don't abort on the first try
	 */
	while (retry--)
	{
		if (cpsups_command("Z02\r") > 0)
		{
			upslogx(LOG_INFO, "Shutdown in progress");
			return;
		}
	}

	upslogx(LOG_ERR, "Shutdown command returned with an error!");
}

void upsdrv_updateinfo(void)
{
	/*
	 * The "D\r" command should return exactly 34 characters
	 */
	if (cpsups_command("D\r") != 34)
	{
		ser_comm_fail("Status read failed");
		dstate_datastale();
	}

	status_init();

	if ((cpsups_reply[POLL_STATUS] & CPS_STAT_OL) && !(cpsups_reply[POLL_STATUS] & CPS_STAT_OB))
		status_set("OL");

	if (cpsups_reply[POLL_STATUS] & CPS_STAT_OB) 
		status_set("OB");

	if (cpsups_reply[POLL_STATUS] & CPS_STAT_LB) 
		status_set("LB");

	if (cpsups_reply[POLL_STATUS] & CPS_STAT_CAL)
		status_set("CAL");

	if (cpsups_reply[POLL_STATUS]  == 0)
		status_set("OFF");

	status_commit();

        dstate_setinfo("input.voltage", "%g", strtod(&cpsups_reply[POLL_INPUTVOLT], NULL));
        dstate_setinfo("output.voltage", "%g", strtod(&cpsups_reply[POLL_OUTPUTVOLT], NULL));
        dstate_setinfo("ups.load", "%li", strtol(&cpsups_reply[POLL_LOAD], NULL, 10));
        dstate_setinfo("input.frequency", "%g", strtod(&cpsups_reply[POLL_FREQUENCY], NULL));
        dstate_setinfo("ups.temperature", "%li", strtol(&cpsups_reply[POLL_TEMP], NULL,10));
	dstate_setinfo("battery.charge", "%02.1f", strtod(&cpsups_reply[POLL_BATTCHARGE], NULL));

	ser_comm_good();
	dstate_dataok();

	return;
}

void upsdrv_help(void)
{
}

void upsdrv_makevartable(void)
{
}

void upsdrv_banner(void)
{
	printf("Network UPS Tools -  CyberPower text protocol UPS driver %s (%s)\n",
		DRV_VERSION, UPS_VERSION);
	experimental_driver = 1;	/* Causes a warning message to be printed */
}

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);
}


More information about the Nut-upsdev mailing list