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