Refact. before adding OS X (1)
[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
f6b405fc 93 i = strlen(buffer);
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;
167
168 // TODO: enumerate all instances, then filter by unique ID
169 //$$$ find any one device
170
171 err = usbhidEnumDevices(USB_CFG_VENDOR_ID, USB_CFG_DEVICE_ID, &g_enumCtx, enumFunc);
172
f6b405fc 173 if ( err || !g_enumCtx.mydev )
174 {
175 fprintf(stderr, "error finding USB relay: %s\n", usbErrorMessage(err));
176 return NULL;
177 }
b60fbf7e 178
179 return g_enumCtx.mydev;
6629800a 180}
181
182/* ------------------------------------------------------------------------- */
183#if 0
184static void hexdump(char *buffer, int len)
185{
186int i;
187FILE *fp = stdout;
188
189 for(i = 0; i < len; i++){
190 if(i != 0){
191 if(i % 16 == 0){
192 fprintf(fp, "\n");
193 }else{
194 fprintf(fp, " ");
195 }
196 }
197 fprintf(fp, "0x%02x", buffer[i] & 0xff);
198 }
199 if(i != 0)
200 fprintf(fp, "\n");
201}
202#endif
203
204/* ------------------------------------------------------------------------- */
205
6629800a 206// Read state of all relays
207// @return bit mask of all relays (R1->bit 0, R2->bit 1 ...) or -1 on error
1c892392 208static int rel_read_status_raw(USBDEVHANDLE dev, void *raw_data)
6629800a 209{
210 char buffer[10];
211 int err;
212 int reportnum = 0;
213 int len = 8 + 1; /* report id 1 byte + 8 bytes data */
214 memset(buffer, 0, sizeof(buffer));
215
216 err = usbhidGetReport(dev, reportnum, buffer, &len);
217 if ( err ) {
218 fprintf(stderr, "error reading status: %s\n", usbErrorMessage(err));
219 return -1;
220 }
221
222 if ( len != 9 || buffer[0] != reportnum ) {
28f22397 223 fprintf(stderr, "ERROR: wrong HID report returned! %d\n", len);
6629800a 224 return -2;
225 }
226
227 if (raw_data) {
1c892392 228 /* copy raw report data */
6629800a 229 memcpy( raw_data, buffer, sizeof(buffer) );
230 }
231
b60fbf7e 232 return (unsigned char)buffer[8]; /* byte of relay states */
6629800a 233}
234
235
236static int rel_onoff( USBDEVHANDLE dev, int is_on, char const *numstr )
237{
238 unsigned char buffer[10];
239 int err = -1;
240 int relaynum = numstr ? atoi(numstr) : 0;
241
28f22397 242 if ( numstr && (0 == strcasecmp(numstr,"all")) ) {
6629800a 243 char x[2] = {'1', 0};
244 int i;
245 for (i = 1; i <= g_max_relay_num; i++) {
246 x[0] = (char)('0' + i);
247 err = rel_onoff(dev, is_on, x);
248 if (err) break;
249 }
250 return err;
251 }
252
253 if ( relaynum <= 0 || relaynum > g_max_relay_num ) {
28f22397 254 fprintf(stderr, "Invalid relay number. Must be 1-%d or ALL)\n", g_max_relay_num);
6629800a 255 return 1;
256 }
257
258 memset(buffer, 0, sizeof(buffer));
259 buffer[0] = 0; /* report # */
260 buffer[1] = is_on ? 0xFF : 0xFD;
261 buffer[2] = (unsigned char)relaynum;
28f22397 262 if((err = usbhidSetReport(dev, (void*)buffer, 9)) != 0) {
6629800a 263 fprintf(stderr, "Error writing data: %s\n", usbErrorMessage(err));
264 return 1;
265 }
266
267 // Read back & verify
1c892392 268 err = rel_read_status_raw(dev, NULL);
6629800a 269 if ( err >= 0 ) {
270 err = (err >> (unsigned)(relaynum -1)) & 1;
271 err ^= !!is_on;
272 }
273
274 if ( err ) {
275 fprintf(stderr, "Error: failed set %s relay %u\n", is_on ? "ON":"OFF", relaynum);
276 return 1;
277 }
278
279 return 0;
280}
281
282
1c892392 283static int show_status(USBDEVHANDLE dev)
284{
285 int err;
286 char buffer[10];
f6b405fc 287 static const char* on_off[] = {"OFF","ON"};
1c892392 288
289#define onoff(n) on_off[!!(err & (1U << n))]
290
291 err = rel_read_status_raw(dev, buffer);
292 if ( err < 0 ){
293 fprintf(stderr, "error reading data: %s\n", usbErrorMessage(err));
294 err = 1;
295 } else {
296 //hexdump(buffer + 1, len - 1);
297 switch (g_max_relay_num) {
298 case 1:
299 printf("Board ID=[%5.5s] State: R1=%s\n", &buffer[1], onoff(0) );
f6b405fc 300 break;
1c892392 301 case 2:
302 printf("Board ID=[%5.5s] State: R1=%s R2=%s\n",
f6b405fc 303 &buffer[1], onoff(0), onoff(1) );
304 break;
1c892392 305 case 4:
306 printf("Board ID=[%5.5s] State: R1=%s R3=%s R1=%s R4=%s\n",
f6b405fc 307 &buffer[1], onoff(0), onoff(1), onoff(2), onoff(3) );
308 break;
1c892392 309 default:
f6b405fc 310 printf("Board ID=[%5.5s] State: %2.2X (hex)\n", &buffer[1], (unsigned char)err );
311 break;
1c892392 312 }
313 err = 0;
314 }
315 return err;
316#undef onoff
317}
318
b60fbf7e 319// Enumerate available relay devices
320
321static int showFunc(USBDEVHANDLE dev, void *context)
322{
323 int err = enumFunc( dev, context );
f6b405fc 324 if (err != 0 || g_enumCtx.mydev == 0) // not my device, continue
325 return err;
b60fbf7e 326
327 show_status(g_enumCtx.mydev);
f6b405fc 328 usbhidCloseDevice(g_enumCtx.mydev);
329 g_enumCtx.mydev = 0;
b60fbf7e 330
f6b405fc 331 return 1; // continue
b60fbf7e 332}
333
334static int show_relays(void)
335{
336 int err;
f6b405fc 337 g_enumCtx.mydev = 0;
b60fbf7e 338
f6b405fc 339 err = usbhidEnumDevices(USB_CFG_VENDOR_ID, USB_CFG_DEVICE_ID, &g_enumCtx, showFunc);
340 if ( err )
341 {
342 fprintf(stderr, "Error finding USB relay: %s\n", usbErrorMessage(err));
343 return 1;
344 }
b60fbf7e 345
346 return 0;
347}
348
349
6629800a 350int main(int argc, char **argv)
351{
1c892392 352 USBDEVHANDLE dev = 0;
6629800a 353 int err;
354 char const *arg1 = (argc >= 2) ? argv[1] : NULL;
355 char const *arg2 = (argc >= 3) ? argv[2] : NULL;
356
357 if ( !arg1 ) {
358 usage(argv[0]);
b60fbf7e 359 return 1;
6629800a 360 }
361
b60fbf7e 362 if ( strcasecmp(arg1, "enum") == 0 ) {
f6b405fc 363 err = show_relays();
364 return err;
365 }
366
367 if ( strncasecmp(arg1, "id=", 3) == 0 ) {
368 if (strlen(&arg1[3]) != 5) {
369 fprintf(stderr, "ERROR: ID must be 5 characters (%s)\n", arg1);
370 return 1;
371 }
372
373 strcpy( g_enumCtx.id, &arg1[3]);
374
375 // shift following params
376 arg1 = arg2;
c2529f42 377 arg2 = (argc >= 4) ? argv[3] : NULL;
f6b405fc 378 }
c2529f42 379
6629800a 380 dev = openDevice();
381 if ( !dev )
b60fbf7e 382 return 1;
6629800a 383
1c892392 384 if ( strncasecmp(arg1, "stat", 4) == 0 ) { // stat|state|status
385 err = show_status(dev);
f6b405fc 386 // TODO enumerate all devices
1c892392 387 }else if( strcasecmp(arg1, "on" ) == 0) {
6629800a 388 err = rel_onoff(dev, 1, arg2);
1c892392 389 }else if( strcasecmp(arg1, "off" ) == 0) {
6629800a 390 err = rel_onoff(dev, 0, arg2);
391 }else {
392 usage(argv[0]);
393 err = 2;
394 }
395
396 if ( dev ) {
397 usbhidCloseDevice(dev);
398 }
399
400 return err;
401}
402
403/* ------------------------------------------------------------------------- */