Fix warnings (#21)
[usb-relay-hid.git] / commandline / usbrelay-cmd.c
... / ...
CommitLineData
1// Command line tool for low-cost USB/HID relays
2//
3// pa02 20-Nov-2014 supports 1,2,4,8 - relay devices
4//
5//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6
7/* Prototype: V-USB example: vusb-20121206/examples/hid-data/commandline/hidtool.c
8 * Author: Christian Starkjohann
9 * Creation Date: 2008-04-11
10 * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
11 */
12
13#define A_VER_STR "r1.4"
14#define A_URL "http://vusb.wikidot.com/project:driver-less-usb-relays-hid-interface"
15
16#include <stdio.h>
17#include <string.h>
18#include <stdlib.h>
19#include "hidusb-tool.h"
20
21#define USB_RELAY_VENDOR_NAME "www.dcttech.com"
22#define USB_RELAY_NAME_PREF "USBRelay" // + number
23
24#define USB_RELAY_ID_STR_LEN 5 /* length of "unique serial number" in the devices */
25
26static int rel_read_status_raw(USBDEVHANDLE dev, void *raw_data);
27
28#define printerr(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__);
29
30/* ------------------------------------------------------------------------- */
31
32static void usage(char *myName)
33{
34 char *p = strrchr(myName, '\\'); /* windows */
35 if (p) myName = p + 1;
36 else p = strrchr(myName, '/'); /* whatever */
37 if (p) myName = p + 1;
38
39 printf("HID USB relay utility, " A_VER_STR " "
40 "For info: " A_URL "\n"
41 "Usage:\n");
42 printf(" %s on <num> - turn relay <num> ON\n", myName);
43 printf(" %s off <num> - turn relay <num> OFF\n", myName);
44 printf(" %s state - print states of the relays\n", myName);
45 printf(" %s enum - print state of all found relay devices\n", myName);
46 printf("\nParameter ID=XXXXX selects one device if several are connected.\n");
47 printf("Example: %s ID=ABCDE ON 2\n\n", myName);
48}
49
50
51static const char *usbErrorMessage(int errCode)
52{
53 static char buffer[80];
54 buffer[0] = 0;
55 if ( errCode != USBHID_ERR_UNKNOWN ) {
56 usbhidStrerror_r(errCode, buffer, sizeof(buffer));
57 }
58 if ( 0 == buffer[0] ) {
59 snprintf(buffer, sizeof(buffer), "Unknown error (%d)", errCode);
60 }
61 return buffer;
62}
63
64// Data for enumeration func:
65static struct
66{
67 USBDEVHANDLE mydev;
68 char id[USB_RELAY_ID_STR_LEN * 2];
69} g_enumCtx;
70
71
72static int g_max_relay_num = 0; // number of relays in the active device
73
74
75static int enumFunc(USBDEVHANDLE dev, void *context)
76{
77 static const char vendorName[] = USB_RELAY_VENDOR_NAME;
78 static const char productName[] = USB_RELAY_NAME_PREF;
79 int err;
80 char buffer[128*sizeof(short)]; // max USB string is 128 UTF-16 chars
81 int num = 0;
82 int i;
83
84 err = usbhidGetVendorString(dev, buffer, sizeof(buffer));
85 if ( err || 0 != strcmp( buffer, vendorName) )
86 {
87 goto next;
88 }
89
90 err = usbhidGetProductString(dev, buffer, sizeof(buffer));
91 if (err)
92 {
93 goto next;
94 }
95
96 i = (int)strlen(buffer);
97 if ( i != (int)strlen(productName) + 1 )
98 {
99 goto next;
100 }
101
102 /* the last char of ProductString is number of relays */
103 num = (int)(buffer[i - 1]) - (int)'0';
104 buffer[i - 1] = 0;
105
106 if ( 0 != strcmp( buffer, productName) )
107 {
108 goto next;
109 }
110
111 if ( num <= 0 || num > 8 )
112 {
113 printerr("Unknown relay device? num relays=%d\n", num);
114 goto next;
115 }
116
117 /* Check the unique ID: USB_RELAY_ID_STR_LEN bytes at offset 1 (just after the report id) */
118 err = rel_read_status_raw(dev, buffer);
119 if( err < 0 )
120 {
121 printerr("Error reading report 0: %s\n", usbErrorMessage(err));
122 goto next;
123 }
124
125 for (i = 1; i <= USB_RELAY_ID_STR_LEN; i++)
126 {
127 unsigned char x = (unsigned char)buffer[i];
128 if (x <= 0x20 || x >= 0x7F)
129 {
130 fprintf(stderr, "Bad device ID!\n");
131 goto next;
132 }
133 }
134
135 if( buffer[USB_RELAY_ID_STR_LEN + 1] != 0 )
136 {
137 printerr("Bad device ID!\n");
138 goto next;
139 }
140
141 DEBUG_PRINT(("Device %s%d found: ID=[%5s]\n", productName, num, &buffer[1]));
142 g_max_relay_num = num;
143
144 if ( g_enumCtx.id[0] != 0 )
145 {
146 if ( 0 != memcmp(g_enumCtx.id, &buffer[1], USB_RELAY_ID_STR_LEN) )
147 goto next;
148 }
149#if 0
150 if ( g_enumCtx.mydev )
151 {
152 printerr("ERROR: More than one relay device found. ID must be specified\n");
153 usbhidCloseDevice(dev);
154 usbhidCloseDevice(g_enumCtx.mydev);
155 return 0;
156 }
157#endif
158 g_enumCtx.mydev = dev;
159 return 0; /* stop */
160
161 next:
162 /* Continue search */
163 usbhidCloseDevice(dev);
164 return 1;
165}
166
167static USBDEVHANDLE openDevice(void)
168{
169 int err;
170 err = usbhidEnumDevices(USB_CFG_VENDOR_ID, USB_CFG_DEVICE_ID, &g_enumCtx, enumFunc);
171
172 if ( err || !g_enumCtx.mydev )
173 {
174 printerr("error finding USB relay: %s\n", usbErrorMessage(err));
175 return NULL;
176 }
177
178 return g_enumCtx.mydev;
179}
180
181
182// Read state of all relays
183// @return bit mask of all relays (R1->bit 0, R2->bit 1 ...) or -1 on error
184static int rel_read_status_raw(USBDEVHANDLE dev, void *raw_data)
185{
186 char buffer[10];
187 int err;
188 int reportnum = 0;
189 int len = 8 + 1; /* report id 1 byte + 8 bytes data */
190 memset(buffer, 0, sizeof(buffer));
191
192 err = usbhidGetReport(dev, reportnum, buffer, &len);
193 if ( err ) {
194 printerr("error reading status: %s\n", usbErrorMessage(err));
195 return -1;
196 }
197
198 if ( len != 9 || buffer[0] != reportnum ) {
199 printerr("ERROR: wrong HID report returned! %d\n", len);
200 return -2;
201 }
202
203 if (raw_data) {
204 /* copy raw report data */
205 memcpy( raw_data, buffer, len );
206 }
207
208 return (unsigned char)buffer[8]; /* byte of relay states */
209}
210
211
212static int rel_onoff( USBDEVHANDLE dev, int is_on, char const *numstr )
213{
214 unsigned char buffer[10];
215 int err = -1;
216 int relaynum = numstr ? atoi(numstr) : 0;
217
218 if ( numstr && (0 == strcasecmp(numstr,"all")) ) {
219 char x[2] = {'1', 0};
220 int i;
221 for (i = 1; i <= g_max_relay_num; i++) {
222 x[0] = (char)('0' + i);
223 err = rel_onoff(dev, is_on, x);
224 if (err) break;
225 }
226 return err;
227 }
228
229 if ( relaynum <= 0 || relaynum > g_max_relay_num ) {
230 printerr("Invalid relay number. Must be 1-%d or ALL)\n", g_max_relay_num);
231 return 1;
232 }
233
234 memset(buffer, 0, sizeof(buffer));
235 buffer[0] = 0; /* report # */
236 buffer[1] = is_on ? 0xFF : 0xFD;
237 // ALL ON=0xFE, ALL OFF=0xFC
238 buffer[2] = (unsigned char)relaynum;
239 if((err = usbhidSetReport(dev, (void*)buffer, 9)) != 0) {
240 printerr("Error writing data: %s\n", usbErrorMessage(err));
241 return 1;
242 }
243
244 // Read back & verify
245 err = rel_read_status_raw(dev, NULL);
246 if ( err >= 0 ) {
247 err = (err >> (unsigned)(relaynum -1)) & 1;
248 err ^= !!is_on;
249 }
250
251 if ( err ) {
252 printerr("Error: failed to set relay %u %s\n", relaynum, is_on ? "ON":"OFF");
253 return 1;
254 }
255
256 return 0;
257}
258
259
260static int show_status(USBDEVHANDLE dev)
261{
262 int err;
263 char buffer[10];
264 static const char* on_off[] = {"OFF","ON"};
265
266#define onoff(n) on_off[!!(err & (1U << n))]
267
268 err = rel_read_status_raw(dev, buffer);
269 if ( err < 0 ) {
270 printerr("Error reading data: %s\n", usbErrorMessage(err));
271 err = 1;
272 } else {
273 switch (g_max_relay_num) {
274 case 1:
275 printf("Board ID=[%5.5s] State: R1=%s\n", &buffer[1], onoff(0) );
276 break;
277 case 2:
278 printf("Board ID=[%5.5s] State: R1=%s R2=%s\n",
279 &buffer[1], onoff(0), onoff(1) );
280 break;
281 case 4:
282 printf("Board ID=[%5.5s] State: R1=%s R3=%s R1=%s R4=%s\n",
283 &buffer[1], onoff(0), onoff(1), onoff(2), onoff(3) );
284 break;
285 default: /* print as bit mask */
286 printf("Board ID=[%5.5s] State: %2.2X (hex)\n", &buffer[1], (unsigned char)err );
287 break;
288 }
289 err = 0;
290 }
291 return err;
292#undef onoff
293}
294
295// Enumerate available relay devices
296
297static int showFunc(USBDEVHANDLE dev, void *context)
298{
299 int err = enumFunc( dev, context );
300 if (err != 0 || g_enumCtx.mydev == 0) // not my device, continue
301 return err;
302
303 show_status(g_enumCtx.mydev);
304 usbhidCloseDevice(g_enumCtx.mydev);
305 g_enumCtx.mydev = 0;
306
307 return 1; // continue
308}
309
310static int show_relays(void)
311{
312 int err;
313 g_enumCtx.mydev = 0;
314
315 err = usbhidEnumDevices(USB_CFG_VENDOR_ID, USB_CFG_DEVICE_ID, &g_enumCtx, showFunc);
316 if ( err )
317 {
318 printerr("Error finding USB relays: %s\n", usbErrorMessage(err));
319 return 1;
320 }
321
322 return 0;
323}
324
325
326int main(int argc, char **argv)
327{
328 USBDEVHANDLE dev = 0;
329 int err;
330 char const *arg1 = (argc >= 2) ? argv[1] : NULL;
331 char const *arg2 = (argc >= 3) ? argv[2] : NULL;
332
333 if ( !arg1 ) {
334 usage(argv[0]);
335 return 1;
336 }
337
338 if ( strcasecmp(arg1, "enum") == 0 ) {
339 err = show_relays();
340 return err;
341 }
342
343 if ( strncasecmp(arg1, "id=", 3) == 0 ) {
344 /* Set the ID for following commands. else use 1st found device.*/
345 if (strlen(&arg1[3]) != USB_RELAY_ID_STR_LEN) {
346 printerr("ERROR: ID must be %d characters (%s)\n", USB_RELAY_ID_STR_LEN, arg1);
347 return 1;
348 }
349
350 strcpy(g_enumCtx.id, &arg1[3]);
351
352 // shift following params
353 arg1 = arg2;
354 arg2 = (argc >= 4) ? argv[3] : NULL;
355 }
356
357 dev = openDevice();
358 if ( !dev )
359 return 1;
360
361 if ( strncasecmp(arg1, "stat", 4) == 0 ) { // stat|state|status
362 err = show_status(dev);
363 }else if( strcasecmp(arg1, "on" ) == 0) {
364 err = rel_onoff(dev, 1, arg2);
365 }else if( strcasecmp(arg1, "off" ) == 0) {
366 err = rel_onoff(dev, 0, arg2);
367 }else {
368 usage(argv[0]);
369 err = 2;
370 }
371
372 if ( dev ) {
373 usbhidCloseDevice(dev);
374 }
375
376 return err;
377}
378