Fix warnings (#21)
[usb-relay-hid.git] / commandline / usbrelay-cmd.c
CommitLineData
85fe04b8 1// Command line tool for low-cost USB/HID relays
6629800a 2//
fe63975d 3// pa02 20-Nov-2014 supports 1,2,4,8 - relay devices
c2529f42 4//
fe63975d 5//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6629800a 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
fe63975d 13#define A_VER_STR "r1.4"
f6b405fc 14#define A_URL "http://vusb.wikidot.com/project:driver-less-usb-relays-hid-interface"
6629800a 15
16#include <stdio.h>
17#include <string.h>
18#include <stdlib.h>
19#include "hidusb-tool.h"
20
1c892392 21#define USB_RELAY_VENDOR_NAME "www.dcttech.com"
b60fbf7e 22#define USB_RELAY_NAME_PREF "USBRelay" // + number
6629800a 23
fe63975d 24#define USB_RELAY_ID_STR_LEN 5 /* length of "unique serial number" in the devices */
6629800a 25
1c892392 26static int rel_read_status_raw(USBDEVHANDLE dev, void *raw_data);
6629800a 27
fe63975d 28#define printerr(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__);
29
6629800a 30/* ------------------------------------------------------------------------- */
31
c2529f42 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
fe63975d 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);
c2529f42 48}
49
50
6629800a 51static const char *usbErrorMessage(int errCode)
52{
53 static char buffer[80];
fe63975d 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);
6629800a 60 }
1c892392 61 return buffer;
6629800a 62}
63
b60fbf7e 64// Data for enumeration func:
65static struct
6629800a 66{
f6b405fc 67 USBDEVHANDLE mydev;
fe63975d 68 char id[USB_RELAY_ID_STR_LEN * 2];
b60fbf7e 69} g_enumCtx;
6629800a 70
1c892392 71
fe63975d 72static int g_max_relay_num = 0; // number of relays in the active device
73
74
b60fbf7e 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;
f6b405fc 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 }
b60fbf7e 89
90 err = usbhidGetProductString(dev, buffer, sizeof(buffer));
f6b405fc 91 if (err)
92 {
93 goto next;
94 }
b60fbf7e 95
645340be 96 i = (int)strlen(buffer);
56c8dcf9 97 if ( i != (int)strlen(productName) + 1 )
f6b405fc 98 {
99 goto next;
100 }
b60fbf7e 101
102 /* the last char of ProductString is number of relays */
f6b405fc 103 num = (int)(buffer[i - 1]) - (int)'0';
b60fbf7e 104 buffer[i - 1] = 0;
105
f6b405fc 106 if ( 0 != strcmp( buffer, productName) )
107 {
108 goto next;
109 }
b60fbf7e 110
f6b405fc 111 if ( num <= 0 || num > 8 )
112 {
fe63975d 113 printerr("Unknown relay device? num relays=%d\n", num);
f6b405fc 114 goto next;
115 }
b60fbf7e 116
fe63975d 117 /* Check the unique ID: USB_RELAY_ID_STR_LEN bytes at offset 1 (just after the report id) */
b60fbf7e 118 err = rel_read_status_raw(dev, buffer);
119 if( err < 0 )
f6b405fc 120 {
fe63975d 121 printerr("Error reading report 0: %s\n", usbErrorMessage(err));
f6b405fc 122 goto next;
1c892392 123 }
b60fbf7e 124
fe63975d 125 for (i = 1; i <= USB_RELAY_ID_STR_LEN; i++)
f6b405fc 126 {
b60fbf7e 127 unsigned char x = (unsigned char)buffer[i];
128 if (x <= 0x20 || x >= 0x7F)
f6b405fc 129 {
b60fbf7e 130 fprintf(stderr, "Bad device ID!\n");
f6b405fc 131 goto next;
b60fbf7e 132 }
6629800a 133 }
134
fe63975d 135 if( buffer[USB_RELAY_ID_STR_LEN + 1] != 0 )
f6b405fc 136 {
fe63975d 137 printerr("Bad device ID!\n");
f6b405fc 138 goto next;
6629800a 139 }
140
f6b405fc 141 DEBUG_PRINT(("Device %s%d found: ID=[%5s]\n", productName, num, &buffer[1]));
b60fbf7e 142 g_max_relay_num = num;
c2529f42 143
f6b405fc 144 if ( g_enumCtx.id[0] != 0 )
145 {
fe63975d 146 if ( 0 != memcmp(g_enumCtx.id, &buffer[1], USB_RELAY_ID_STR_LEN) )
f6b405fc 147 goto next;
148 }
c2529f42 149#if 0
f6b405fc 150 if ( g_enumCtx.mydev )
151 {
fe63975d 152 printerr("ERROR: More than one relay device found. ID must be specified\n");
f6b405fc 153 usbhidCloseDevice(dev);
154 usbhidCloseDevice(g_enumCtx.mydev);
155 return 0;
156 }
c2529f42 157#endif
f6b405fc 158 g_enumCtx.mydev = dev;
fe63975d 159 return 0; /* stop */
b60fbf7e 160
161 next:
f6b405fc 162 /* Continue search */
163 usbhidCloseDevice(dev);
164 return 1;
b60fbf7e 165}
166
167static USBDEVHANDLE openDevice(void)
168{
169 int err;
b60fbf7e 170 err = usbhidEnumDevices(USB_CFG_VENDOR_ID, USB_CFG_DEVICE_ID, &g_enumCtx, enumFunc);
171
f6b405fc 172 if ( err || !g_enumCtx.mydev )
173 {
fe63975d 174 printerr("error finding USB relay: %s\n", usbErrorMessage(err));
f6b405fc 175 return NULL;
176 }
b60fbf7e 177
178 return g_enumCtx.mydev;
6629800a 179}
180
6629800a 181
6629800a 182// Read state of all relays
183// @return bit mask of all relays (R1->bit 0, R2->bit 1 ...) or -1 on error
1c892392 184static int rel_read_status_raw(USBDEVHANDLE dev, void *raw_data)
6629800a 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 ) {
fe63975d 194 printerr("error reading status: %s\n", usbErrorMessage(err));
6629800a 195 return -1;
196 }
197
198 if ( len != 9 || buffer[0] != reportnum ) {
fe63975d 199 printerr("ERROR: wrong HID report returned! %d\n", len);
6629800a 200 return -2;
201 }
202
203 if (raw_data) {
1c892392 204 /* copy raw report data */
fe63975d 205 memcpy( raw_data, buffer, len );
6629800a 206 }
207
b60fbf7e 208 return (unsigned char)buffer[8]; /* byte of relay states */
6629800a 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
28f22397 218 if ( numstr && (0 == strcasecmp(numstr,"all")) ) {
6629800a 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 ) {
fe63975d 230 printerr("Invalid relay number. Must be 1-%d or ALL)\n", g_max_relay_num);
6629800a 231 return 1;
232 }
233
234 memset(buffer, 0, sizeof(buffer));
235 buffer[0] = 0; /* report # */
236 buffer[1] = is_on ? 0xFF : 0xFD;
fe63975d 237 // ALL ON=0xFE, ALL OFF=0xFC
6629800a 238 buffer[2] = (unsigned char)relaynum;
28f22397 239 if((err = usbhidSetReport(dev, (void*)buffer, 9)) != 0) {
fe63975d 240 printerr("Error writing data: %s\n", usbErrorMessage(err));
6629800a 241 return 1;
242 }
243
244 // Read back & verify
1c892392 245 err = rel_read_status_raw(dev, NULL);
6629800a 246 if ( err >= 0 ) {
247 err = (err >> (unsigned)(relaynum -1)) & 1;
248 err ^= !!is_on;
249 }
250
251 if ( err ) {
fe63975d 252 printerr("Error: failed to set relay %u %s\n", relaynum, is_on ? "ON":"OFF");
6629800a 253 return 1;
254 }
255
256 return 0;
257}
258
259
1c892392 260static int show_status(USBDEVHANDLE dev)
261{
262 int err;
263 char buffer[10];
f6b405fc 264 static const char* on_off[] = {"OFF","ON"};
1c892392 265
266#define onoff(n) on_off[!!(err & (1U << n))]
267
268 err = rel_read_status_raw(dev, buffer);
fe63975d 269 if ( err < 0 ) {
270 printerr("Error reading data: %s\n", usbErrorMessage(err));
1c892392 271 err = 1;
272 } else {
1c892392 273 switch (g_max_relay_num) {
274 case 1:
275 printf("Board ID=[%5.5s] State: R1=%s\n", &buffer[1], onoff(0) );
f6b405fc 276 break;
1c892392 277 case 2:
278 printf("Board ID=[%5.5s] State: R1=%s R2=%s\n",
f6b405fc 279 &buffer[1], onoff(0), onoff(1) );
280 break;
1c892392 281 case 4:
282 printf("Board ID=[%5.5s] State: R1=%s R3=%s R1=%s R4=%s\n",
f6b405fc 283 &buffer[1], onoff(0), onoff(1), onoff(2), onoff(3) );
284 break;
fe63975d 285 default: /* print as bit mask */
f6b405fc 286 printf("Board ID=[%5.5s] State: %2.2X (hex)\n", &buffer[1], (unsigned char)err );
287 break;
1c892392 288 }
289 err = 0;
290 }
291 return err;
292#undef onoff
293}
294
b60fbf7e 295// Enumerate available relay devices
296
297static int showFunc(USBDEVHANDLE dev, void *context)
298{
299 int err = enumFunc( dev, context );
f6b405fc 300 if (err != 0 || g_enumCtx.mydev == 0) // not my device, continue
301 return err;
b60fbf7e 302
303 show_status(g_enumCtx.mydev);
f6b405fc 304 usbhidCloseDevice(g_enumCtx.mydev);
305 g_enumCtx.mydev = 0;
b60fbf7e 306
f6b405fc 307 return 1; // continue
b60fbf7e 308}
309
310static int show_relays(void)
311{
312 int err;
f6b405fc 313 g_enumCtx.mydev = 0;
b60fbf7e 314
f6b405fc 315 err = usbhidEnumDevices(USB_CFG_VENDOR_ID, USB_CFG_DEVICE_ID, &g_enumCtx, showFunc);
316 if ( err )
317 {
fe63975d 318 printerr("Error finding USB relays: %s\n", usbErrorMessage(err));
f6b405fc 319 return 1;
320 }
b60fbf7e 321
322 return 0;
323}
324
325
6629800a 326int main(int argc, char **argv)
327{
1c892392 328 USBDEVHANDLE dev = 0;
6629800a 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]);
b60fbf7e 335 return 1;
6629800a 336 }
337
b60fbf7e 338 if ( strcasecmp(arg1, "enum") == 0 ) {
f6b405fc 339 err = show_relays();
340 return err;
341 }
342
343 if ( strncasecmp(arg1, "id=", 3) == 0 ) {
645340be 344 /* Set the ID for following commands. else use 1st found device.*/
fe63975d 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);
f6b405fc 347 return 1;
348 }
349
645340be 350 strcpy(g_enumCtx.id, &arg1[3]);
f6b405fc 351
352 // shift following params
353 arg1 = arg2;
c2529f42 354 arg2 = (argc >= 4) ? argv[3] : NULL;
f6b405fc 355 }
c2529f42 356
6629800a 357 dev = openDevice();
358 if ( !dev )
b60fbf7e 359 return 1;
6629800a 360
1c892392 361 if ( strncasecmp(arg1, "stat", 4) == 0 ) { // stat|state|status
362 err = show_status(dev);
1c892392 363 }else if( strcasecmp(arg1, "on" ) == 0) {
6629800a 364 err = rel_onoff(dev, 1, arg2);
1c892392 365 }else if( strcasecmp(arg1, "off" ) == 0) {
6629800a 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