[Nut-upsuser] CyberPower CP1200AVR/BC1200D problems

doug reynolds mav at wastegate.net
Fri Jul 28 00:36:14 UTC 2006


Rob wrote:
> I'm trying to get more information about the problems with the 
> CP1200AVR  and am connecting to the device through the USB cable.  
> Since I haven't received the working driver yet (someone modified the 
> cpsups driver), I figured I'd start trying to play with some drivers 
> and ran them in debug mode.  Here's the output from the newhidups run:
>
> /lib/nut/newhidups -u root -a Lysander -DDDD
> Network UPS Tools: 0.28 USB communication driver 0.28 - core 0.30 (2.1.0)
>
> debug level is '4'
> Checking device (0764/0005) (001/008)
> - VendorID: 0764
> - ProductID: 0005
> - Manufacturer: unknown
> - Product: unknown
> - Serial Number: unknown
> - Bus: 001
> Trying to match device
> Device matches
> failed to claim USB device, trying 2 more time(s)...
> detaching kernel driver from USB device...
> trying again to claim USB device...
> Unable to get HID descriptor (error sending control message: 
> Connection timed out)
>
> Running the driver seems to cause issues with the 2.6.17 kernel as the 
> /dev/usb/hiddev0 device disappears after the failed attempt to access 
> the UPS.  This doesn't happen with the 2.0.3 version of the driver 
> (but that version also doesn't even recognize the UPS.
>
> The failure to claim the USB device seems troubling to me, unless that 
> error is because the device node is disappearing after the device 
> check (which itself it troubling).
>
> I also tried the cpsups driver, but it complains thusly:
> cpsups -a Lysander -u root -DDDD
> Network UPS Tools -  CyberPower text protocol UPS driver .04 (2.1.0)
> Warning: This is an experimental driver.
> Some features may not function correctly.
>
> debug level is '4'
> tcgetattr(/dev/usb/hiddev0): Invalid argument
>
> genericups with usbtype=22 complains:
> genericups -a Lysander -u root -DDDD
> Network UPS Tools - Generic UPS driver 1.32 (2.1.0)
> debug level is '4'
> UPS type: Gamatronic UPSs with alarm interface
> ioctl TIOCMSET: Invalid argument
>
> So it looks like the newhidups is the most likely to get working.  Is 
> that a fair assessment?  Is the problem with the device node a known 
> problem?  Is there a way to get the dump of the communication traffic 
> going on between the PC and the UPS?  Any ideas on where to go next?
>
> Rob
>
> Rob wrote:
>> Yes, I'd be very interested in getting a copy of your driver which works
>> with the CP1200AVR.   Does your cpsups driver support USB connections or
>> only serial?  Also, which NUT release is it based on/work with (2.0.x or
>> 2.1)?
>>
>> Rob
>>
>>> -------- Original Message --------
>>> Subject: Re: [Nut-upsuser] CyberPower CP1200AVR/BC1200D problems
>>> From: doug reynolds <mav at wastegate.net>
>>> Date: Thu, July 20, 2006 9:07 pm
>>> To: Rob <lists at midsummerdream.org>
>>> Cc: nut-upsuser at lists.alioth.debian.org
>>>
>>> Rob wrote:
>>>> I recently purchased a CyberPower CP1200AVR UPS and am trying to 
>>>> get NUT 2.0.3 or NUT 2.1.0 to work with the newhidups driver, but 
>>>> am having little success.  Before I purchased I did some googling 
>>>> and saw people talking about problems so I'm guessing it may not be 
>>>> supported yet?  I did find some posts about people getting it to 
>>>> (sort of) work with some hacks, but couldn't tell if they ever got 
>>>> submitted to the NUT development team.  I also found some posts 
>>>> about the CyberPower 900AVR/BC900D having issues, and it looks like 
>>>> I am having the same problems as the person trying to use that 
>>>> UPS.  Namely, I see these messages in syslog:
>>>>
>>>> kernel: usb 1-2: usbfs: USBDEVFS_CONTROL failed cmd newhidups rqt 
>>>> 128 rq 6 len 255 ret -110
>>>>
>>>> I don't have much experience in protocol decoding, but I am a 
>>>> software engineer and I'd like to help get this UPS supported by 
>>>> NUT.  Does anyone know the status of the support for the CyberPower 
>>>> CP1200AVR or have anything new to try?
>>> i got mine working fairly well with the cpsups with a couple change 
>>> to the code... email me if you would like a copy of my driver.
oops.  sorry, been busy.. here is the driver mods I came up with..
other than the type definition, I removed the leading \r's from the 
commands sent to the UPS.  seems to work pretty good.. haven't had a 
whole lot of time to test thoroughly..   another gentleman on this list 
mentioned what he did, so I went through the code and this is what I 
came up with..

/* cpsups.c - model specific routines 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
*/

/* This driver started out as the bestups.c driver from 1.5.11 -
 * I've hacked it up every which way to get it to function
 * with a CPS1100AVR. Thanks go to the guys at
 * http://networkupstools.org for creating a very nice toolset.
*/

/* hack version for 1200VA
 * by DJR
*/

#include "cpsups.h"

#define DRV_VERSION ".04"

static void model_set(const char *abbr, const char *rating)
{

        /*
         * Added: Brad Sawatzky <brad+nut at lamorak.phys.virginia.edu> 02Jun04
         * NOTE: I have no idea how to set the runtime parameter... I 
basically
         * scaled up linearly from the 1100 and 500 entries based on the
         * 'voltage'.  The realtime runtime calculated under load looks
         * reasonable.
         */
    if (!strcmp(abbr, "#1500VA    ")) {
            dstate_setinfo("ups.mfr", "%s", "CyberPower");
            dstate_setinfo("ups.model", "CPS1500AVR %s", rating);
        dstate_setinfo("ups.runtime", "%s", "90");
        dstate_setinfo("ups.power.nominal", "%s", "1500");
            return;
    }

// define the type of ups, and match it to the string returned, #BC1200

    if (!strcmp(abbr, "#BC1200    ")) {
        dstate_setinfo("ups.mfr", "%s", "CyberPower");
        dstate_setinfo("ups.model", "CPS1200VA %s", rating);
        dstate_setinfo("ups.runtime", "%s", "70");
        dstate_setinfo("ups.power.nominal", "%s", "1200");
        return;
    }

    if (!strcmp(abbr, "#1100VA    ")) {
            dstate_setinfo("ups.mfr", "%s", "CyberPower");
            dstate_setinfo("ups.model", "CPS1100VA %s", rating);
        dstate_setinfo("ups.runtime", "%s", "60");
        dstate_setinfo("ups.power.nominal", "%s", "1100");
            return;
    }

    /* Added: Armin Diehl <diehl at ...> 14Dec04 */
    if (!strcmp(abbr, "#1000VA    ")) {
        dstate_setinfo("ups.mfr", "%s", "MicroDowell");
        dstate_setinfo("ups.model", "B.Box BP 1000 %s", rating);
        dstate_setinfo("ups.runtime", "%s", "50");
        dstate_setinfo("ups.voltage", "%s", "1000");
        return;
    }
           
    if (!strcmp(abbr, "#825VA     ")) {
        dstate_setinfo("ups.mfr", "%s", "CyberPower");
        dstate_setinfo("ups.model", "CPS825VA %s", rating);
        dstate_setinfo("ups.runtime", "%s", "29");
        dstate_setinfo("ups.power.nominal", "%s", "825");
        return;
    }

    /* Added: Armin Diehl <diehl at ...> 14Dec04 */
    if (!strcmp(abbr, "#750VA     ")) {
        dstate_setinfo("ups.mfr", "%s", "MicroDowell");
        dstate_setinfo("ups.model", "B.Box BP 750 %s", rating);
        dstate_setinfo("ups.runtime", "%s", "29");
        dstate_setinfo("ups.voltage", "%s", "825");
        return;
    }

    if (!strcmp(abbr, "#500VA     ")) {
                dstate_setinfo("ups.mfr", "%s", "CyberPower");
                dstate_setinfo("ups.model", "OP500TE %s", rating);
                dstate_setinfo("ups.runtime", "%s", "16.5");
                dstate_setinfo("ups.power.nominal", "%s", "500");
        return;
    }

    dstate_setinfo("ups.mfr", "%s", "Unknown");
    dstate_setinfo("ups.model", "Unknown %s (%s)", abbr, rating);
    dstate_setinfo("ups.runtime", "%s", "1");
    dstate_setinfo("ups.power.nominal", "%s", "1");

    printf("Unknown model detected - please report this ID: '%s'\n", abbr);
}

static int instcmd(const char *cmdname, const char *extra)
{
    /* The following commands also appear to be valid on the CPS1100 */

    if (!strcasecmp(cmdname, "test.battery.stop")) {
        ser_send_pace(upsfd, UPSDELAY, "CT\r");
        return STAT_INSTCMD_HANDLED;
    }

    if (!strcasecmp(cmdname, "test.battery.start")) {
        ser_send_pace(upsfd, UPSDELAY, "T\r");
        return STAT_INSTCMD_HANDLED;
    }

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


static int get_ident(char *buf, size_t bufsize)
{
    int    i, ret;

    for (i = 0; i < MAXTRIES; i++) {
        ser_send_pace(upsfd, UPSDELAY, "P4\r");

        ret = ser_get_line(upsfd, buf, bufsize, ENDCHAR, "",
            SER_WAIT_SEC, SER_WAIT_USEC);

        if (ret > 0)
            upsdebugx(2, "get_ident: got [%s]", buf);

        /* buf must start with # and be in the range [25-27] */
        if ((ret > 0) && (buf[0] == '#') && (strlen(buf) >= 25) &&
            (strlen(buf) <= 50))
            return 1;

        sleep(1);
    }

    upslogx(LOG_INFO, "Giving up on hardware detection after %d tries",
        MAXTRIES);

    return 0;
}

static int scan_poll_values(char *buf)
{
    char values[20][200], *pos;
    int i = 0, battremain, length, rseconds;
    double rminutes;

/*    These are used to hold status of UPS.
 *    val1 = online/onbattery status
 */
     char temp1=values[6][0];
    char *tmp1=&temp1;

    while ((pollstatusmap[i].end != 0))
    {
        pos = &buf[pollstatusmap[i].begin];
        length = pollstatusmap[i].end + 1 - pollstatusmap[i].begin;
        strncpy(values[i],pos,length);
        i++;
    }

    if ((*tmp1 & CPS_STAT_OL) && !(*tmp1 & CPS_STAT_OB))
        status_set("OL");

    if (*tmp1 & CPS_STAT_OB)
        status_set("OB");

    if (*tmp1 & CPS_STAT_CAL)
        status_set("CAL");

    if (*tmp1 & CPS_STAT_LB)
        status_set("LB");

    if (*tmp1 == 0)
        status_set("OFF");


    pos = values[3];
        battremain = strtol(dstate_getinfo("ups.power.nominal"),NULL,10) 
* (strtod(pos,NULL)/100);

    /* Figure out runtime minutes */
    rminutes = strtod(dstate_getinfo("ups.runtime"),NULL) *
        ((battremain * ( 1 - (strtod (values[2],NULL) / 100 ))) /
         strtol(dstate_getinfo("ups.power.nominal"),NULL,10));
    rseconds = ((int)(rminutes*100) - ((int)rminutes)*100) * 0.6 ;

        dstate_setinfo("input.voltage", "%g", strtod(values[0],NULL));
        dstate_setinfo("output.voltage", "%g", strtod(values[1],NULL));
        dstate_setinfo("ups.load", "%li", strtol(values[2],NULL,10));
        dstate_setinfo("input.frequency", "%g", strtod(values[5],NULL));
        dstate_setinfo("ups.temperature", "%li", strtol(values[4],NULL,10));
    dstate_setinfo("battery.charge", "%02.1f", strtod(values[3],NULL));
    dstate_setinfo("battery.runtime", "%2.0f:%02d", rminutes, rseconds);

    status_commit();
    dstate_dataok();
    return 0;
}

static void ups_ident(void)
{
    char    buf[256], *ptr, *com, *model, *rating;
    int    i;

    if (!get_ident(buf, sizeof(buf)))
        fatalx("Unable to detect a CyberPower text protocol UPS");

    model = rating = NULL;

    ptr = buf;

    /* Leaving this in place for future */
    for (i = 0; i < 2; i++) {
        com = strchr(ptr, ',');

        if (com)
            *com = '\0';

        switch (i) {
            case 0: model = ptr;
                break;
            case 1: rating = ptr;
                break;
            default:
                break;
        }

        if (com)
            ptr = com + 1;
    }

    if (!model)
        fatalx("Didn't get a valid ident string");

    model_set(model, rating);
}

static void ups_sync(void)
{
    char    buf[256];
    int    i, ret;

    for (i = 0; i < MAXTRIES; i++) {
        ser_send_pace(upsfd, UPSDELAY, "P4\r");
        upsdebugx(3, "ups_sync: send [%s]", "\P4\\r");

        ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, "",
            SER_WAIT_SEC, SER_WAIT_USEC);
        upsdebugx(3, "ups_sync: got ret %d [%s]", ret, buf);

        /* return once we get something that looks usable */
        if ((ret > 0) && (buf[0] == '#')) {
            upsdebugx(3, "ups_sync: got line beginning with #, looks 
usable, returning");
            return;
        }

        usleep(250000);
    }

    fatalx("Unable to detect a CyberPower text protocol UPS");
}

void upsdrv_initinfo(void)
{
    int ret;
    char temp[256];

    ups_sync();
    ups_ident();

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

    /* paranoia - cancel any shutdown that might already be running */
    ser_send_pace(upsfd, UPSDELAY, "C\r"); /* Need a readback so the 
first poll doesn't fail */
    ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, "", 
SER_WAIT_SEC, SER_WAIT_USEC);

    upsh.instcmd = instcmd;

    dstate_setinfo("driver.version.internal", "%s", DRV_VERSION);
    dstate_addcmd("test.battery.start");
    dstate_addcmd("test.battery.stop");
}

static int ups_on_line(void)
{
    int    i, ret;
    char    temp[256];

    for (i = 0; i < MAXTRIES; i++) {
        ser_send_pace(upsfd, UPSDELAY, "D\r");

        ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, "",
            SER_WAIT_SEC, SER_WAIT_USEC);

        /* D must return 34 bytes starting with a # */
        if ((ret > 0) && (temp[0] == '#') && (strlen(temp) == 34)) {

            char * pos = &temp[pollstatusmap[POLL_UPSSTATUS].begin];

            if ((*pos & CPS_STAT_OL) && !(*pos & CPS_STAT_OB))
                return(1);    /* on line */

            return 0;    /* on battery */
        }

        sleep(1);
    }

    upslogx(LOG_ERR, "Status read failed: assuming on battery");

    return 0;    /* on battery */
}

void upsdrv_shutdown(void)
{
    int ret;
    char    buf[256];

    ups_sync();

    printf("The UPS will shut down in approximately one minute.\n");

    if (ups_on_line())
        printf("The UPS will restart in about one minute.\n");
    else
        printf("The UPS will restart when power returns.\n");

    /* Although this is straight from the bestups.c driver, the UPS
     * does indeed shutdown correctly. */

    ser_send_pace(upsfd, UPSDELAY, "S01R0001\r");

    ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, "",
                       SER_WAIT_SEC, SER_WAIT_USEC);

    if ((ret < 1) || (buf[0] != '#'))
        printf ("Warning: got unexpected reply to shutdown command, 
shutdown may fail\n");
}

void upsdrv_updateinfo(void)
{
    char    buf[256];
    int    ret;

    ret = ser_send_pace(upsfd, UPSDELAY, "D\r");

    if (ret < 1) {
        ser_comm_fail("ser_send_pace failed");
        dstate_datastale();
        return;
    }

    /* these things need a long time to respond completely */
    usleep(200000);

    ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, "",
        SER_WAIT_SEC, SER_WAIT_USEC);

    if (ret < 1) {
        ser_comm_fail(NULL);
        dstate_datastale();
        return;
    }

    if (ret < 34) {
        if (ret == 2)         /* We need to retry this read right away    */
        {
            ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, "", 
SER_WAIT_SEC, SER_WAIT_USEC);
            if (ret < 34) {
                ser_comm_fail("Poll failed: short read (got %d bytes)", 
ret);
                dstate_datastale();
                return;
            }
        } else {
            ser_comm_fail("Poll failed: short read (got %d bytes)", ret);
            dstate_datastale();
            return;
        }
    }

    if (ret > 34) {
        upslogx(LOG_INFO, "String too long...");
        ser_comm_fail("Poll failed: response too long (got %d bytes)",
            ret);
        dstate_datastale();
        return;
    }

    if (buf[0] != '#') {
        ser_comm_fail("Poll failed: invalid start character (got %02x)",
            buf[0]);
        dstate_datastale();
        return;
    }

    ser_comm_good();

    scan_poll_values(buf);

    status_init();

}

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-upsuser mailing list