6629800a |
1 | // Chinese USB/HID relay command line tool: |
2 | // |
1c892392 |
3 | // pa02 19-Nov-2014 supports 1,2,4 - relay devices |
6629800a |
4 | // Currently finds the 1st matching device by ven,dev, product name string. |
5 | // TODO: |
1c892392 |
6 | // - Support multiple devices, select one by ID! |
aed7e4dc |
7 | // Build for Windows: using VC++ 2008 and WDK7.1 |
6629800a |
8 | //~~~~~~~~~~~~~~~~~~~~~~~~ |
9 | |
10 | /* Prototype: V-USB example: vusb-20121206/examples/hid-data/commandline/hidtool.c |
11 | * Author: Christian Starkjohann |
12 | * Creation Date: 2008-04-11 |
13 | * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH |
14 | */ |
15 | |
1c892392 |
16 | #define A_VER_STR "r1.1x (1 device only)" |
6629800a |
17 | |
18 | #include <stdio.h> |
19 | #include <string.h> |
20 | #include <stdlib.h> |
21 | #include "hidusb-tool.h" |
22 | |
1c892392 |
23 | #define USB_RELAY_VENDOR_NAME "www.dcttech.com" |
24 | #define USB_RELAY1_NAME "USBRelay1" // 1 relay |
25 | #define USB_RELAY2_NAME "USBRelay2" // 2 relays |
26 | #define USB_RELAY4_NAME "USBRelay4" // 4 relays |
6629800a |
27 | |
28 | |
29 | static int g_max_relay_num = 0; |
30 | |
1c892392 |
31 | static int rel_read_status_raw(USBDEVHANDLE dev, void *raw_data); |
6629800a |
32 | |
33 | /* ------------------------------------------------------------------------- */ |
34 | |
35 | static const char *usbErrorMessage(int errCode) |
36 | { |
37 | static char buffer[80]; |
38 | |
1c892392 |
39 | switch (errCode) { |
6629800a |
40 | case USBOPEN_ERR_ACCESS: return "Access to device denied"; |
41 | case USBOPEN_ERR_NOTFOUND: return "The specified device was not found"; |
42 | case USBOPEN_ERR_IO: return "Communication error with device"; |
6629800a |
43 | } |
1c892392 |
44 | |
45 | snprintf(buffer, sizeof(buffer), "Unknown USB error %d", errCode); |
46 | return buffer; |
6629800a |
47 | } |
48 | |
49 | static USBDEVHANDLE openDevice(void) |
50 | { |
51 | USBDEVHANDLE dev = 0; |
1c892392 |
52 | char vendorName[] = USB_RELAY_VENDOR_NAME, |
53 | productName[] = USB_RELAY1_NAME; |
6629800a |
54 | int vid = USB_CFG_VENDOR_ID; |
55 | int pid = USB_CFG_DEVICE_ID; |
56 | int err; |
1c892392 |
57 | int num = 0; |
6629800a |
58 | |
59 | // TODO: enumerate all instances, then filter by unique ID |
1c892392 |
60 | //$$$ find any one device |
61 | strcpy(productName, USB_RELAY2_NAME); |
62 | if((err = usbhidOpenDevice(&dev, vid, vendorName, pid, productName, 0)) == 0) { |
63 | num = 2; |
64 | goto check1; |
65 | } |
66 | |
67 | strcpy(productName, USB_RELAY1_NAME); |
68 | if((err = usbhidOpenDevice(&dev, vid, vendorName, pid, productName, 0)) == 0) { |
69 | num = 1; |
70 | goto check1; |
71 | } |
72 | |
73 | strcpy(productName, USB_RELAY4_NAME); |
74 | if((err = usbhidOpenDevice(&dev, vid, vendorName, pid, productName, 0)) == 0) { |
75 | num = 4; |
76 | goto check1; |
6629800a |
77 | } |
78 | |
1c892392 |
79 | fprintf(stderr, "error finding USB relay: %s\n", usbErrorMessage(err)); |
80 | return NULL; |
81 | |
82 | check1: |
6629800a |
83 | { /* Check the unique ID: 5 bytes at offs 0 */ |
84 | char buffer[16]; |
1c892392 |
85 | err = rel_read_status_raw(dev, buffer); |
6629800a |
86 | if( err < 0 ){ |
87 | fprintf(stderr, "error reading report 0: %s\n", usbErrorMessage(err)); |
88 | usbhidCloseDevice(dev); |
89 | return NULL; |
90 | }else{ |
91 | int i; |
92 | //hexdump(buffer + 1, sizeof(buffer) - 1); |
93 | for (i=1; i <=5; i++) { |
94 | unsigned char x = (unsigned char)buffer[i]; |
95 | if (x <= 0x20 || x >= 0x7F) { |
96 | fprintf(stderr, "Bad device ID!\n"); |
97 | usbhidCloseDevice(dev); |
98 | return NULL; |
99 | } |
100 | } |
101 | if( buffer[6] != 0 ) { |
102 | fprintf(stderr, "Bad device ID!\n"); |
103 | usbhidCloseDevice(dev); |
104 | return NULL; |
105 | } |
106 | |
107 | DEBUG_PRINT(("Device %s found: ID=[%5s]\n", USB_CFG_DEVICE_NAME, &buffer[1])); |
1c892392 |
108 | g_max_relay_num = num; |
6629800a |
109 | } |
110 | } |
111 | |
112 | return dev; |
113 | } |
114 | |
115 | /* ------------------------------------------------------------------------- */ |
116 | #if 0 |
117 | static void hexdump(char *buffer, int len) |
118 | { |
119 | int i; |
120 | FILE *fp = stdout; |
121 | |
122 | for(i = 0; i < len; i++){ |
123 | if(i != 0){ |
124 | if(i % 16 == 0){ |
125 | fprintf(fp, "\n"); |
126 | }else{ |
127 | fprintf(fp, " "); |
128 | } |
129 | } |
130 | fprintf(fp, "0x%02x", buffer[i] & 0xff); |
131 | } |
132 | if(i != 0) |
133 | fprintf(fp, "\n"); |
134 | } |
135 | #endif |
136 | |
137 | /* ------------------------------------------------------------------------- */ |
138 | |
139 | static void usage(char *myName) |
140 | { |
141 | char *p = strrchr(myName, '\\'); /* windows */ |
142 | if (p) myName = p + 1; |
1c892392 |
143 | else p = strrchr(myName, '/'); /* whatever */ |
6629800a |
144 | if (p) myName = p + 1; |
145 | |
146 | fprintf(stderr, "USBHID relay utility, " A_VER_STR "\n"); |
147 | fprintf(stderr, "Usage:\n"); |
148 | fprintf(stderr, " %s on <num> - turn relay <num> ON\n", myName); |
149 | fprintf(stderr, " %s off <num> - turn relay <num> OFF\n", myName); |
150 | fprintf(stderr, " %s state - print state of the relays\n", myName); |
151 | } |
152 | |
153 | |
154 | // Read state of all relays |
155 | // @return bit mask of all relays (R1->bit 0, R2->bit 1 ...) or -1 on error |
1c892392 |
156 | static int rel_read_status_raw(USBDEVHANDLE dev, void *raw_data) |
6629800a |
157 | { |
158 | char buffer[10]; |
159 | int err; |
160 | int reportnum = 0; |
161 | int len = 8 + 1; /* report id 1 byte + 8 bytes data */ |
162 | memset(buffer, 0, sizeof(buffer)); |
163 | |
164 | err = usbhidGetReport(dev, reportnum, buffer, &len); |
165 | if ( err ) { |
166 | fprintf(stderr, "error reading status: %s\n", usbErrorMessage(err)); |
167 | return -1; |
168 | } |
169 | |
170 | if ( len != 9 || buffer[0] != reportnum ) { |
171 | fprintf(stderr, "ERROR: wrong HID report returned!\n", len); |
172 | return -2; |
173 | } |
174 | |
175 | if (raw_data) { |
1c892392 |
176 | /* copy raw report data */ |
6629800a |
177 | memcpy( raw_data, buffer, sizeof(buffer) ); |
178 | } |
179 | |
180 | return (int)(unsigned char)buffer[8]; /* byte of relay states */ |
181 | } |
182 | |
183 | |
184 | static int rel_onoff( USBDEVHANDLE dev, int is_on, char const *numstr ) |
185 | { |
186 | unsigned char buffer[10]; |
187 | int err = -1; |
188 | int relaynum = numstr ? atoi(numstr) : 0; |
189 | |
190 | if ( numstr && (0 == strcmp(numstr,"*")) ) { |
191 | char x[2] = {'1', 0}; |
192 | int i; |
193 | for (i = 1; i <= g_max_relay_num; i++) { |
194 | x[0] = (char)('0' + i); |
195 | err = rel_onoff(dev, is_on, x); |
196 | if (err) break; |
197 | } |
198 | return err; |
199 | } |
200 | |
201 | if ( relaynum <= 0 || relaynum > g_max_relay_num ) { |
202 | fprintf(stderr, "Invalid relay number. Must be 1-%d or * (all)\n", g_max_relay_num); |
203 | return 1; |
204 | } |
205 | |
206 | memset(buffer, 0, sizeof(buffer)); |
207 | buffer[0] = 0; /* report # */ |
208 | buffer[1] = is_on ? 0xFF : 0xFD; |
209 | buffer[2] = (unsigned char)relaynum; |
210 | if((err = usbhidSetReport(dev, buffer, 9)) != 0) { |
211 | fprintf(stderr, "Error writing data: %s\n", usbErrorMessage(err)); |
212 | return 1; |
213 | } |
214 | |
215 | // Read back & verify |
1c892392 |
216 | err = rel_read_status_raw(dev, NULL); |
6629800a |
217 | if ( err >= 0 ) { |
218 | err = (err >> (unsigned)(relaynum -1)) & 1; |
219 | err ^= !!is_on; |
220 | } |
221 | |
222 | if ( err ) { |
223 | fprintf(stderr, "Error: failed set %s relay %u\n", is_on ? "ON":"OFF", relaynum); |
224 | return 1; |
225 | } |
226 | |
227 | return 0; |
228 | } |
229 | |
230 | |
1c892392 |
231 | static int show_status(USBDEVHANDLE dev) |
232 | { |
233 | int err; |
234 | char buffer[10]; |
235 | static const char* on_off[] = {"OFF","ON"}; |
236 | |
237 | #define onoff(n) on_off[!!(err & (1U << n))] |
238 | |
239 | err = rel_read_status_raw(dev, buffer); |
240 | if ( err < 0 ){ |
241 | fprintf(stderr, "error reading data: %s\n", usbErrorMessage(err)); |
242 | err = 1; |
243 | } else { |
244 | //hexdump(buffer + 1, len - 1); |
245 | switch (g_max_relay_num) { |
246 | case 1: |
247 | printf("Board ID=[%5.5s] State: R1=%s\n", &buffer[1], onoff(0) ); |
248 | break; |
249 | case 2: |
250 | printf("Board ID=[%5.5s] State: R1=%s R2=%s\n", |
251 | &buffer[1], onoff(0), onoff(1) ); |
252 | break; |
253 | case 4: |
254 | printf("Board ID=[%5.5s] State: R1=%s R3=%s R1=%s R4=%s\n", |
255 | &buffer[1], onoff(0), onoff(1), onoff(2), onoff(3) ); |
256 | break; |
257 | default: |
258 | printf("Board ID=[%5.5s] State: %2.2X (hex)\n", &buffer[1], (unsigned char)err ); |
259 | break; |
260 | } |
261 | err = 0; |
262 | } |
263 | return err; |
264 | #undef onoff |
265 | } |
266 | |
6629800a |
267 | int main(int argc, char **argv) |
268 | { |
1c892392 |
269 | USBDEVHANDLE dev = 0; |
6629800a |
270 | int err; |
271 | char const *arg1 = (argc >= 2) ? argv[1] : NULL; |
272 | char const *arg2 = (argc >= 3) ? argv[2] : NULL; |
273 | |
274 | if ( !arg1 ) { |
275 | usage(argv[0]); |
276 | exit(1); |
277 | } |
278 | |
279 | dev = openDevice(); |
280 | if ( !dev ) |
281 | exit(1); |
282 | |
1c892392 |
283 | if ( strncasecmp(arg1, "stat", 4) == 0 ) { // stat|state|status |
284 | err = show_status(dev); |
285 | // TODO enumerate all devices |
286 | }else if( strcasecmp(arg1, "on" ) == 0) { |
6629800a |
287 | err = rel_onoff(dev, 1, arg2); |
1c892392 |
288 | }else if( strcasecmp(arg1, "off" ) == 0) { |
6629800a |
289 | err = rel_onoff(dev, 0, arg2); |
290 | }else { |
291 | usage(argv[0]); |
292 | err = 2; |
293 | } |
294 | |
295 | if ( dev ) { |
296 | usbhidCloseDevice(dev); |
297 | } |
298 | |
299 | return err; |
300 | } |
301 | |
302 | /* ------------------------------------------------------------------------- */ |