add commandline utility
authorpavel <pavel@aaa>
Tue, 14 Oct 2014 03:35:21 +0000 (06:35 +0300)
committerpavel <pavel@aaa>
Tue, 14 Oct 2014 03:42:48 +0000 (06:42 +0300)
commandline/Makefile [new file with mode: 0644]
commandline/README-hidusb-relay-cmd.txt [new file with mode: 0644]
commandline/hiddata.c [new file with mode: 0644]
commandline/hiddata.h [new file with mode: 0644]
commandline/hidusb-demo.sln [new file with mode: 0644]
commandline/hidusb-relay-cmd(vc2008).vcproj [new file with mode: 0644]
commandline/hidusb-tool.h [new file with mode: 0644]
commandline/relay1-pulse.sh [new file with mode: 0644]
commandline/targetver.h [new file with mode: 0644]
commandline/usbrelay-cmd.c [new file with mode: 0644]
doc/hidrelay.md [new file with mode: 0644]

diff --git a/commandline/Makefile b/commandline/Makefile
new file mode 100644 (file)
index 0000000..53a3e02
--- /dev/null
@@ -0,0 +1,44 @@
+# Makefile for HID USB relay utility, usbrelay-cmd
+# pa01 16-apr-2014
+#
+# prototype:
+# Author: Christian Starkjohann
+# Creation Date: 2008-04-11
+# Tabsize: 4
+# Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+# License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
+
+
+# For Unix/Linux, Mac OS X:
+USBFLAGS=   `libusb-config --cflags`
+USBLIBS=    `libusb-config --libs`
+EXE_SUFFIX=
+
+# Use the following 3 lines on Windows and comment out the 3 above:
+#USBFLAGS=
+#USBLIBS=    -lhid -lusb -lsetupapi
+#EXE_SUFFIX= .exe
+
+#+pa GCC on my ubuntu won't pick this dir by default ?!
+#USBFLAGS+=-I/usr/include
+
+CC=            gcc
+CFLAGS=                -O -Wall $(USBFLAGS)
+LIBS=          $(USBLIBS)
+
+OBJ=           usbrelay-cmd.o hiddata.o
+PROGRAM=       usbrelay-cmd$(EXE_SUFFIX)
+
+all: $(PROGRAM)
+
+$(PROGRAM): $(OBJ)
+       $(CC) -o $(PROGRAM) $(OBJ) $(LIBS)
+
+strip: $(PROGRAM)
+       strip $(PROGRAM)
+
+clean:
+       rm -f $(OBJ) $(PROGRAM)
+
+.c.o:
+       $(CC) $(ARCH_COMPILE) $(CFLAGS) -c $*.c -o $*.o
diff --git a/commandline/README-hidusb-relay-cmd.txt b/commandline/README-hidusb-relay-cmd.txt
new file mode 100644 (file)
index 0000000..e8115b7
--- /dev/null
@@ -0,0 +1,70 @@
+Command line utility for Chinese USB HID relays
+(USB 1.1 HID devices, VEN=5824 (0x16c0) DEV=1503 (0x05DF), based on the V-USB project)
+
+Rev. 1.0 (16-apr-2014)
+
+Usage:
+  usbrelay-cmd ON|OFF <num>   -- turn the relay ON or OFF
+  usbrelay-cmd STATE          -- print state of relays and the "unique ID"
+  
+  where <num> is the relay number: 1-2 or "*" for all
+
+Each relay has two contact pairs: Normally Open and Normally Closed.
+The OFF state is the "normal" state, when the red LED is off, and the Normally Open contacts are disconnected.
+The ON state is when the red LED is on, and the Normally Open contacts are connected.
+
+Note: In the original s/w readme, "Open" means ON, "Close" means OFF.
+  
+Currently tested only with the 2-relay device.
+Finds the FIRST AVAILABLE RELAY device! Does not address by the "unique ID" yet!
+
+SOURCE based on a V-USB HID example.
+LICENSE: TBD!
+         Should be non-GPL; rewrite all prototype code!
+
+
+For Windows XP and later:
+ - using the in-box hid.dll and WDK 7.1.
+ - build with VC++ 2008
+
+For Linux (PC, x86):
+ - using the old libusb (v 0.1; usb.h)
+ - tested on Centos 5, Ubuntu 12.04
+ - requires root!
+   To grant access to users, define udev rule for this device (or all HID). Example:
+   SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="05df", MODE:="0666" 
+>>> Target for 1st public release:
+================================
+1. Remove GPL encumberment
+2. Make a .so for Linux (Windows already has orig DLL)
+
+Other TODO's
+============
+TODO: support multiple devices, support one-relay and other existing variants
+  usbrelay-cmd -SN=XXXXX ON|OFF <num>
+  usbrelay-cmd -SN=XXXXX  STATE       
+    or -s XXXXX or --SN or -ID ...
+    
+ *** Check that these devices indeed have unique IDs!
+  The orig. h file mentions  function:   usb_relay_device_set_serial(int hHandle, char serial[5]);
+  which may be used by mfg to burn the ID? but the orig. DLL does not export it.
+  In detection code (find by "unique ID"), check only by product name, Ignore vendor string?
+
+TODO: Make a .so callable from Java, Python etc.
+      For this, move to libusb 1.x (libusb.h)? 
+TODO: provide the original API "usb_relay_device.h" API, compatible with the orig. usb_relay_device.dll
+      The orig .h file and example are in orig dir (slightly adapted and updated to VC2008)  
+     In orig .h file relay state "open" means ON (the red LED is on). "Close" means OFF (the red LED is off)
+~~
+
+Uses code by:
+Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH 
+Author: Christian Starkjohann
+Creation Date: 2008-04-11
+
+~~
+The relay device ID - from HID attributes:
+vendorName = "www.dcttech.com" -> this site does not exist (squatted)
+productName = "USBRelay2"
+
diff --git a/commandline/hiddata.c b/commandline/hiddata.c
new file mode 100644 (file)
index 0000000..2874c33
--- /dev/null
@@ -0,0 +1,332 @@
+/* Name: hiddata.c
+ * Author: Christian Starkjohann
+ * Creation Date: 2008-04-11
+ * Tabsize: 4
+ * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+ * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
+ */
+
+#include <stdio.h>
+#include "hiddata.h"
+
+#if 0 //ifdef DEBUG
+#define DEBUG_PRINT(arg)    printf arg
+#else
+#define DEBUG_PRINT(arg)
+#endif
+
+/* ######################################################################## */
+#if defined(_WIN32) /* ##################################################### */
+/* ######################################################################## */
+
+#include "targetver.h"
+#include <windows.h>
+#include <setupapi.h>
+#include <hidsdi.h>
+#include <hidpi.h>
+
+#ifdef _MSC_VER
+#pragma comment(lib, "setupapi")
+#pragma comment(lib, "hid")
+#endif /*_MSC_VER*/
+
+
+/* ------------------------------------------------------------------------ */
+
+static void convertUniToAscii(char *buffer)
+{
+unsigned short  *uni = (void *)buffer;
+char            *ascii = buffer;
+
+    while(*uni != 0){
+        if(*uni >= 256){
+            *ascii++ = '?';
+        }else{
+            *ascii++ = (char)(*uni++);
+        }
+    }
+    *ascii++ = 0;
+}
+
+int usbhidOpenDevice(usbDevice_t **device, int vendor, char *vendorName, int product, char *productName, int usesReportIDs)
+{
+       GUID                                hidGuid;        /* GUID for HID driver */
+       HDEVINFO                            deviceInfoList;
+       SP_DEVICE_INTERFACE_DATA            deviceInfo;
+       SP_DEVICE_INTERFACE_DETAIL_DATA     *deviceDetails = NULL;
+       DWORD                               size;
+       int                                 i, openFlag = 0;  /* may be FILE_FLAG_OVERLAPPED */
+       int                                 errorCode = USBOPEN_ERR_NOTFOUND;
+       HANDLE                              handle = INVALID_HANDLE_VALUE;
+       HIDD_ATTRIBUTES                     deviceAttributes;
+                               
+    HidD_GetHidGuid(&hidGuid);
+    deviceInfoList = SetupDiGetClassDevs(&hidGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
+    deviceInfo.cbSize = sizeof(deviceInfo);
+    for(i=0;;i++){
+        if(handle != INVALID_HANDLE_VALUE){
+            CloseHandle(handle);
+            handle = INVALID_HANDLE_VALUE;
+        }
+        if(!SetupDiEnumDeviceInterfaces(deviceInfoList, 0, &hidGuid, i, &deviceInfo))
+            break;  /* no more entries */
+        /* first do a dummy call just to determine the actual size required */
+        SetupDiGetDeviceInterfaceDetail(deviceInfoList, &deviceInfo, NULL, 0, &size, NULL);
+        if(deviceDetails != NULL)
+            free(deviceDetails);
+        deviceDetails = malloc(size);
+        deviceDetails->cbSize = sizeof(*deviceDetails);
+        /* this call is for real: */
+        SetupDiGetDeviceInterfaceDetail(deviceInfoList, &deviceInfo, deviceDetails, size, &size, NULL);
+        DEBUG_PRINT(("checking HID path \"%s\"\n", deviceDetails->DevicePath));
+#if 0
+        /* If we want to access a mouse or keyboard, we can only use feature
+         * requests as the device is locked by Windows. It must be opened
+         * with ACCESS_TYPE_NONE.
+         */
+        handle = CreateFile(deviceDetails->DevicePath, ACCESS_TYPE_NONE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, openFlag, NULL);
+#endif
+        /* attempt opening for R/W -- we don't care about devices which can't be accessed */
+        handle = CreateFile(deviceDetails->DevicePath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, openFlag, NULL);
+        if(handle == INVALID_HANDLE_VALUE){
+            DEBUG_PRINT(("opening failed: %d\n", (int)GetLastError()));
+            /* errorCode = USBOPEN_ERR_ACCESS; opening will always fail for mouse -- ignore */
+            continue;
+        }
+        deviceAttributes.Size = sizeof(deviceAttributes);
+        HidD_GetAttributes(handle, &deviceAttributes);
+        DEBUG_PRINT(("device attributes: vid=%d pid=%d\n", deviceAttributes.VendorID, deviceAttributes.ProductID));
+        if(deviceAttributes.VendorID != vendor || deviceAttributes.ProductID != product)
+            continue;   /* ignore this device */
+        errorCode = USBOPEN_ERR_NOTFOUND;
+        if(vendorName != NULL && productName != NULL){
+            char    buffer[512];
+                       memset(buffer, 0xFF, sizeof(buffer));
+            if(!HidD_GetManufacturerString(handle, buffer, sizeof(buffer))){
+                DEBUG_PRINT(("error obtaining vendor name\n"));
+                errorCode = USBOPEN_ERR_IO;
+                continue;
+            }
+            convertUniToAscii(buffer);
+            DEBUG_PRINT(("vendorName = \"%s\"\n", buffer));
+            if(strcmp(vendorName, buffer) != 0)
+                continue;
+                       memset(buffer, 0xFF, sizeof(buffer));
+            if(!HidD_GetProductString(handle, buffer, sizeof(buffer))){
+                DEBUG_PRINT(("error obtaining product name\n"));
+                errorCode = USBOPEN_ERR_IO;
+                continue;
+            }
+            convertUniToAscii(buffer);
+            DEBUG_PRINT(("productName = \"%s\"\n", buffer));
+            if(strcmp(productName, buffer) != 0)
+                continue;
+        }
+        break;  /* we have found the device we are looking for! */
+    }
+    SetupDiDestroyDeviceInfoList(deviceInfoList);
+    if(deviceDetails != NULL)
+        free(deviceDetails);
+    if(handle != INVALID_HANDLE_VALUE){
+        *device = (usbDevice_t *)handle;
+        errorCode = 0;
+    }
+    return errorCode;
+}
+
+/* ------------------------------------------------------------------------ */
+
+void    usbhidCloseDevice(usbDevice_t *device)
+{
+    CloseHandle((HANDLE)device);
+}
+
+/* ------------------------------------------------------------------------ */
+
+int usbhidSetReport(usbDevice_t *device, char *buffer, int len)
+{
+BOOLEAN rval;
+
+    rval = HidD_SetFeature((HANDLE)device, buffer, len);
+    return rval == 0 ? USBOPEN_ERR_IO : 0;
+}
+
+/* ------------------------------------------------------------------------ */
+
+int usbhidGetReport(usbDevice_t *device, int reportNumber, char *buffer, int *len)
+{
+BOOLEAN rval = 0;
+
+    buffer[0] = reportNumber;
+    rval = HidD_GetFeature((HANDLE)device, buffer, *len);
+    return rval == 0 ? USBOPEN_ERR_IO : 0;
+}
+
+/* ------------------------------------------------------------------------ */
+
+/* ######################################################################## */
+#else /* defined WIN32 #################################################### */
+/* ######################################################################## */
+
+#include <string.h>
+#include <usb.h>
+
+#define usbDevice   usb_dev_handle  /* use libusb's device structure */
+
+/* ------------------------------------------------------------------------- */
+
+#define USBRQ_HID_GET_REPORT    0x01
+#define USBRQ_HID_SET_REPORT    0x09
+
+#define USB_HID_REPORT_TYPE_FEATURE 3
+
+
+static int  usesReportIDs;
+
+/* ------------------------------------------------------------------------- */
+
+static int usbhidGetStringAscii(usb_dev_handle *dev, int index, char *buf, int buflen)
+{
+char    buffer[256];
+int     rval, i;
+
+    if((rval = usb_get_string_simple(dev, index, buf, buflen)) >= 0) /* use libusb version if it works */
+        return rval;
+    if((rval = usb_control_msg(dev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) + index, 0x0409, buffer, sizeof(buffer), 5000)) < 0)
+        return rval;
+    if(buffer[1] != USB_DT_STRING){
+        *buf = 0;
+        return 0;
+    }
+    if((unsigned char)buffer[0] < rval)
+        rval = (unsigned char)buffer[0];
+    rval /= 2;
+    /* lossy conversion to ISO Latin1: */
+    for(i=1;i<rval;i++){
+        if(i > buflen)              /* destination buffer overflow */
+            break;
+        buf[i-1] = buffer[2 * i];
+        if(buffer[2 * i + 1] != 0)  /* outside of ISO Latin1 range */
+            buf[i-1] = '?';
+    }
+    buf[i-1] = 0;
+    return i-1;
+}
+
+int usbhidOpenDevice(usbDevice_t **device, int vendor, char *vendorName, int product, char *productName, int _usesReportIDs)
+{
+struct usb_bus      *bus;
+struct usb_device   *dev;
+usb_dev_handle      *handle = NULL;
+int                 errorCode = USBOPEN_ERR_NOTFOUND;
+static int          didUsbInit = 0;
+
+    if(!didUsbInit){
+        usb_init();
+        didUsbInit = 1;
+    }
+    usb_find_busses();
+    usb_find_devices();
+    for(bus=usb_get_busses(); bus; bus=bus->next){
+        for(dev=bus->devices; dev; dev=dev->next){
+            if(dev->descriptor.idVendor == vendor && dev->descriptor.idProduct == product){
+                char    string[256];
+                int     len;
+                handle = usb_open(dev); /* we need to open the device in order to query strings */
+                if(!handle){
+                    errorCode = USBOPEN_ERR_ACCESS;
+                    fprintf(stderr, "Warning: cannot open USB device: %s\n", usb_strerror());
+                    continue;
+                }
+                if(vendorName == NULL && productName == NULL){  /* name does not matter */
+                    break;
+                }
+                /* now check whether the names match: */
+                len = usbhidGetStringAscii(handle, dev->descriptor.iManufacturer, string, sizeof(string));
+                if(len < 0){
+                    errorCode = USBOPEN_ERR_IO;
+                    fprintf(stderr, "Warning: cannot query manufacturer for device: %s\n", usb_strerror());
+                }else{
+                    errorCode = USBOPEN_ERR_NOTFOUND;
+                    /* fprintf(stderr, "seen device from vendor ->%s<-\n", string); */
+                    if(strcmp(string, vendorName) == 0){
+                        len = usbhidGetStringAscii(handle, dev->descriptor.iProduct, string, sizeof(string));
+                        if(len < 0){
+                            errorCode = USBOPEN_ERR_IO;
+                            fprintf(stderr, "Warning: cannot query product for device: %s\n", usb_strerror());
+                        }else{
+                            errorCode = USBOPEN_ERR_NOTFOUND;
+                            /* fprintf(stderr, "seen product ->%s<-\n", string); */
+                            if(strcmp(string, productName) == 0)
+                                break;
+                        }
+                    }
+                }
+                usb_close(handle);
+                handle = NULL;
+            }
+        }
+        if(handle)
+            break;
+    }
+    if(handle != NULL){
+        errorCode = 0;
+        *device = (void *)handle;
+        usesReportIDs = _usesReportIDs;
+    }
+    return errorCode;
+}
+
+/* ------------------------------------------------------------------------- */
+
+void    usbhidCloseDevice(usbDevice_t *device)
+{
+    if(device != NULL)
+        usb_close((void *)device);
+}
+
+/* ------------------------------------------------------------------------- */
+
+int usbhidSetReport(usbDevice_t *device, char *buffer, int len)
+{
+int bytesSent, reportId = buffer[0];
+
+    if(!usesReportIDs){
+        buffer++;   /* skip dummy report ID */
+        len--;
+    }
+    bytesSent = usb_control_msg((void *)device, USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_ENDPOINT_OUT, USBRQ_HID_SET_REPORT, USB_HID_REPORT_TYPE_FEATURE << 8 | (reportId & 0xff), 0, buffer, len, 5000);
+    if(bytesSent != len){
+        if(bytesSent < 0)
+            fprintf(stderr, "Error sending message: %s\n", usb_strerror());
+        return USBOPEN_ERR_IO;
+    }
+    return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+int usbhidGetReport(usbDevice_t *device, int reportNumber, char *buffer, int *len)
+{
+int bytesReceived, maxLen = *len;
+
+    if(!usesReportIDs){
+        buffer++;   /* make room for dummy report ID */
+        maxLen--;
+    }
+    bytesReceived = usb_control_msg((void *)device, USB_TYPE_CLASS | USB_RECIP_DEVICE | USB_ENDPOINT_IN, USBRQ_HID_GET_REPORT, USB_HID_REPORT_TYPE_FEATURE << 8 | reportNumber, 0, buffer, maxLen, 5000);
+    if(bytesReceived < 0){
+        fprintf(stderr, "Error sending message: %s\n", usb_strerror());
+        return USBOPEN_ERR_IO;
+    }
+    *len = bytesReceived;
+    if(!usesReportIDs){
+        buffer[-1] = reportNumber;  /* add dummy report ID */
+        (*len)++;
+    }
+    return 0;
+}
+
+/* ######################################################################## */
+#endif /* defined WIN32 ################################################### */
+/* ######################################################################## */
diff --git a/commandline/hiddata.h b/commandline/hiddata.h
new file mode 100644 (file)
index 0000000..245453d
--- /dev/null
@@ -0,0 +1,70 @@
+/* Name: hiddata.h
+ * Author: Christian Starkjohann
+ * Creation Date: 2008-04-11
+ * Tabsize: 4
+ * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+ * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)
+ */
+
+#ifndef __HIDDATA_H_INCLUDED__
+#define __HIDDATA_H_INCLUDED__
+
+/*
+General Description:
+This module implements an abstraction layer for data transfer over HID feature
+requests. The implementation uses native Windows functions on Windows so that
+no driver installation is required and libusb on Unix. You must link the
+appropriate libraries in either case: "-lhid -lusb -lsetupapi" on Windows and
+`libusb-config --libs` on Unix.
+*/
+
+/* ------------------------------------------------------------------------ */
+
+#define USBOPEN_SUCCESS         0   /* no error */
+#define USBOPEN_ERR_ACCESS      1   /* not enough permissions to open device */
+#define USBOPEN_ERR_IO          2   /* I/O error */
+#define USBOPEN_ERR_NOTFOUND    3   /* device not found */
+
+/* ------------------------------------------------------------------------ */
+
+typedef struct usbDevice    usbDevice_t;
+/* Opaque data type representing the USB device. This can be a Windows handle
+ * or a libusb handle, depending on the backend implementation.
+ */
+
+/* ------------------------------------------------------------------------ */
+
+int usbhidOpenDevice(usbDevice_t **device, int vendorID, char *vendorName, int productID, char *productName, int usesReportIDs);
+/* This function opens a USB device. 'vendorID' and 'productID' are the numeric
+ * Vendor-ID and Product-ID of the device we want to open. If 'vendorName' and
+ * 'productName' are both not NULL, only devices with matching manufacturer-
+ * and product name strings are accepted. If the device uses report IDs,
+ * 'usesReportIDs' must be set to a non-zero value.
+ * Returns: If a matching device has been found, USBOPEN_SUCCESS is returned
+ * and '*device' is set to an opaque pointer representing the device. The
+ * device must be closed with usbhidCloseDevice(). If the device has not been
+ * found or opening failed, an error code is returned.
+ */
+void    usbhidCloseDevice(usbDevice_t *device);
+/* Every device opened with usbhidOpenDevice() must be closed with this function.
+ */
+int usbhidSetReport(usbDevice_t *device, char *buffer, int len);
+/* This function sends a feature report to the device. The report ID must be
+ * in the first byte of buffer and the length 'len' of the report is specified
+ * including this report ID. If no report IDs are used, buffer[0] must be set
+ * to 0 (dummy report ID).
+ * Returns: 0 on success, an error code otherwise.
+ */
+int usbhidGetReport(usbDevice_t *device, int reportID, char *buffer, int *len);
+/* This function obtains a feature report from the device. The requested
+ * report-ID is passed in 'reportID'. The caller must pass a buffer of the size
+ * of the expected report in 'buffer' and initialize the variable pointed to by
+ * 'len' to the total size of this buffer. Upon successful return, the report
+ * (prefixed with the report-ID) is in 'buffer' and the actual length of the
+ * report is returned in '*len'.
+ * Returns: 0 on success, an error code otherwise.
+ */
+
+/* ------------------------------------------------------------------------ */
+
+#endif /* __HIDDATA_H_INCLUDED__ */
diff --git a/commandline/hidusb-demo.sln b/commandline/hidusb-demo.sln
new file mode 100644 (file)
index 0000000..65974d2
--- /dev/null
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual C++ Express 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hidusb-relay-cmd", "hidusb-relay-cmd(vc2008).vcproj", "{CF9B07CB-509D-43B5-94FC-2BA43427C093}"
+EndProject
+Global
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution
+               Debug|Win32 = Debug|Win32
+               Release|Win32 = Release|Win32
+       EndGlobalSection
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution
+               {CF9B07CB-509D-43B5-94FC-2BA43427C093}.Debug|Win32.ActiveCfg = Debug|Win32
+               {CF9B07CB-509D-43B5-94FC-2BA43427C093}.Debug|Win32.Build.0 = Debug|Win32
+               {CF9B07CB-509D-43B5-94FC-2BA43427C093}.Release|Win32.ActiveCfg = Release|Win32
+               {CF9B07CB-509D-43B5-94FC-2BA43427C093}.Release|Win32.Build.0 = Release|Win32
+       EndGlobalSection
+       GlobalSection(SolutionProperties) = preSolution
+               HideSolutionNode = FALSE
+       EndGlobalSection
+EndGlobal
diff --git a/commandline/hidusb-relay-cmd(vc2008).vcproj b/commandline/hidusb-relay-cmd(vc2008).vcproj
new file mode 100644 (file)
index 0000000..800f366
--- /dev/null
@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<VisualStudioProject
+       ProjectType="Visual C++"
+       Version="9.00"
+       Name="hidusb-relay-cmd"
+       ProjectGUID="{CF9B07CB-509D-43B5-94FC-2BA43427C093}"
+       RootNamespace="hidusb-relay-cmd"
+       Keyword="Win32Proj"
+       TargetFrameworkVersion="0"
+       >
+       <Platforms>
+               <Platform
+                       Name="Win32"
+               />
+       </Platforms>
+       <ToolFiles>
+       </ToolFiles>
+       <Configurations>
+               <Configuration
+                       Name="Debug|Win32"
+                       OutputDirectory="Debug"
+                       IntermediateDirectory="Debug"
+                       ConfigurationType="1"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               Optimization="0"
+                               AdditionalIncludeDirectories="\winddk\7.1.0\inc\crt;\winddk\7.1.0\inc\api"
+                               PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;DEBUG=1;_CRT_SECURE_NO_WARNINGS=1"
+                               MinimalRebuild="true"
+                               BasicRuntimeChecks="3"
+                               RuntimeLibrary="3"
+                               UsePrecompiledHeader="0"
+                               WarningLevel="3"
+                               DebugInformationFormat="4"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               LinkIncremental="2"
+                               AdditionalLibraryDirectories="D:\winddk\7.1.0\lib\wxp\i386"
+                               GenerateDebugInformation="true"
+                               SubSystem="1"
+                               TargetMachine="1"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCManifestTool"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCAppVerifierTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                       />
+               </Configuration>
+               <Configuration
+                       Name="Release|Win32"
+                       OutputDirectory="Release"
+                       IntermediateDirectory="Release"
+                       ConfigurationType="1"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               AdditionalIncludeDirectories="\winddk\7.1.0\inc\crt;\winddk\7.1.0\inc\api"
+                               PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS=1"
+                               RuntimeLibrary="2"
+                               UsePrecompiledHeader="0"
+                               WarningLevel="3"
+                               DebugInformationFormat="3"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               LinkIncremental="2"
+                               AdditionalLibraryDirectories="D:\winddk\7.1.0\lib\wxp\i386"
+                               GenerateDebugInformation="true"
+                               SubSystem="1"
+                               OptimizeReferences="2"
+                               EnableCOMDATFolding="2"
+                               TargetMachine="1"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCManifestTool"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCAppVerifierTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                       />
+               </Configuration>
+       </Configurations>
+       <References>
+       </References>
+       <Files>
+               <Filter
+                       Name="Header Files"
+                       Filter="h;hpp;hxx;hm;inl;inc;xsd"
+                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+                       >
+                       <File
+                               RelativePath=".\hiddata.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\hidusb-tool.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\targetver.h"
+                               >
+                       </File>
+               </Filter>
+               <Filter
+                       Name="Resource Files"
+                       Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
+                       UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+                       >
+               </Filter>
+               <Filter
+                       Name="Source Files"
+                       Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+                       >
+                       <File
+                               RelativePath=".\hiddata.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\usbrelay-cmd.c"
+                               >
+                       </File>
+               </Filter>
+               <File
+                       RelativePath=".\README-hidusb-relay-cmd.txt"
+                       >
+               </File>
+       </Files>
+       <Globals>
+       </Globals>
+</VisualStudioProject>
diff --git a/commandline/hidusb-tool.h b/commandline/hidusb-tool.h
new file mode 100644 (file)
index 0000000..d095c0f
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef __USBHIDCHEAP_H_INCLUDED__
+#define __USBHIDCHEAP_H_INCLUDED__
+
+#include "targetver.h"
+#include "hiddata.h"
+
+#define USB_CFG_VENDOR_ID       0x16c0 /* 5824 = voti.nl */
+#define USB_CFG_DEVICE_ID       0x05DF /* obdev's shared PID for HIDs */
+
+
+typedef usbDevice_t *USBDEVHANDLE; /* I prefer handles to libusb 'opaque pointer' */
+
+#ifndef DEBUG_PRINT
+#if 0 //ifdef DEBUG
+#      define DEBUG_PRINT(arg)    printf arg
+#else
+#      define DEBUG_PRINT(arg)
+#endif
+#endif /*DEBUG_PRINT*/
+
+#ifdef _MSC_VER
+#define strcasecmp _stricmp
+#define strncasecmp _strnicmp
+#define snprintf   _snprintf
+#endif
+
+#endif /*__USBHIDCHEAP_H_INCLUDED__*/
diff --git a/commandline/relay1-pulse.sh b/commandline/relay1-pulse.sh
new file mode 100644 (file)
index 0000000..df9ff39
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/sh
+# Pulse USB relay channel for  (sec) seconds
+# Needs root acccess! run with sudo!
+sec=${1}
+chan=1
+SUDO=sudo
+$SUDO ./usbrelay-cmd ON $chan
+sleep $sec
+$SUDO ./usbrelay-cmd OFF $chan
diff --git a/commandline/targetver.h b/commandline/targetver.h
new file mode 100644 (file)
index 0000000..d29cb45
--- /dev/null
@@ -0,0 +1,15 @@
+#ifdef _MSC_VER
+
+#pragma once
+
+// Target Windows XP or later.
+// Refer to MSDN for the latest info on corresponding values for different platforms.
+#ifndef WINVER
+#define WINVER 0x0501
+#endif
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501
+#endif
+
+#endif /*MSC_VER*/
diff --git a/commandline/usbrelay-cmd.c b/commandline/usbrelay-cmd.c
new file mode 100644 (file)
index 0000000..1c8c9a4
--- /dev/null
@@ -0,0 +1,255 @@
+// Chinese USB/HID relay command line tool:
+//
+// pa01 15-Apr-2014
+// Currently finds the 1st matching device by ven,dev, product name string.
+// TODO:
+// - Enum all matching devices, select one by ID
+// - Determine the relay model (1 or 2 or more) by product string? 
+// Windows build: using VC++2008 & WDK7.1
+//~~~~~~~~~~~~~~~~~~~~~~~~
+
+/* Prototype: V-USB example: vusb-20121206/examples/hid-data/commandline/hidtool.c 
+ * Author: Christian Starkjohann
+ * Creation Date: 2008-04-11
+ * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH
+ */
+
+#define A_VER_STR "r0.2 (2 ch. only)"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "hidusb-tool.h"
+
+#define USB_CFG_VENDOR_NAME     "www.dcttech.com"
+#define USB_CFG_DEVICE_NAME     "USBRelay2"  // can be relay1... relay8?
+
+
+static int g_max_relay_num = 0;
+
+static int rel_read_status(USBDEVHANDLE dev, void *raw_data);
+
+/* ------------------------------------------------------------------------- */
+
+static const char *usbErrorMessage(int errCode)
+{
+    static char buffer[80];
+
+    switch(errCode){
+        case USBOPEN_ERR_ACCESS:      return "Access to device denied";
+        case USBOPEN_ERR_NOTFOUND:    return "The specified device was not found";
+        case USBOPEN_ERR_IO:          return "Communication error with device";
+        default:
+            snprintf(buffer, sizeof(buffer), "Unknown USB error %d", errCode);
+            return buffer;
+    }
+    return NULL;    /* not reached */
+}
+
+static USBDEVHANDLE openDevice(void)
+{
+    USBDEVHANDLE    dev = 0;
+    char            vendorName[]  = USB_CFG_VENDOR_NAME, 
+                    productName[] = USB_CFG_DEVICE_NAME; 
+    int             vid = USB_CFG_VENDOR_ID;
+    int             pid = USB_CFG_DEVICE_ID;
+    int             err;
+
+    // TODO: enumerate all instances, then filter  by unique ID
+    if((err = usbhidOpenDevice(&dev, vid, vendorName, pid, productName, 0)) != 0){
+        fprintf(stderr, "error finding %s: %s\n", productName, usbErrorMessage(err));
+        return NULL;
+    }
+
+    { /* Check the unique ID: 5 bytes at offs 0 */
+        char buffer[16];
+               err = rel_read_status(dev, buffer);
+        if( err < 0 ){
+            fprintf(stderr, "error reading report 0: %s\n", usbErrorMessage(err));
+            usbhidCloseDevice(dev);
+            return NULL;
+        }else{
+            int i;
+            //hexdump(buffer + 1, sizeof(buffer) - 1);
+            for (i=1; i <=5; i++) {
+                unsigned char x = (unsigned char)buffer[i];
+                if (x <= 0x20 || x >= 0x7F) {
+                    fprintf(stderr, "Bad device ID!\n");
+                    usbhidCloseDevice(dev);
+                    return NULL;
+                }
+            }
+            if( buffer[6] != 0 ) {
+                    fprintf(stderr, "Bad device ID!\n");
+                    usbhidCloseDevice(dev);
+                    return NULL;
+            }
+            
+            DEBUG_PRINT(("Device %s found: ID=[%5s]\n", USB_CFG_DEVICE_NAME, &buffer[1]));
+            g_max_relay_num = 2; /* hardcoded for "USBRelay2" */
+        }
+    }
+
+    return dev;
+}
+
+/* ------------------------------------------------------------------------- */
+#if 0
+static void hexdump(char *buffer, int len)
+{
+int     i;
+FILE    *fp = stdout;
+
+    for(i = 0; i < len; i++){
+        if(i != 0){
+            if(i % 16 == 0){
+                fprintf(fp, "\n");
+            }else{
+                fprintf(fp, " ");
+            }
+        }
+        fprintf(fp, "0x%02x", buffer[i] & 0xff);
+    }
+    if(i != 0)
+        fprintf(fp, "\n");
+}
+#endif
+
+/* ------------------------------------------------------------------------- */
+
+static void usage(char *myName)
+{
+    char *p = strrchr(myName, '\\'); /* windows */
+    if (p) myName = p + 1;
+    p = strrchr(myName, '/'); /* whatever */
+    if (p) myName = p + 1;
+
+    fprintf(stderr, "USBHID relay utility, " A_VER_STR "\n");
+    fprintf(stderr, "Usage:\n");
+    fprintf(stderr, "  %s on <num>  - turn relay <num> ON\n", myName);
+    fprintf(stderr, "  %s off <num> - turn relay <num> OFF\n", myName);
+    fprintf(stderr, "  %s state     - print state of the relays\n", myName);
+}
+
+
+// Read state of all relays
+// @return bit mask of all relays (R1->bit 0, R2->bit 1 ...) or -1 on error
+static int rel_read_status(USBDEVHANDLE dev, void *raw_data)
+{
+    char buffer[10];
+    int err;
+    int reportnum = 0;
+    int len = 8 + 1; /* report id 1 byte + 8 bytes data */
+    memset(buffer, 0, sizeof(buffer));
+
+    err = usbhidGetReport(dev, reportnum, buffer, &len);
+    if ( err ) {
+        fprintf(stderr, "error reading status: %s\n", usbErrorMessage(err));
+        return -1;
+    }
+
+    if ( len != 9 || buffer[0] != reportnum ) {
+        fprintf(stderr, "ERROR: wrong HID report returned!\n", len);
+        return -2;
+    }
+
+    if (raw_data) {
+        /* REVISE! copy raw report data */
+        memcpy( raw_data, buffer, sizeof(buffer) );
+    }
+
+    return (int)(unsigned char)buffer[8]; /* byte of relay states */
+}
+
+
+static int rel_onoff( USBDEVHANDLE dev, int is_on, char const *numstr )
+{
+    unsigned char buffer[10];
+    int err = -1;
+    int relaynum = numstr ? atoi(numstr) : 0;
+    
+    if ( numstr && (0 == strcmp(numstr,"*")) ) {
+        char x[2] = {'1', 0};
+        int i;
+        for (i = 1; i <= g_max_relay_num; i++) {
+            x[0] = (char)('0' + i);
+            err = rel_onoff(dev, is_on, x);
+            if (err) break;
+        }
+        return err;
+    }
+
+    if ( relaynum <= 0 || relaynum > g_max_relay_num ) {
+        fprintf(stderr, "Invalid relay number. Must be 1-%d or * (all)\n", g_max_relay_num);
+        return 1;
+    }
+
+    memset(buffer, 0, sizeof(buffer));
+    buffer[0] = 0; /* report # */
+    buffer[1] = is_on ? 0xFF : 0xFD;
+    buffer[2] = (unsigned char)relaynum;
+    if((err = usbhidSetReport(dev, buffer, 9)) != 0) {  
+        fprintf(stderr, "Error writing data: %s\n", usbErrorMessage(err));
+        return 1;
+    }
+    
+    // Read back & verify
+    err = rel_read_status(dev, NULL);
+    if ( err >= 0 ) {
+        err = (err >> (unsigned)(relaynum -1)) & 1;
+        err ^= !!is_on;
+    }
+
+    if ( err ) {
+        fprintf(stderr, "Error: failed set %s relay %u\n", is_on ? "ON":"OFF", relaynum);
+        return 1;
+    }
+
+    return 0;
+}
+
+
+int main(int argc, char **argv)
+{
+    usbDevice_t *dev;
+    int         err;
+    char const *arg1 = (argc >= 2) ? argv[1] : NULL;
+    char const *arg2 = (argc >= 3) ? argv[2] : NULL;
+
+    if ( !arg1 ) {
+        usage(argv[0]);
+        exit(1);
+    }
+
+    dev = openDevice();
+    if ( !dev )
+        exit(1);
+
+//    if ( strcasecmp(arg1, "state") == 0 ){
+       if ( strncasecmp(arg1, "stat", 4) == 0 ){
+        char buffer[10];
+        err = rel_read_status(dev, buffer);
+        if ( err < 0 ){
+            fprintf(stderr, "error reading data: %s\n", usbErrorMessage(err));
+            err = 1;
+        } else {
+            //hexdump(buffer + 1, len - 1);
+            printf("State: R1=%d R2=%d Board ID=[%5.5s]\n", !!(err & 0x1), !!(err & 0x2), &buffer[1] );
+        }
+    }else if( strcasecmp(arg1, "on" ) == 0){
+        err = rel_onoff(dev, 1, arg2);
+    }else if( strcasecmp(arg1, "off" ) == 0){
+        err = rel_onoff(dev, 0, arg2);
+    }else {
+        usage(argv[0]);
+        err = 2;
+    }
+
+    if ( dev ) {
+        usbhidCloseDevice(dev);
+    }
+
+    return err;
+}
+
+/* ------------------------------------------------------------------------- */
diff --git a/doc/hidrelay.md b/doc/hidrelay.md
new file mode 100644 (file)
index 0000000..3cf9d92
--- /dev/null
@@ -0,0 +1,18 @@
+
+The HID Relay device details:
+=========================
+
+Two USB HID relays are on eBay
+(search for "5V USB Relay Programmable Computer Control For Smart Home"):
+ one- and two-channel models; there can be also a four-channel model (?)
+
+- Each relay can connect up to 250V/10A AC or 28V DC load. Please see the electrical specs provided by vendors. 
+- USB-powered only; no external power input. When disconnected from host, the relays state is not remembered.
+- The board has red status LEDs for each channels and a green power LED.
+- Each relay channel has Normally Open and Normally Closed contact. The status LED lighs when the Normally Open contacts get closed. We'll name this state "**ON**", and the "normal" state - "**OFF**".
+- The USB interface is low-speed (USB 1.1) HID, based on [V-USB](http://www.obdev.at/products/vusb/) software USB emulation library.
+- The USB device ID is VEN=16C0 DEV=05DF. This is the shared ID for V-USB HID class devices. The V-USB documentation describes how to detect the real vendor and product IDs for such devices.
+- For the 2-channel boards, the vendor ID is "www.dcttech.com" and product ID is "USBRelay2".
+- Each board has an unique 5-character ID string in its HID descriptor (not to be confused with the standard USB "serial number"). We don't know whether these IDs are really unique, at least, on all our devices they are different.
+
+