Added support for 1, 4-relay device (still only one)
[usb-relay-hid.git] / commandline / usbrelay-cmd.c
1 // Chinese USB/HID relay command line tool:
2 //
3 // pa02 19-Nov-2014 supports 1,2,4 - relay devices
4 // Currently finds the 1st matching device by ven,dev, product name string.
5 // TODO:
6 // - Support multiple devices, select one by ID!
7 // Build for Windows: using VC++ 2008 and WDK7.1
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
16 #define A_VER_STR "r1.1x (1 device only)"
17
18 #include <stdio.h>
19 #include <string.h>
20 #include <stdlib.h>
21 #include "hidusb-tool.h"
22
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
27
28
29 static int g_max_relay_num = 0;
30
31 static int rel_read_status_raw(USBDEVHANDLE dev, void *raw_data);
32
33 /* ------------------------------------------------------------------------- */
34
35 static const char *usbErrorMessage(int errCode)
36 {
37 static char buffer[80];
38
39 switch (errCode) {
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";
43 }
44
45 snprintf(buffer, sizeof(buffer), "Unknown USB error %d", errCode);
46 return buffer;
47 }
48
49 static USBDEVHANDLE openDevice(void)
50 {
51 USBDEVHANDLE dev = 0;
52 char vendorName[] = USB_RELAY_VENDOR_NAME,
53 productName[] = USB_RELAY1_NAME;
54 int vid = USB_CFG_VENDOR_ID;
55 int pid = USB_CFG_DEVICE_ID;
56 int err;
57 int num = 0;
58
59 // TODO: enumerate all instances, then filter by unique ID
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;
77 }
78
79 fprintf(stderr, "error finding USB relay: %s\n", usbErrorMessage(err));
80 return NULL;
81
82 check1:
83 { /* Check the unique ID: 5 bytes at offs 0 */
84 char buffer[16];
85 err = rel_read_status_raw(dev, buffer);
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]));
108 g_max_relay_num = num;
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;
143 else p = strrchr(myName, '/'); /* whatever */
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
156 static int rel_read_status_raw(USBDEVHANDLE dev, void *raw_data)
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) {
176 /* copy raw report data */
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
216 err = rel_read_status_raw(dev, NULL);
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
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
267 int main(int argc, char **argv)
268 {
269 USBDEVHANDLE dev = 0;
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
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) {
287 err = rel_onoff(dev, 1, arg2);
288 }else if( strcasecmp(arg1, "off" ) == 0) {
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 /* ------------------------------------------------------------------------- */