GUI demo: fixes, added VS2013 config, x64
[usb-relay-hid.git] / commandline / usbrelay-cmd.c
1 // Command line tool for Chinese 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
26 static int rel_read_status_raw(USBDEVHANDLE dev, void *raw_data);
27
28 #define printerr(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__);
29
30 /* ------------------------------------------------------------------------- */
31
32 static 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
51 static 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:
65 static struct
66 {
67 USBDEVHANDLE mydev;
68 char id[USB_RELAY_ID_STR_LEN * 2];
69 } g_enumCtx;
70
71
72 static int g_max_relay_num = 0; // number of relays in the active device
73
74
75 static 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 != 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
167 static USBDEVHANDLE openDevice(void)
168 {
169 int err;
170
171 err = usbhidEnumDevices(USB_CFG_VENDOR_ID, USB_CFG_DEVICE_ID, &g_enumCtx, enumFunc);
172
173 if ( err || !g_enumCtx.mydev )
174 {
175 printerr("error finding USB relay: %s\n", usbErrorMessage(err));
176 return NULL;
177 }
178
179 return g_enumCtx.mydev;
180 }
181
182
183 // Read state of all relays
184 // @return bit mask of all relays (R1->bit 0, R2->bit 1 ...) or -1 on error
185 static int rel_read_status_raw(USBDEVHANDLE dev, void *raw_data)
186 {
187 char buffer[10];
188 int err;
189 int reportnum = 0;
190 int len = 8 + 1; /* report id 1 byte + 8 bytes data */
191 memset(buffer, 0, sizeof(buffer));
192
193 err = usbhidGetReport(dev, reportnum, buffer, &len);
194 if ( err ) {
195 printerr("error reading status: %s\n", usbErrorMessage(err));
196 return -1;
197 }
198
199 if ( len != 9 || buffer[0] != reportnum ) {
200 printerr("ERROR: wrong HID report returned! %d\n", len);
201 return -2;
202 }
203
204 if (raw_data) {
205 /* copy raw report data */
206 memcpy( raw_data, buffer, len );
207 }
208
209 return (unsigned char)buffer[8]; /* byte of relay states */
210 }
211
212
213 static int rel_onoff( USBDEVHANDLE dev, int is_on, char const *numstr )
214 {
215 unsigned char buffer[10];
216 int err = -1;
217 int relaynum = numstr ? atoi(numstr) : 0;
218
219 if ( numstr && (0 == strcasecmp(numstr,"all")) ) {
220 char x[2] = {'1', 0};
221 int i;
222 for (i = 1; i <= g_max_relay_num; i++) {
223 x[0] = (char)('0' + i);
224 err = rel_onoff(dev, is_on, x);
225 if (err) break;
226 }
227 return err;
228 }
229
230 if ( relaynum <= 0 || relaynum > g_max_relay_num ) {
231 printerr("Invalid relay number. Must be 1-%d or ALL)\n", g_max_relay_num);
232 return 1;
233 }
234
235 memset(buffer, 0, sizeof(buffer));
236 buffer[0] = 0; /* report # */
237 buffer[1] = is_on ? 0xFF : 0xFD;
238 // ALL ON=0xFE, ALL OFF=0xFC
239 buffer[2] = (unsigned char)relaynum;
240 if((err = usbhidSetReport(dev, (void*)buffer, 9)) != 0) {
241 printerr("Error writing data: %s\n", usbErrorMessage(err));
242 return 1;
243 }
244
245 // Read back & verify
246 err = rel_read_status_raw(dev, NULL);
247 if ( err >= 0 ) {
248 err = (err >> (unsigned)(relaynum -1)) & 1;
249 err ^= !!is_on;
250 }
251
252 if ( err ) {
253 printerr("Error: failed to set relay %u %s\n", relaynum, is_on ? "ON":"OFF");
254 return 1;
255 }
256
257 return 0;
258 }
259
260
261 static int show_status(USBDEVHANDLE dev)
262 {
263 int err;
264 char buffer[10];
265 static const char* on_off[] = {"OFF","ON"};
266
267 #define onoff(n) on_off[!!(err & (1U << n))]
268
269 err = rel_read_status_raw(dev, buffer);
270 if ( err < 0 ) {
271 printerr("Error reading data: %s\n", usbErrorMessage(err));
272 err = 1;
273 } else {
274 switch (g_max_relay_num) {
275 case 1:
276 printf("Board ID=[%5.5s] State: R1=%s\n", &buffer[1], onoff(0) );
277 break;
278 case 2:
279 printf("Board ID=[%5.5s] State: R1=%s R2=%s\n",
280 &buffer[1], onoff(0), onoff(1) );
281 break;
282 case 4:
283 printf("Board ID=[%5.5s] State: R1=%s R3=%s R1=%s R4=%s\n",
284 &buffer[1], onoff(0), onoff(1), onoff(2), onoff(3) );
285 break;
286 default: /* print as bit mask */
287 printf("Board ID=[%5.5s] State: %2.2X (hex)\n", &buffer[1], (unsigned char)err );
288 break;
289 }
290 err = 0;
291 }
292 return err;
293 #undef onoff
294 }
295
296 // Enumerate available relay devices
297
298 static int showFunc(USBDEVHANDLE dev, void *context)
299 {
300 int err = enumFunc( dev, context );
301 if (err != 0 || g_enumCtx.mydev == 0) // not my device, continue
302 return err;
303
304 show_status(g_enumCtx.mydev);
305 usbhidCloseDevice(g_enumCtx.mydev);
306 g_enumCtx.mydev = 0;
307
308 return 1; // continue
309 }
310
311 static int show_relays(void)
312 {
313 int err;
314 g_enumCtx.mydev = 0;
315
316 err = usbhidEnumDevices(USB_CFG_VENDOR_ID, USB_CFG_DEVICE_ID, &g_enumCtx, showFunc);
317 if ( err )
318 {
319 printerr("Error finding USB relays: %s\n", usbErrorMessage(err));
320 return 1;
321 }
322
323 return 0;
324 }
325
326
327 int main(int argc, char **argv)
328 {
329 USBDEVHANDLE dev = 0;
330 int err;
331 char const *arg1 = (argc >= 2) ? argv[1] : NULL;
332 char const *arg2 = (argc >= 3) ? argv[2] : NULL;
333
334 if ( !arg1 ) {
335 usage(argv[0]);
336 return 1;
337 }
338
339 if ( strcasecmp(arg1, "enum") == 0 ) {
340 err = show_relays();
341 return err;
342 }
343
344 if ( strncasecmp(arg1, "id=", 3) == 0 ) {
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 // TODO enumerate all devices
364 }else if( strcasecmp(arg1, "on" ) == 0) {
365 err = rel_onoff(dev, 1, arg2);
366 }else if( strcasecmp(arg1, "off" ) == 0) {
367 err = rel_onoff(dev, 0, arg2);
368 }else {
369 usage(argv[0]);
370 err = 2;
371 }
372
373 if ( dev ) {
374 usbhidCloseDevice(dev);
375 }
376
377 return err;
378 }
379
380 /* ------------------------------------------------------------------------- */