2 * USB HID relays API library
5 * This is reconstruction of the original Windows DLL,
6 * as supplied by the USB relay vendors.
7 * It is binary compatible and works with their example programs.
8 * The original usb_relay_device.h file has been slightly hacked up.
10 * 12-jan-2015 pa01 Win32 version
13 #define MY_VERSION 0x02
15 #if defined (WIN32) || defined (_WIN32)
17 // Windows 32 or 64 bit
18 #include "targetver.h"
19 #define WIN32_EXTRALEAN
23 /* The original DLL has cdecl calling convention */
24 #define USBRL_CALL __cdecl
25 #define USBRL_API __declspec(dllexport) USBRL_CALL
27 #if _MSC_VER < 1900 /* before VS2015 */
28 #define snprintf _snprintf
33 #include "usb_relay_device.h"
35 #if USBRELAY_LIB_VER != MY_VERSION
36 #error "Oops. Wrong version of usb_relay_device.h"
39 #include "usb_relay_hw.h"
45 //#define dbgprintf(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
46 //#define dbgprintf(fmt, ...) printf(fmt, ## __VA_ARGS__)
47 #define dbgprintf(fmt, ...) //__noop(fmt, __VA_ARGS__)
48 //#define printerr(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
49 //#define printerr(fmt, ...) printf(fmt, ## __VA_ARGS__)
50 #define printerr(fmt, ...) // __noop(fmt, __VA_ARGS__)
56 struct usbrelay_internal_s
{
57 struct usb_relay_device_info urdi
; //public part
59 USBDEVHANDLE usbh
; // handle
63 // struct for enum context
65 struct usbrelay_internal_s
*head
, *tail
;
72 static const char *g_dummyPath
= "NOTHING"; // passing dev.path to client not implemented, I return this as path.
74 static const char *usbErrorMessage(int errCode
)
76 static char buffer
[80];
78 if ( errCode
!= USBHID_ERR_UNKNOWN
) {
79 usbhidStrerror_r(errCode
, buffer
, sizeof(buffer
));
81 if ( 0 == buffer
[0] ) {
82 snprintf(buffer
, sizeof(buffer
), "Unknown error (%d)", errCode
);
87 // Read state of all relays
88 // @return bit mask of all relays (R1->bit 0, R2->bit 1 ...) or -1 on error
89 static int rel_read_status_raw(USBDEVHANDLE dev
, void *raw_data
)
94 int len
= 8 + 1; /* report id 1 byte + 8 bytes data */
95 memset(buffer
, 0, sizeof(buffer
));
97 err
= usbhidGetReport(dev
, reportnum
, buffer
, &len
);
99 printerr("error reading status: %s\n", usbErrorMessage(err
));
103 if ( len
!= 9 || buffer
[0] != reportnum
) {
104 printerr("ERROR: wrong HID report returned! %d\n", len
);
109 /* copy raw report data */
110 memcpy( raw_data
, buffer
, len
);
113 return (unsigned char)buffer
[8]; /* byte of relay states */
116 // Turn relay on/off.
117 // @param relaynum: positive (1-N): one relay index, negative: all, -num = number of relays
118 // @returns 0 ok, else error
119 static int rel_onoff( USBDEVHANDLE dev
, int is_on
, int relaynum
)
121 unsigned char buffer
[10];
123 unsigned char cmd1
, cmd2
, mask
, maskval
;
125 if ( relaynum
< 0 && (-relaynum
) <= 8 ) {
130 maskval
= (unsigned char)( (1U << (-relaynum
)) - 1 );
136 if ( relaynum
<= 0 || relaynum
> 8 ) {
137 printerr("Relay number must be 1-8\n");
140 mask
= (unsigned char)(1U << (relaynum
-1));
141 cmd2
= (unsigned char)relaynum
;
151 memset(buffer
, 0, sizeof(buffer
));
152 buffer
[0] = 0; /* report # */
155 if((err
= usbhidSetReport(dev
, (void*)buffer
, 9)) != 0) {
156 printerr("Error writing data: %s\n", usbErrorMessage(err
));
160 // Read back & verify
161 err
= rel_read_status_raw(dev
, NULL
);
163 printerr("Error read back: %s\n", usbErrorMessage(err
));
168 if (err
!= maskval
) {
169 printerr("Error: failed to set relay %u %s\n", relaynum
, is_on
? "ON":"OFF");
179 /** Initialize the USB Relay Library
180 @returns: This function returns 0 on success and -1 on error.
182 int USBRL_API
usb_relay_init(void)
187 /** Finalize the USB Relay Library.
188 This function frees all of the static data associated with USB Relay Library.
189 It should be called at the end of execution to avoid memory leaks.
190 @returns: This function returns 0 on success and -1 on error.
192 int USBRL_API
usb_relay_exit(void)
197 // Enum function for building list of devices
199 int enumfunc(USBDEVHANDLE usbh
, void *context
)
201 // static const char vendorName[] = USB_RELAY_VENDOR_NAME;
202 static const char productName
[] = USB_RELAY_NAME_PREF
;
204 char buffer
[128*sizeof(short)]; // max USB string is 128 UTF-16 chars
207 struct usbrelay_internal_s
*q
;
208 struct enumctx_s
*ectx
= (struct enumctx_s
*)context
;
210 //NOTE: Ignore vendor string. This is against ObjDev rules, restore the check if needed!
212 err
= usbhidGetProductString(usbh
, buffer
, sizeof(buffer
));
218 i
= (int)strlen(buffer
);
219 if ( i
!= strlen(productName
) + 1 )
224 /* the last char of ProductString is number of relays */
225 num
= (int)(buffer
[i
- 1]) - (int)'0';
228 if ( 0 != strcmp( buffer
, productName
) )
233 if ( num
<= 0 || num
> 8 )
235 dbgprintf("Unknown relay device? num relays=%d\n", num
);
239 /* Check the unique ID: USB_RELAY_ID_STR_LEN bytes at offset 1 (just after the report id) */
240 err
= rel_read_status_raw(usbh
, buffer
);
243 dbgprintf("Error reading report 0: %s\n", usbErrorMessage(err
));
247 for (i
= 1; i
<= USB_RELAY_ID_STR_LEN
; i
++)
249 unsigned char x
= (unsigned char)buffer
[i
];
250 if (x
<= 0x20 || x
>= 0x7F)
252 dbgprintf("Bad usbrelay ID string!\n");
257 if( buffer
[USB_RELAY_ID_STR_LEN
+ 1] != 0 )
259 dbgprintf("Bad usbrelay ID string!\n");
263 dbgprintf("Device %s%d found: ID=[%5s]\n", productName
, num
, &buffer
[1]);
265 // allocate & save info
266 q
= (struct usbrelay_internal_s
*)calloc(1, sizeof(struct usbrelay_internal_s
));
268 dbgprintf("Malloc err\n");
269 goto next
; //$$$ revise
271 /* keep this device, continue */
273 memcpy(q
->idstr
, &buffer
[1], USB_RELAY_ID_STR_LEN
);
274 q
->urdi
.type
= num
; // enum = number of relays
275 q
->urdi
.serial_number
= &q
->idstr
[0];
276 q
->urdi
.device_path
= (char*)g_dummyPath
;
282 ectx
->tail
->urdi
.next
= (pusb_relay_device_info_t
)q
;
290 /* Continue search */
291 usbhidCloseDevice(usbh
);
295 // Enum function for open one device by ID
297 int enumOpenfunc(USBDEVHANDLE usbh
, void *context
)
299 // static const char vendorName[] = USB_RELAY_VENDOR_NAME;
300 static const char productName
[] = USB_RELAY_NAME_PREF
;
302 char buffer
[128*sizeof(short)]; // max USB string is 128 UTF-16 chars
305 struct enumctx_s
*ectx
= (struct enumctx_s
*)context
;
306 struct usbrelay_internal_s
*q
= ectx
->head
;
308 //NOTE: Ignore vendor string. This is against ObjDev rules, restore the check if needed!
310 err
= usbhidGetProductString(usbh
, buffer
, sizeof(buffer
));
316 i
= (int)strlen(buffer
);
317 if ( i
!= strlen(productName
) + 1 )
322 /* the last char of ProductString is number of relays */
323 num
= (int)(buffer
[i
- 1]) - (int)'0';
326 if ( 0 != strcmp( buffer
, productName
) )
331 if ( num
<= 0 || num
> 8 )
333 dbgprintf("Unknown relay device? num relays=%d\n", num
);
337 /* Check the unique ID: USB_RELAY_ID_STR_LEN bytes at offset 1 (just after the report id) */
338 err
= rel_read_status_raw(usbh
, buffer
);
341 dbgprintf("Error reading report 0: %s\n", usbErrorMessage(err
));
345 for (i
= 1; i
<= USB_RELAY_ID_STR_LEN
; i
++)
347 unsigned char x
= (unsigned char)buffer
[i
];
348 if (x
<= 0x20 || x
>= 0x7F)
350 dbgprintf("Bad usbrelay ID string!\n");
355 if( buffer
[USB_RELAY_ID_STR_LEN
+ 1] != 0 )
357 dbgprintf("Bad usbrelay ID string!\n");
361 dbgprintf("Device %s%d found: ID=[%5s]\n", productName
, num
, &buffer
[1]);
363 if ( 0 == memcmp( q
->idstr
, &buffer
[1], USB_RELAY_ID_STR_LEN
) ) {
365 q
->urdi
.type
= num
; // enum = number of relays
366 q
->urdi
.serial_number
= &q
->idstr
[0];
367 q
->urdi
.device_path
= (char*)g_dummyPath
;
373 /* Continue search */
374 usbhidCloseDevice(usbh
);
378 /** Enumerate the USB Relay Devices.*/
379 pusb_relay_device_info_t USBRL_API
usb_relay_device_enumerate(void)
381 struct enumctx_s ectx
;
383 memset(&ectx
, 0, sizeof(ectx
));
384 ret
= usbhidEnumDevices(USB_CFG_VENDOR_ID
, USB_CFG_DEVICE_ID
,
388 return (pusb_relay_device_info_t
)ectx
.head
;
392 /** Free an enumeration Linked List*/
393 void USBRL_API
usb_relay_device_free_enumerate(struct usb_relay_device_info
*dilist
)
395 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)dilist
;
398 struct usbrelay_internal_s
*q
= (struct usbrelay_internal_s
*)((pusb_relay_device_info_t
)p
)->next
;
399 if (p
->usbh
&& ((USBDEVHANDLE
)(-1)) != p
->usbh
) {
400 usbhidCloseDevice(p
->usbh
);
410 /** Open device by serial number
411 serial_number == NULL is valid and means any one device.
412 @return: This function returns a valid handle to the device on success or NULL on failure.
413 Example: usb_relay_device_open_with_serial_number("abcde", 5) */
414 intptr_t USBRL_API
usb_relay_device_open_with_serial_number(const char *serial_number
, unsigned len
)
416 struct enumctx_s ectx
;
418 struct usbrelay_internal_s
*q
;
419 memset(&ectx
, 0, sizeof(ectx
));
421 if (serial_number
&& len
!= USB_RELAY_ID_STR_LEN
) {
422 printerr("Specified invalid str id length: %u", len
);
426 q
= ectx
.head
= calloc(1, sizeof(*ectx
.head
));
430 memcpy(q
->idstr
, serial_number
, len
);
432 ret
= usbhidEnumDevices(USB_CFG_VENDOR_ID
, USB_CFG_DEVICE_ID
,
436 goto ret_err
; // error during enum
438 if (ectx
.numdevs
== 0 || q
->usbh
== 0) {
439 goto ret_err
; // not found
442 q
->urdi
.next
= (void*)q
; // mark this element as standalone
450 /** Open a USB relay device
451 @return: This function returns a valid handle to the device on success or NULL on failure.
453 intptr_t USBRL_API
usb_relay_device_open(struct usb_relay_device_info
*device_info
)
455 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)device_info
;
458 if ( (uintptr_t)p
->usbh
== 0 || (uintptr_t)p
->usbh
== (uintptr_t)-1 )
461 return (uintptr_t)device_info
;
464 /** Close a USB relay device*/
465 void USBRL_API
usb_relay_device_close(intptr_t hHandle
)
467 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
468 if ( 0 == hHandle
|| ((intptr_t)-1) == hHandle
)
471 if ( (void*)(p
->urdi
.next
) == (void*)p
) {
472 // This was made by usb_relay_device_open_with_serial_number() so free it now:
473 if ( p
->usbh
&& ((intptr_t)-1) != (intptr_t)(p
->usbh
)) {
474 usbhidCloseDevice(p
->usbh
);
480 // Else this can be in the list, don't do anything.
483 /** Turn ON a relay channel on the USB-Relay-Device
484 @param index -- which channel your want to open
485 @param hHandle -- which usb relay device your want to operate
486 @returns: 0 -- success; 1 -- error; 2 -- index is outnumber the number of the usb relay device
488 int USBRL_API
usb_relay_device_open_one_relay_channel(intptr_t hHandle
, int index
)
490 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
493 if ( index
<= 0 || index
> (int)p
->urdi
.type
)
495 return rel_onoff( p
->usbh
, 1, index
);
498 /** Turn ON all relay channels on the USB-Relay-Device
499 @param hHandle -- which usb relay device your want to operate
500 @returns: 0 -- success; 1 -- error
502 int USBRL_API
usb_relay_device_open_all_relay_channel(intptr_t hHandle
)
504 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
507 return rel_onoff( p
->usbh
, 1, -(int)p
->urdi
.type
);
510 /** Turn OFF a relay channel on the USB-Relay-Device
511 @param index -- which channel your want to close
512 @param hHandle -- which usb relay device your want to operate
513 @returns: 0 -- success; 1 -- error; 2 -- index is outnumber the number of the usb relay device
515 int USBRL_API
usb_relay_device_close_one_relay_channel(intptr_t hHandle
, int index
)
517 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
520 if ( index
<= 0 || index
> (int)p
->urdi
.type
)
522 return rel_onoff( p
->usbh
, 0, index
);
525 /** Turn OFF all relay channels on the USB-Relay-Device
526 @param hHandle -- which usb relay device your want to operate
527 @returns 0 -- success; 1 -- error
529 int USBRL_API
usb_relay_device_close_all_relay_channel(intptr_t hHandle
)
531 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
534 return rel_onoff( p
->usbh
, 0, -(int)p
->urdi
.type
);
537 /** Get status of all relays on the device
538 Status bits: one bit indicate a relay status.
539 bit 0/1/2/3/4/5/6/7/8 indicate channel 1/2/3/4/5/6/7/8 status
540 1 -- means ON, 0 -- means OFF.
541 @returns: 0 -- success; 1 -- error
543 int USBRL_API
usb_relay_device_get_status(intptr_t hHandle
, unsigned int *status
)
545 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
549 err
= rel_read_status_raw(p
->usbh
, NULL
);
551 printerr("Error reading data: %s\n", usbErrorMessage(err
));
555 *status
= (unsigned char)err
;
559 /************ Added ****************/
561 /** Get the library (dll) version
562 @return Lower 16 bits: the library version. Higher bits: undefined, ignore.
563 @note The original DLL does not have this function!
565 int USBRL_API
usb_relay_device_lib_version(void)
567 return (int)(MY_VERSION
);
571 The following functions are for non-native callers, to avoid fumbling with C structs.
572 Native C/C++ callers do not need to use these.
573 The ptr_usb_relay_device_info arg is pointer to struct usb_relay_device_info, cast to intptr_t, void*, etc.
576 /* Return next info struct pointer in the list returned by usb_relay_device_enumerate() */
577 intptr_t USBRL_API
usb_relay_device_next_dev(intptr_t ptr_usb_relay_device_info
)
579 if (!ptr_usb_relay_device_info
)
581 return (intptr_t)(void*)((pusb_relay_device_info_t
)ptr_usb_relay_device_info
)->next
;
584 /* Get number of relay channels on the device */
585 int USBRL_API
usb_relay_device_get_num_relays(intptr_t ptr_usb_relay_device_info
)
587 if (!ptr_usb_relay_device_info
)
589 return (int)((pusb_relay_device_info_t
)ptr_usb_relay_device_info
)->type
;
592 /* Get the ID string of the device. Returns pointer to const C string (1-byte, 0-terminated) */
593 intptr_t USBRL_API
usb_relay_device_get_id_string(intptr_t ptr_usb_relay_device_info
)
595 if (!ptr_usb_relay_device_info
)
597 return (intptr_t)(void const *)((pusb_relay_device_info_t
)ptr_usb_relay_device_info
)->serial_number
;
600 /* Get status of all relays on the device.
601 * @return Bitmask of all relay channels state, if the value > 0. Negative values mean error.
602 bit 0/1/2/3/4/5/6/7/8 indicate channel 1/2/3/4/5/6/7/8 status
603 Each bit value 1 means ON, 0 means OFF.
604 * @note This is same as usb_relay_device_get_status, but without dereferencing pointers.
606 int USBRL_API
usb_relay_device_get_status_bitmap(intptr_t hHandle
)
609 int err
= usb_relay_device_get_status(hHandle
, &st
);
612 return (err
> 0) ? (-err
) : err
;