1 // Command line tool for Chinese USB/HID relays
3 // pa02 20-Nov-2014 supports 1,2,4,8 - relay devices
5 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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
13 #define A_VER_STR "r1.4"
14 #define A_URL "http://vusb.wikidot.com/project:driver-less-usb-relays-hid-interface"
19 #include "hidusb-tool.h"
21 #define USB_RELAY_VENDOR_NAME "www.dcttech.com"
22 #define USB_RELAY_NAME_PREF "USBRelay" // + number
24 #define USB_RELAY_ID_STR_LEN 5 /* length of "unique serial number" in the devices */
26 static int rel_read_status_raw(USBDEVHANDLE dev
, void *raw_data
);
28 #define printerr(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__);
30 /* ------------------------------------------------------------------------- */
32 static void usage(char *myName
)
34 char *p
= strrchr(myName
, '\\'); /* windows */
35 if (p
) myName
= p
+ 1;
36 else p
= strrchr(myName
, '/'); /* whatever */
37 if (p
) myName
= p
+ 1;
39 printf("HID USB relay utility, " A_VER_STR
" "
40 "For info: " A_URL
"\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
);
51 static const char *usbErrorMessage(int errCode
)
53 static char buffer
[80];
55 if ( errCode
!= USBHID_ERR_UNKNOWN
) {
56 usbhidStrerror_r(errCode
, buffer
, sizeof(buffer
));
58 if ( 0 == buffer
[0] ) {
59 snprintf(buffer
, sizeof(buffer
), "Unknown error (%d)", errCode
);
64 // Data for enumeration func:
68 char id
[USB_RELAY_ID_STR_LEN
* 2];
72 static int g_max_relay_num
= 0; // number of relays in the active device
75 static int enumFunc(USBDEVHANDLE dev
, void *context
)
77 static const char vendorName
[] = USB_RELAY_VENDOR_NAME
;
78 static const char productName
[] = USB_RELAY_NAME_PREF
;
80 char buffer
[128*sizeof(short)]; // max USB string is 128 UTF-16 chars
84 err
= usbhidGetVendorString(dev
, buffer
, sizeof(buffer
));
85 if ( err
|| 0 != strcmp( buffer
, vendorName
) )
90 err
= usbhidGetProductString(dev
, buffer
, sizeof(buffer
));
96 i
= (int)strlen(buffer
);
97 if ( i
!= strlen(productName
) + 1 )
102 /* the last char of ProductString is number of relays */
103 num
= (int)(buffer
[i
- 1]) - (int)'0';
106 if ( 0 != strcmp( buffer
, productName
) )
111 if ( num
<= 0 || num
> 8 )
113 printerr("Unknown relay device? num relays=%d\n", num
);
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
);
121 printerr("Error reading report 0: %s\n", usbErrorMessage(err
));
125 for (i
= 1; i
<= USB_RELAY_ID_STR_LEN
; i
++)
127 unsigned char x
= (unsigned char)buffer
[i
];
128 if (x
<= 0x20 || x
>= 0x7F)
130 fprintf(stderr
, "Bad device ID!\n");
135 if( buffer
[USB_RELAY_ID_STR_LEN
+ 1] != 0 )
137 printerr("Bad device ID!\n");
141 DEBUG_PRINT(("Device %s%d found: ID=[%5s]\n", productName
, num
, &buffer
[1]));
142 g_max_relay_num
= num
;
144 if ( g_enumCtx
.id
[0] != 0 )
146 if ( 0 != memcmp(g_enumCtx
.id
, &buffer
[1], USB_RELAY_ID_STR_LEN
) )
150 if ( g_enumCtx
.mydev
)
152 printerr("ERROR: More than one relay device found. ID must be specified\n");
153 usbhidCloseDevice(dev
);
154 usbhidCloseDevice(g_enumCtx
.mydev
);
158 g_enumCtx
.mydev
= dev
;
162 /* Continue search */
163 usbhidCloseDevice(dev
);
167 static USBDEVHANDLE
openDevice(void)
171 err
= usbhidEnumDevices(USB_CFG_VENDOR_ID
, USB_CFG_DEVICE_ID
, &g_enumCtx
, enumFunc
);
173 if ( err
|| !g_enumCtx
.mydev
)
175 printerr("error finding USB relay: %s\n", usbErrorMessage(err
));
179 return g_enumCtx
.mydev
;
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
)
190 int len
= 8 + 1; /* report id 1 byte + 8 bytes data */
191 memset(buffer
, 0, sizeof(buffer
));
193 err
= usbhidGetReport(dev
, reportnum
, buffer
, &len
);
195 printerr("error reading status: %s\n", usbErrorMessage(err
));
199 if ( len
!= 9 || buffer
[0] != reportnum
) {
200 printerr("ERROR: wrong HID report returned! %d\n", len
);
205 /* copy raw report data */
206 memcpy( raw_data
, buffer
, len
);
209 return (unsigned char)buffer
[8]; /* byte of relay states */
213 static int rel_onoff( USBDEVHANDLE dev
, int is_on
, char const *numstr
)
215 unsigned char buffer
[10];
217 int relaynum
= numstr
? atoi(numstr
) : 0;
219 if ( numstr
&& (0 == strcasecmp(numstr
,"all")) ) {
220 char x
[2] = {'1', 0};
222 for (i
= 1; i
<= g_max_relay_num
; i
++) {
223 x
[0] = (char)('0' + i
);
224 err
= rel_onoff(dev
, is_on
, x
);
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
);
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
));
245 // Read back & verify
246 err
= rel_read_status_raw(dev
, NULL
);
248 err
= (err
>> (unsigned)(relaynum
-1)) & 1;
253 printerr("Error: failed to set relay %u %s\n", relaynum
, is_on
? "ON":"OFF");
261 static int show_status(USBDEVHANDLE dev
)
265 static const char* on_off
[] = {"OFF","ON"};
267 #define onoff(n) on_off[!!(err & (1U << n))]
269 err
= rel_read_status_raw(dev
, buffer
);
271 printerr("Error reading data: %s\n", usbErrorMessage(err
));
274 switch (g_max_relay_num
) {
276 printf("Board ID=[%5.5s] State: R1=%s\n", &buffer
[1], onoff(0) );
279 printf("Board ID=[%5.5s] State: R1=%s R2=%s\n",
280 &buffer
[1], onoff(0), onoff(1) );
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) );
286 default: /* print as bit mask */
287 printf("Board ID=[%5.5s] State: %2.2X (hex)\n", &buffer
[1], (unsigned char)err
);
296 // Enumerate available relay devices
298 static int showFunc(USBDEVHANDLE dev
, void *context
)
300 int err
= enumFunc( dev
, context
);
301 if (err
!= 0 || g_enumCtx
.mydev
== 0) // not my device, continue
304 show_status(g_enumCtx
.mydev
);
305 usbhidCloseDevice(g_enumCtx
.mydev
);
308 return 1; // continue
311 static int show_relays(void)
316 err
= usbhidEnumDevices(USB_CFG_VENDOR_ID
, USB_CFG_DEVICE_ID
, &g_enumCtx
, showFunc
);
319 printerr("Error finding USB relays: %s\n", usbErrorMessage(err
));
327 int main(int argc
, char **argv
)
329 USBDEVHANDLE dev
= 0;
331 char const *arg1
= (argc
>= 2) ? argv
[1] : NULL
;
332 char const *arg2
= (argc
>= 3) ? argv
[2] : NULL
;
339 if ( strcasecmp(arg1
, "enum") == 0 ) {
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
);
350 strcpy( g_enumCtx
.id
, &arg1
[3]);
352 // shift following params
354 arg2
= (argc
>= 4) ? argv
[3] : NULL
;
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
);
374 usbhidCloseDevice(dev
);
380 /* ------------------------------------------------------------------------- */