1
/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: t; -*-
3
* Copyright (C) 2003, 2004 Carlos Rodrigues <carlos.efr@mail.telepac.pt>
5
* powermust.c created on 4/10/2003
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33
#define DRV_VERSION "1.1"
35
#define RECV_BUFFER_LEN 128
37
/* The expected reply lengths (without IGNCHARS) */
38
#define F_CMD_REPLY_LEN 20
39
#define Q1_CMD_REPLY_LEN 45
41
#define IDENT_MAXTRIES 5
42
#define IDENT_MINSUCCESS 2
44
#define SEND_PACE 50000 /* 50ms interval between chars */
45
#define READ_TIMEOUT 2 /* 2 seconds timeout on read */
47
#define MAX_START_DELAY 9999
48
#define MAX_SHUTDOWN_DELAY 99
50
/* Maximum length of a string representing these values */
51
#define MAX_START_DELAY_LEN 4
52
#define MAX_SHUTDOWN_DELAY_LEN 2
56
/* The UPS status flags */
59
#define FL_BOOST_TRIM 2
62
#define FL_BATT_TEST 5
64
#define FL_BEEPER_ON 7 /* seemingly not used */
67
* Battery voltage limits
69
* These are hardcoded values for the PowerMust 600VA Plus (12V) and the
70
* 1000VA Plus (24V). If you have another model and these values should
71
* be different, let me know.
73
#define BATT_VOLT_MIN_12 9.7 /* Estimate by looking at Commander Pro */
74
#define BATT_VOLT_MAX_12 13.7
75
#define BATT_VOLT_MIN_24 19.4 /* Estimate from LB at 22.2V (using same factor as 12V models) */
76
#define BATT_VOLT_MAX_24 27.4
79
/* The values returned by the UPS for a "F" query */
88
/* The values returned by the UPS for a "Q1" query */
97
char flags[N_FLAGS + 1];
101
/* Defined in upsdrv_initups */
102
static float battvolt_min;
103
static float battvolt_max;
105
/* Minimum and maximum voltage seen on input */
106
static float ivolt_min = INT_MAX;
107
static float ivolt_max = -1;
110
static short start_delay = 3; /* wait this amount of time to come back online */
111
static short shutdown_delay = 2; /* wait until going offline */
114
static float batt_charge_pct(float battvolt);
115
static int check_ups(void);
116
static int get_firmware_values(FirmwareValues *values);
117
static int run_query(QueryValues *values);
118
int instcmd(const char *cmdname, const char *extra);
119
int setvar(const char *varname, const char *val);
122
/* I know, macros should evaluate their arguments only once */
123
#define CLAMP(x, min, max) (((x) < (min)) ? (min) : (((x) > (max)) ? (max) : (x)))
126
static float batt_charge_pct(float battvolt)
130
battvolt = CLAMP(battvolt, battvolt_min, battvolt_max);
131
value = (battvolt - battvolt_min) / (battvolt_max - battvolt_min);
137
static int check_ups(void)
139
char buffer[RECV_BUFFER_LEN];
142
ser_send_pace(upsfd, SEND_PACE, "F%c", ENDCHAR);
143
ret = ser_get_line(upsfd, buffer, RECV_BUFFER_LEN, ENDCHAR, IGNCHARS, READ_TIMEOUT, 0);
144
if (ret < F_CMD_REPLY_LEN) {
148
ser_send_pace(upsfd, SEND_PACE, "Q1%c", ENDCHAR);
149
ret = ser_get_line(upsfd, buffer, RECV_BUFFER_LEN, ENDCHAR, IGNCHARS, READ_TIMEOUT, 0);
150
if (ret < Q1_CMD_REPLY_LEN) {
158
static int get_firmware_values(FirmwareValues *values)
160
char buffer[RECV_BUFFER_LEN];
163
ser_send_pace(upsfd, SEND_PACE, "F%c", ENDCHAR);
164
ret = ser_get_line(upsfd, buffer, RECV_BUFFER_LEN, ENDCHAR, IGNCHARS, READ_TIMEOUT, 0);
165
if (ret < F_CMD_REPLY_LEN) {
169
sscanf(buffer, "%f %f %f %f", &values->volt, &values->current,
170
&values->battvolt, &values->freq);
176
static int run_query(QueryValues *values)
178
char buffer[RECV_BUFFER_LEN];
181
ser_send_pace(upsfd, SEND_PACE, "Q1%c", ENDCHAR);
182
ret = ser_get_line(upsfd, buffer, RECV_BUFFER_LEN, ENDCHAR, IGNCHARS, READ_TIMEOUT, 0);
183
if (ret < Q1_CMD_REPLY_LEN) {
187
sscanf(buffer, "%f %f %f %f %f %f %f %s", &values->ivolt, &values->fvolt, &values->ovolt,
188
&values->load, &values->freq, &values->battvolt, &values->temp, values->flags);
194
void upsdrv_initinfo(void)
198
FirmwareValues values;
200
/* try to detect the UPS */
201
for (i = 0; i < IDENT_MAXTRIES; i++) {
202
if (check_ups() == 0) {
207
if (success < IDENT_MINSUCCESS) {
208
fatalx("Mustek PowerMust UPS, or compatible, not detected.");
210
upslogx(LOG_INFO, "Mustek PowerMust UPS, or compatible, detected.");
212
dstate_setinfo("driver.version.internal", "%s", DRV_VERSION);
213
dstate_setinfo("ups.mfr", "Mustek");
214
dstate_setinfo("ups.model", "PowerMust");
215
dstate_setinfo("ups.serial", "unknown");
217
if (get_firmware_values(&values) < 0) {
218
fatalx("Error reading firmware values from UPS!");
221
if (values.battvolt == 12) {
222
battvolt_min = BATT_VOLT_MIN_12;
223
battvolt_max = BATT_VOLT_MAX_12;
224
} else { /* 24V battery */
225
battvolt_min = BATT_VOLT_MIN_24;
226
battvolt_max = BATT_VOLT_MAX_24;
229
dstate_setinfo("output.voltage.target.battery", "%.1f", values.volt);
230
dstate_setinfo("battery.voltage.nominal", "%.1f", values.battvolt);
232
dstate_setinfo("ups.delay.start", "%d", start_delay);
233
dstate_setflags("ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING);
234
dstate_setaux("ups.delay.start", MAX_START_DELAY_LEN);
236
dstate_setinfo("ups.delay.shutdown", "%d", shutdown_delay);
237
dstate_setflags("ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING);
238
dstate_setaux("ups.delay.shutdown", MAX_SHUTDOWN_DELAY_LEN);
240
dstate_addcmd("test.battery.start");
241
dstate_addcmd("shutdown.return");
242
dstate_addcmd("shutdown.stayoff");
243
dstate_addcmd("shutdown.stop");
244
dstate_addcmd("load.on");
245
dstate_addcmd("load.off");
246
dstate_addcmd("reset.input.minmax");
248
upsh.instcmd = instcmd;
249
upsh.setvar = setvar;
251
/* clean up a possible shutdown in progress */
252
ser_send_pace(upsfd, SEND_PACE, "C%c", ENDCHAR);
256
void upsdrv_updateinfo(void)
260
if (run_query(&query) < 0) {
262
* Query wasn't successful (we got some weird
263
* response), however we won't fatalx() as this
264
* happens sometimes when the ups is offline.
266
* Some fault tolerance is good, we just assume
267
* that the UPS is just taking a nap. ;)
274
dstate_setinfo("input.voltage", "%.1f", query.ivolt);
275
dstate_setinfo("input.voltage.fault", "%.1f", query.fvolt);
276
dstate_setinfo("output.voltage", "%.1f", query.ovolt);
277
dstate_setinfo("ups.load", "%.1f", query.load);
278
dstate_setinfo("output.frequency", "%.1f", query.freq);
279
dstate_setinfo("battery.voltage", "%.1f", query.battvolt);
281
/* this value seems to be bogus, it always reports 37.8 */
282
/*dstate_setinfo("ups.temperature", "%.1f", query.temp);*/
284
dstate_setinfo("battery.charge", "%.1f", batt_charge_pct(query.battvolt));
286
/* For debug purposes (I know it isn't good to create new variables) */
287
/*dstate_setinfo("ups.flags", query.flags);*/
291
if (query.flags[FL_LOAD_OFF] == '1') {
293
} else if (query.flags[FL_ON_BATT] == '1' || query.flags[FL_BATT_TEST] == '1') {
298
if (query.flags[FL_BOOST_TRIM] == '1') {
299
if (query.ivolt < query.ovolt) {
301
} else if (query.ivolt > query.ovolt) {
304
status_set("BYPASS");
309
if (query.flags[FL_LOW_BATT] == '1') {
313
if (query.flags[FL_FAILED] == '1') {
314
status_set("FAILED");
319
/* Update minimum and maximum input voltage levels only when on line */
320
if (query.flags[FL_ON_BATT] == '0') {
321
if (query.ivolt < ivolt_min) {
322
ivolt_min = query.ivolt;
325
if (query.ivolt > ivolt_max) {
326
ivolt_max = query.ivolt;
329
dstate_setinfo("input.voltage.minimum", "%.1f", ivolt_min);
330
dstate_setinfo("input.voltage.maximum", "%.1f", ivolt_max);
337
void upsdrv_shutdown(void)
339
upslogx(LOG_INFO, "Shutting down UPS immediately.");
341
ser_send_pace(upsfd, SEND_PACE, "C%c", ENDCHAR);
342
ser_send_pace(upsfd, SEND_PACE, "S00R0001%c", ENDCHAR);
346
int instcmd(const char *cmdname, const char *extra)
348
if (strcasecmp(cmdname, "test.battery.start") == 0) {
349
ser_send_pace(upsfd, SEND_PACE, "C%c", ENDCHAR);
350
ser_send_pace(upsfd, SEND_PACE, "T%c", ENDCHAR);
352
upslogx(LOG_INFO, "Start battery test for 10 seconds.");
354
return STAT_INSTCMD_HANDLED;
357
if (strcasecmp(cmdname, "shutdown.return") == 0) {
358
ser_send_pace(upsfd, SEND_PACE, "C%c", ENDCHAR);
359
ser_send_pace(upsfd, SEND_PACE, "S%02dR%04d%c", shutdown_delay, start_delay, ENDCHAR);
361
upslogx(LOG_INFO, "Shutdown (return) initiated.");
363
return STAT_INSTCMD_HANDLED;
366
if (strcasecmp(cmdname, "shutdown.stayoff") == 0) {
367
ser_send_pace(upsfd, SEND_PACE, "C%c", ENDCHAR);
368
ser_send_pace(upsfd, SEND_PACE, "S%02dR0000%c", shutdown_delay, ENDCHAR);
370
upslogx(LOG_INFO, "Shutdown (stayoff) initiated.");
372
return STAT_INSTCMD_HANDLED;
375
if (strcasecmp(cmdname, "shutdown.stop") == 0) {
376
ser_send_pace(upsfd, SEND_PACE, "C%c", ENDCHAR);
378
upslogx(LOG_INFO, "Shutdown canceled.");
380
return STAT_INSTCMD_HANDLED;
383
if (strcasecmp(cmdname, "load.on") == 0) {
384
ser_send_pace(upsfd, SEND_PACE, "C%c", ENDCHAR);
386
upslogx(LOG_INFO, "Turning load on.");
388
return STAT_INSTCMD_HANDLED;
391
if (strcasecmp(cmdname, "load.off") == 0) {
392
ser_send_pace(upsfd, SEND_PACE, "C%c", ENDCHAR);
393
ser_send_pace(upsfd, SEND_PACE, "S00R0000%c", ENDCHAR);
395
upslogx(LOG_INFO, "Turning load off.");
397
return STAT_INSTCMD_HANDLED;
400
if (strcasecmp(cmdname, "reset.input.minmax") == 0) {
404
dstate_setinfo("input.voltage.minimum", "%.1f", ivolt_min);
405
dstate_setinfo("input.voltage.maximum", "%.1f", ivolt_max);
407
upslogx(LOG_INFO, "Resetting minimum and maximum input voltage values.");
409
return STAT_INSTCMD_HANDLED;
412
upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname);
414
return STAT_INSTCMD_UNKNOWN;
418
int setvar(const char *varname, const char *val)
422
if (sscanf(val, "%d", &delay) != 1) {
423
return STAT_SET_UNKNOWN;
426
if (strcasecmp(varname, "ups.delay.start") == 0) {
427
delay = CLAMP(delay, 0, MAX_START_DELAY);
429
dstate_setinfo( "ups.delay.start", "%d", delay);
433
return STAT_SET_HANDLED;
436
if (strcasecmp(varname, "ups.delay.shutdown") == 0) {
437
delay = CLAMP(delay, 0, MAX_SHUTDOWN_DELAY);
438
shutdown_delay = delay;
439
dstate_setinfo( "ups.delay.shutdown", "%d", delay);
443
return STAT_SET_HANDLED;
446
return STAT_SET_UNKNOWN;
450
void upsdrv_help(void)
455
void upsdrv_makevartable(void)
460
void upsdrv_banner(void)
462
printf("Network UPS Tools - Mustek PowerMust UPS driver %s (%s)\n", DRV_VERSION, UPS_VERSION);
463
printf("Carlos Rodrigues (c) 2003, 2004\n\n");
467
void upsdrv_initups(void)
469
upsfd = ser_open(device_path);
470
ser_set_speed(upsfd, device_path, B2400);
474
void upsdrv_cleanup(void)
476
ser_close(upsfd, device_path);
480
/* EOF - powermust.c */