4 * A SmartUPS serial protocol simulator.
6 * This is a basic SmartUPS protocol simulator. It answers
7 * queries from the host (i.e., apcuspd) and allows you to toggle
8 * flags such as onbattery and adjust readouts such as timeleft.
9 * This can be very useful for exercising apcupsd with a series
10 * of events that would otherwise require draining your UPS battery
13 * Point smartsim at a serial port (/dev/ttyS0 is the default) and
14 * connect that port via a null modem cable to another serial port
15 * on which you are running apcupsd configured for the apcsmart
18 * smartsim responds to the Smart protocol commands listed in the
19 * upscmds[] table. More protocol commands can easily be added.
21 * Keyboard commands consisting of a single keypress cause smartsim
22 * to toggle flags and readouts. These commands can be found in the
23 * keycmds[] table. Supported keypress commands:
32 * r ReplaceBatt toggle
35 * 7/4 BattPct (inc/dec) (use numeric keypad)
36 * 8/5 LoadPct (inc/dec) (use numeric keypad)
37 * 9/6 TimeLeft (inc/dec) (use numeric keypad)
39 * Some keypress commands produce procotol events. For example,
40 * toggling on/off battery changes the status variable and also
41 * issues '!' and '$' "interrupt" events.
45 * Copyright (C) 2005 Adam Kropelin
47 * This program is free software; you can redistribute it and/or
48 * modify it under the terms of version 2 of the GNU General
49 * Public License as published by the Free Software Foundation.
51 * This program is distributed in the hope that it will be useful,
52 * but WITHOUT ANY WARRANTY; without even the implied warranty of
53 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
54 * General Public License for more details.
56 * You should have received a copy of the GNU General Public
57 * License along with this program; if not, write to the Free
58 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
62 #include <sys/types.h>
70 #define DEFAULT_LINEF 60.0
71 #define DEFAULT_LOADPCT 25.0
72 #define DEFAULT_BATTPCT 100.0
73 #define DEfAULT_TIMELEFT 20.0
74 #define DEFAULT_BATTV 24.0
75 #define DEFAULT_LINEV 120.0
76 #define DEFAULT_OUTV 120.0
77 #define DEFAULT_REG2 0x00
83 float linefreq
= DEFAULT_LINEF
;
84 float loadpct
= DEFAULT_LOADPCT
;
85 float battpct
= DEFAULT_BATTPCT
;
86 float timeleft
= DEfAULT_TIMELEFT
;
87 float battv
= DEFAULT_BATTV
;
88 float linev
= DEFAULT_LINEV
;
89 float outv
= DEFAULT_OUTV
;
90 unsigned char reg2
= DEFAULT_REG2
;
91 char xfercause
[] = "O";
92 char selftest
[] = "OK";
102 #define dbg(str, args...) \
107 printf(str, ## args); \
113 /* Response callbacks */
114 static void rsp_string(const void* arg
);
115 static void rsp_float(const void* arg
);
116 static void rsp_cmds(const void* arg
);
117 static void rsp_status(const void* arg
);
118 static void rsp_hex(const void *arg
);
120 /* Mapping of UPS commands to response callbacks */
124 void (*func
)(const void*);
128 { 'Y', rsp_string
, "SM" },
129 { '\x01', rsp_string
, "SMART-UPS Simulator" },
130 { 'c', rsp_string
, "SMARTSIM" },
131 { 'F', rsp_float
, &linefreq
},
132 { 'Q', rsp_status
, NULL
},
133 { 'P', rsp_float
, &loadpct
},
134 { 'f', rsp_float
, &battpct
},
135 { 'a', rsp_cmds
, NULL
},
136 { 'j', rsp_float
, &timeleft
},
137 { 'G', rsp_string
, xfercause
},
138 { 'X', rsp_string
, selftest
},
139 { 'B', rsp_float
, &battv
},
140 { 'L', rsp_float
, &linev
},
141 { 'O', rsp_float
, &outv
},
142 { '\'', rsp_hex
, ®2
},
143 { 'V', rsp_string
, "Lzy" },
147 /* The alert characters we support */
148 const char alerts
[] = "!$%+#";
150 static void key_toggle(const void* arg
);
151 static void key_onbatt(const void* arg
);
152 static void key_battlow(const void* arg
);
153 static void key_inc(const void* arg
);
154 static void key_dec(const void* arg
);
155 static void key_selftest(const void* arg
);
156 static void key_rebatt(const void* arg
);
157 static void key_batdet(const void *arg
);
158 static void key_commfail(const void *arg
);
159 static void key_help(const void *arg
);
161 /* Mapping of keyboard commands to callbacks */
165 void (*func
)(const void*);
169 { 'b', key_onbatt
, NULL
},
170 { 'o', key_toggle
, &overload
},
171 { 't', key_toggle
, &trim
},
172 { 's', key_toggle
, &boost
},
173 { 'l', key_battlow
, NULL
},
174 { '7', key_inc
, &battpct
},
175 { '4', key_dec
, &battpct
},
176 { '8', key_inc
, &loadpct
},
177 { '5', key_dec
, &loadpct
},
178 { '9', key_inc
, &timeleft
},
179 { '6', key_dec
, &timeleft
},
180 { 'x', key_selftest
, NULL
},
181 { 'r', key_rebatt
, NULL
},
182 { 'd', key_batdet
, NULL
},
183 { 'c', key_commfail
, NULL
},
184 { '?', key_help
, NULL
},
189 #define DEFAULT_DEVICE "/dev/ttyS0";
191 void wups(const char* str
, int len
)
194 write(fileno(stdout
), str
, len
);
196 write(ups
, str
, len
);
199 void rsp_string(const void* arg
)
201 wups((char*)arg
, strlen((char*)arg
));
205 void rsp_float(const void* arg
)
209 sprintf(buf
, "%03.3f\r\n", *(float*)arg
);
210 wups(buf
, strlen(buf
));
213 void rsp_hex(const void* arg
)
217 sprintf(buf
, "%02x\r\n", *(unsigned char*)arg
);
218 wups(buf
, strlen(buf
));
221 void rsp_cmds(const void* arg
)
225 /* Protocol version */
228 /* Supported alert characters */
229 wups(alerts
, sizeof(alerts
)-1);
232 /* Supported commands */
233 for (x
=0; upscmds
[x
].func
; x
++)
234 wups(&upscmds
[x
].cmd
, 1);
239 void rsp_status(const void* arg
)
243 sprintf(buf
, "%02x\r\n",
252 wups(buf
, strlen(buf
));
255 static void key_toggle(const void* arg
)
257 *(int *)arg
= !*(int *)arg
;
260 static void key_onbatt(const void* arg
)
276 linev
= DEFAULT_LINEV
;
283 static void key_battlow(const void* arg
)
301 static void key_inc(const void* arg
)
304 dbg("%3.3f\n", *(float*)arg
);
307 static void key_dec(const void* arg
)
310 dbg("%3.3f\n", *(float*)arg
);
313 static void key_selftest(const void* arg
)
321 static void key_rebatt(const void* arg
)
336 static void key_batdet(const void *arg
)
344 static void key_commfail(const void *arg
)
349 dbg("COMMFAIL disabled\n");
354 dbg("COMMFAIL enabled\n");
359 static void key_help(const void *arg
)
363 dbg("b Onbattery toggle\n");
364 dbg("o Overload toggle\n");
365 dbg("t Trim toggle\n");
366 dbg("s Boost toggle\n");
367 dbg("l BattLow toggle\n");
368 dbg("x Selftest toggle\n");
369 dbg("r ReplaceBatt toggle\n");
370 dbg("d BattDetach toggle\n");
371 dbg("c CommFail toggle\n");
372 dbg("7/4 BattPct (inc/dec) (use numeric keypad)\n");
373 dbg("8/5 LoadPct (inc/dec) (use numeric keypad)\n");
374 dbg("9/6 TimeLeft (inc/dec) (use numeric keypad)\n");
377 void handle_ups_cmd(char cmd
)
387 for (x
=0; upscmds
[x
].func
; x
++)
389 if (upscmds
[x
].cmd
== cmd
)
391 upscmds
[x
].func(upscmds
[x
].arg
);
396 if (!upscmds
[x
].func
)
400 void handle_key_cmd(char cmd
)
404 for (x
=0; keycmds
[x
].func
; x
++)
406 if (keycmds
[x
].key
== cmd
)
408 keycmds
[x
].func(keycmds
[x
].arg
);
415 ( ((a) > (b)) ? (a) : (b) )
417 int main(int argc
, char* argv
[])
426 /* Allow serial port device to be supplied on command line */
430 dev
= DEFAULT_DEVICE
;
432 /* Open serial port device */
433 ups
= open(dev
, O_RDWR
| O_NOCTTY
);
440 /* Set serial port for 2400 baud, N81 */
441 tio
.c_cflag
= B2400
| CS8
| CLOCAL
| CREAD
;
442 tio
.c_iflag
= IGNPAR
; /* Ignore errors, raw input */
443 tio
.c_oflag
= 0; /* Raw output */
444 tio
.c_lflag
= 0; /* No local echo */
445 cfsetospeed(&tio
, B2400
);
446 cfsetispeed(&tio
, B2400
);
447 tcflush(ups
, TCIFLUSH
);
448 tcsetattr(ups
, TCSANOW
, &tio
);
449 tcflush(ups
, TCIFLUSH
);
451 /* Disable echo and line buffering on stdin */
453 tcgetattr(con
, &tio
);
454 tio
.c_lflag
&= ~(ECHO
|ICANON
);
455 tcsetattr(con
, TCSANOW
, &tio
);
465 rc
= select(max(con
,ups
)+1, &fds
, NULL
, NULL
, NULL
);
467 while (rc
== -1 && (errno
== EAGAIN
|| errno
== EINTR
));
475 if (FD_ISSET(ups
, &fds
))
479 rc
= read(ups
, &cmd
, 1);
481 while (rc
!= 1 && (errno
== EAGAIN
|| errno
== EINTR
));
492 else if (cmd
>= 1 && cmd
<= 26)
501 if (FD_ISSET(con
, &fds
))
505 rc
= read(con
, &cmd
, 1);
507 while (rc
!= 1 && (errno
== EAGAIN
|| errno
== EINTR
));