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 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
;
289 /* Continue search */
290 usbhidCloseDevice(usbh
);
294 // Enum function for open one device by ID
296 int enumOpenfunc(USBDEVHANDLE usbh
, void *context
)
298 // static const char vendorName[] = USB_RELAY_VENDOR_NAME;
299 static const char productName
[] = USB_RELAY_NAME_PREF
;
301 char buffer
[128*sizeof(short)]; // max USB string is 128 UTF-16 chars
304 struct enumctx_s
*ectx
= (struct enumctx_s
*)context
;
305 struct usbrelay_internal_s
*q
= ectx
->head
;
307 //NOTE: Ignore vendor string. This is against ObjDev rules, restore the check if needed!
309 err
= usbhidGetProductString(usbh
, buffer
, sizeof(buffer
));
315 i
= (int)strlen(buffer
);
316 if ( i
!= strlen(productName
) + 1 )
321 /* the last char of ProductString is number of relays */
322 num
= (int)(buffer
[i
- 1]) - (int)'0';
325 if ( 0 != strcmp( buffer
, productName
) )
330 if ( num
<= 0 || num
> 8 )
332 dbgprintf("Unknown relay device? num relays=%d\n", num
);
336 /* Check the unique ID: USB_RELAY_ID_STR_LEN bytes at offset 1 (just after the report id) */
337 err
= rel_read_status_raw(usbh
, buffer
);
340 dbgprintf("Error reading report 0: %s\n", usbErrorMessage(err
));
344 for (i
= 1; i
<= USB_RELAY_ID_STR_LEN
; i
++)
346 unsigned char x
= (unsigned char)buffer
[i
];
347 if (x
<= 0x20 || x
>= 0x7F)
349 dbgprintf("Bad usbrelay ID string!\n");
354 if( buffer
[USB_RELAY_ID_STR_LEN
+ 1] != 0 )
356 dbgprintf("Bad usbrelay ID string!\n");
360 dbgprintf("Device %s%d found: ID=[%5s]\n", productName
, num
, &buffer
[1]);
362 if ( 0 == memcmp( q
->idstr
, &buffer
[1], USB_RELAY_ID_STR_LEN
) ) {
364 q
->urdi
.type
= num
; // enum = number of relays
365 q
->urdi
.serial_number
= &q
->idstr
[0];
366 q
->urdi
.device_path
= (char*)g_dummyPath
;
372 /* Continue search */
373 usbhidCloseDevice(usbh
);
377 /** Enumerate the USB Relay Devices.*/
378 pusb_relay_device_info_t USBRL_API
usb_relay_device_enumerate(void)
380 struct enumctx_s ectx
;
382 memset(&ectx
, 0, sizeof(ectx
));
383 ret
= usbhidEnumDevices(USB_CFG_VENDOR_ID
, USB_CFG_DEVICE_ID
,
387 return (pusb_relay_device_info_t
)ectx
.head
;
391 /** Free an enumeration Linked List*/
392 void USBRL_API
usb_relay_device_free_enumerate(struct usb_relay_device_info
*dilist
)
394 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)dilist
;
397 struct usbrelay_internal_s
*q
= (struct usbrelay_internal_s
*)((pusb_relay_device_info_t
)p
)->next
;
398 if (p
->usbh
&& ((USBDEVHANDLE
)(-1)) != p
->usbh
) {
399 usbhidCloseDevice(p
->usbh
);
409 /** Open device by serial number
410 serial_number == NULL is valid and means any one device.
411 @return: This function returns a valid handle to the device on success or NULL on failure.
412 Example: usb_relay_device_open_with_serial_number("abcde", 5) */
413 intptr_t USBRL_API
usb_relay_device_open_with_serial_number(const char *serial_number
, unsigned len
)
415 struct enumctx_s ectx
;
417 struct usbrelay_internal_s
*q
;
418 memset(&ectx
, 0, sizeof(ectx
));
420 if (serial_number
&& len
!= USB_RELAY_ID_STR_LEN
) {
421 printerr("Specified invalid str id length: %u", len
);
425 q
= ectx
.head
= calloc(1, sizeof(*ectx
.head
));
429 memcpy(q
->idstr
, serial_number
, len
);
431 ret
= usbhidEnumDevices(USB_CFG_VENDOR_ID
, USB_CFG_DEVICE_ID
,
435 goto ret_err
; // error during enum
437 if (ectx
.numdevs
== 0 || q
->usbh
== 0) {
438 goto ret_err
; // not found
441 q
->urdi
.next
= (void*)q
; // mark this element as standalone
449 /** Open a USB relay device
450 @return: This function returns a valid handle to the device on success or NULL on failure.
452 intptr_t USBRL_API
usb_relay_device_open(struct usb_relay_device_info
*device_info
)
454 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)device_info
;
457 if ( (uintptr_t)p
->usbh
== 0 || (uintptr_t)p
->usbh
== (uintptr_t)-1 )
460 return (uintptr_t)device_info
;
463 /** Close a USB relay device*/
464 void USBRL_API
usb_relay_device_close(intptr_t hHandle
)
466 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
467 if ( 0 == hHandle
|| ((intptr_t)-1) == hHandle
)
470 if ( (void*)(p
->urdi
.next
) == (void*)p
) {
471 // This was made by usb_relay_device_open_with_serial_number() so free it now:
472 if ( p
->usbh
&& ((intptr_t)-1) != (intptr_t)(p
->usbh
)) {
473 usbhidCloseDevice(p
->usbh
);
479 // Else this can be in the list, don't do anything.
482 /** Turn ON a relay channel on the USB-Relay-Device
483 @param index -- which channel your want to open
484 @param hHandle -- which usb relay device your want to operate
485 @returns: 0 -- success; 1 -- error; 2 -- index is outnumber the number of the usb relay device
487 int USBRL_API
usb_relay_device_open_one_relay_channel(intptr_t hHandle
, int index
)
489 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
492 if ( index
<= 0 || index
> (int)p
->urdi
.type
)
494 return rel_onoff( p
->usbh
, 1, index
);
497 /** Turn ON all relay channels on the USB-Relay-Device
498 @param hHandle -- which usb relay device your want to operate
499 @returns: 0 -- success; 1 -- error
501 int USBRL_API
usb_relay_device_open_all_relay_channel(intptr_t hHandle
)
503 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
506 return rel_onoff( p
->usbh
, 1, -(int)p
->urdi
.type
);
509 /** Turn OFF a relay channel on the USB-Relay-Device
510 @param index -- which channel your want to close
511 @param hHandle -- which usb relay device your want to operate
512 @returns: 0 -- success; 1 -- error; 2 -- index is outnumber the number of the usb relay device
514 int USBRL_API
usb_relay_device_close_one_relay_channel(intptr_t hHandle
, int index
)
516 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
519 if ( index
<= 0 || index
> (int)p
->urdi
.type
)
521 return rel_onoff( p
->usbh
, 0, index
);
524 /** Turn OFF all relay channels on the USB-Relay-Device
525 @param hHandle -- which usb relay device your want to operate
526 @returns 0 -- success; 1 -- error
528 int USBRL_API
usb_relay_device_close_all_relay_channel(intptr_t hHandle
)
530 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
533 return rel_onoff( p
->usbh
, 0, -(int)p
->urdi
.type
);
536 /** Get status of all relays on the device
537 Status bits: one bit indicate a relay status.
538 bit 0/1/2/3/4/5/6/7/8 indicate channel 1/2/3/4/5/6/7/8 status
539 1 -- means ON, 0 -- means OFF.
540 @returns: 0 -- success; 1 -- error
542 int USBRL_API
usb_relay_device_get_status(intptr_t hHandle
, unsigned int *status
)
544 struct usbrelay_internal_s
*p
= (struct usbrelay_internal_s
*)hHandle
;
548 err
= rel_read_status_raw(p
->usbh
, NULL
);
550 printerr("Error reading data: %s\n", usbErrorMessage(err
));
554 *status
= (unsigned char)err
;
558 /************ Added ****************/
560 /** Get the library (dll) version
561 @return Lower 16 bits: the library version. Higher bits: undefined, ignore.
562 @note The original DLL does not have this function!
564 int USBRL_API
usb_relay_device_lib_version(void)
566 return (int)(MY_VERSION
);
570 The following functions are for non-native callers, to avoid fumbling with C structs.
571 Native C/C++ callers do not need to use these.
572 The ptr_usb_relay_device_info arg is pointer to struct usb_relay_device_info, cast to intptr_t, void*, etc.
575 /* Return next info struct pointer in the list returned by usb_relay_device_enumerate() */
576 intptr_t USBRL_API
usb_relay_device_next_dev(intptr_t ptr_usb_relay_device_info
)
578 if (!ptr_usb_relay_device_info
)
580 return (intptr_t)(void*)((pusb_relay_device_info_t
)ptr_usb_relay_device_info
)->next
;
583 /* Get number of relay channels on the device */
584 int USBRL_API
usb_relay_device_get_num_relays(intptr_t ptr_usb_relay_device_info
)
586 if (!ptr_usb_relay_device_info
)
588 return (int)((pusb_relay_device_info_t
)ptr_usb_relay_device_info
)->type
;
591 /* Get the ID string of the device. Returns pointer to const C string (1-byte, 0-terminated) */
592 intptr_t USBRL_API
usb_relay_device_get_id_string(intptr_t ptr_usb_relay_device_info
)
594 if (!ptr_usb_relay_device_info
)
596 return (intptr_t)(void const *)((pusb_relay_device_info_t
)ptr_usb_relay_device_info
)->serial_number
;
599 /* Get status of all relays on the device.
600 * @return Bitmask of all relay channels state, if the value > 0. Negative values mean error.
601 bit 0/1/2/3/4/5/6/7/8 indicate channel 1/2/3/4/5/6/7/8 status
602 Each bit value 1 means ON, 0 means OFF.
603 * @note This is same as usb_relay_device_get_status, but without dereferencing pointers.
605 int USBRL_API
usb_relay_device_get_status_bitmap(intptr_t hHandle
)
608 int err
= usb_relay_device_get_status(hHandle
, &st
);
611 return (err
> 0) ? (-err
) : err
;