HID USB Relay
Posted: Sun Dec 16, 2018 6:17 am
Bought a cheapo $5 USB Relay device from Ebay. It's a HID device, pretty straight forward.
And the required HID Module...
Tried to translate some of the strings from Russian, with strange results.
Wrote some quick and dirty Sunday afternoon code for it..."1 Channel 5V USB Relay Module USB Computer Control USB Relay Switch Board"
Description:
- The module uses HID technology, foreign trade quality, without any driver, download the data with its own application, plug and play.
- Support WIN7, XP 32-bit, 64-bit system, easy to use.
- On-board square USB interface, stable connection.
- Using special relay driver chip ULN2803, relay work more stable.
- Military-level PCB production. (HAHA!)
Code: Select all
EnableExplicit
; Generic USB HID Relay for Win32 - See Ebay
; reports as "dcttech.com"
; only tested on 1 channel unit.
; TODO: multi-unit / multi-relay support
; TODO: serial number setting / getting
; TODO: Modularize with a better API
; see inspiration on https://github.com/darrylb123/usbrelay
; http://vusb.wikidot.com/project:driver-less-usb-relays-hid-interface
XIncludeFile "hid_module.pbi" ; found in google with no credits sorry :( ... posted below,
#RELAY_ON=$ff
#RELAY_OFF=$fd
#RELAY_CMD_SET_SERIAL=$fa
Global *relaydevice
Procedure Operate_Relay(*hid_device, relay.a, state.a);{
Protected res.l ;
Protected *b = AllocateMemory(9)
PokeB(*b, $00)
PokeB(*b+1, state)
PokeB(*b+2, relay)
res = hid::SetFeature(*hid_device, *b, 9)
If (res < 0)
Debug ("Unable to WriteDevice() Error: " + Str(res));
EndIf
ProcedureReturn res;
EndProcedure
Procedure Init_Relays(vendor_id.l = $16c0, product_id.l = $05df)
Protected relay_device_available
Protected Test=HID::TestDevice(product_id, vendor_id)
If Test
HID::CloseDevice(*relaydevice)
*relaydevice=HID::OpenDevice(product_id, vendor_id, -1, 0)
Debug "Relay: Device Opened"
Debug "Manufacturer: "+hid::GetManufacturerString(*relaydevice)
Debug "Product: "+hid::GetProductString(*relaydevice)
Debug "Serial: "+hid::GetSerialNumberString(*relaydevice)
relay_device_available = 1
Else
Debug "Relay: No Device"
relay_device_available = 0
EndIf
ProcedureReturn relay_device_available
EndProcedure
Procedure Done_Relay(*relaydevice)
hid::CloseDevice(*relaydevice)
EndProcedure
HID::HID_Init()
If Init_Relays()
; simple test toggles relay 1 at 1hz.
Repeat
Sleep_(1000)
Operate_Relay(*relaydevice,1,#RELAY_ON) ; not zero indexed, wasted some time trying relay 0 :)
Sleep_(1000)
Operate_Relay(*relaydevice,1,#RELAY_OFF)
ForEver
Else
Debug "No device found."
EndIf
And the required HID Module...
Tried to translate some of the strings from Russian, with strange results.
Code: Select all
DeclareModule HID
;-DeclareModule
Structure HID_CAPS
Usage.w
UsagePage.w
InputReportByteLength.w
OutputReportByteLength.w
FeatureReportByteLength.w
Reserved.w[17]
NumberLinkCollectionNodes.w
NumberInputButtonCaps.w
NumberInputValueCaps.w
NumberInputDataIndices.w
NumberOutputButtonCaps.w
NumberOutputValueCaps.w
NumberOutputDataIndices.w
NumberFeatureButtonCaps.w
NumberFeatureValueCaps.w
NumberFeatureDataIndices.w
EndStructure
Structure HID_Sub_DeviceInfo
VendorID.u
ProductID.u
VersionNumber.u
NumInputBuffers.u
InputReportByteLength.u
OutputReportByteLength.u
FeatureReportByteLength.u
Manufacturer.s
Product.s
SerialNumber.s
EndStructure
Structure HID_DeviceInfo
CountDevice.w ; The number of the HID devices
DeviceInfo.HID_Sub_DeviceInfo[258]
EndStructure
Structure HID_Attributes
VID.u
PID.u
VersionNumber.u
EndStructure
Declare HID_Init()
Declare HID_End()
Declare OpenDevice(PID.u, VID.u, VersionNumber.w=-1, Index.u=0)
Declare CloseDevice(hDevice)
Declare TestDevice(PID.u, VID.u, VersionNumber.w=-1, Index.u=0)
Declare DeviceInfo(*Info.HID_DeviceInfo)
Declare ReadDevice(hDevice, *Buffer, Len)
Declare WriteDevice(hDevice, *Buffer, Len)
Declare GetFeature(hDevice, *Buffer, Len)
Declare SetFeature(hDevice, *Buffer, Len)
Declare GetInputReport(hDevice, *Buffer, Len)
Declare SetOutputReport(hDevice, *Buffer, Len)
Declare GetCaps(hDevice, *Capabilities.HID_CAPS)
; Assignment for the lack of facilities, the PID, the VID and the number of the universe.
Declare GetAttributes(hDevice, *DeviceInfo.HID_Attributes)
Declare GetNumInputBuffers(hDevice)
Declare.s GetManufacturerString(hDevice)
Declare.s GetProductString(hDevice)
Declare.s GetSerialNumberString(hDevice)
Declare.s GetIndexedString(hDevice, Index)
EndDeclareModule
Module HID
Structure HIDD_ATTRIBUTES
Size.l
VendorID.u
ProductID.u
VersionNumber.w
EndStructure
Structure PSP_DEVICE_INTERFACE_DETAIL_DATA
cbSize.l
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
DevicePath.l
CompilerElse
DevicePath.c
CompilerEndIf
EndStructure
CompilerIf Defined(SP_DEVICE_INTERFACE_DATA, #PB_Structure)=0
Structure SP_DEVICE_INTERFACE_DATA
cbSize.l
InterfaceClassGuid.GUID
Flags.l
Reserved.l
EndStructure
CompilerEndIf
EnableExplicit
Define.i
Prototype pGetHidGuid(*HidGuid.GUID)
Prototype pGetAttributes(*HidDeviceObject, *Attributes.HIDD_ATTRIBUTES)
Prototype pGetPreparsedData(*HidDeviceObject, *PreparsedData)
Prototype pGetCaps(*PreparsedData, *Capabilities.HID_CAPS)
Prototype pFreePreparsedData(PreparsedData)
Prototype pGetNumInputBuffers(HidHandle, *NumInputBuffers)
Prototype pGetManufacturerString(HidHandle, *Buffer, Len)
Prototype pGetProductString(HidHandle, *Buffer, Len)
Prototype pGetSerialNumberString(HidHandle, *Buffer, Len)
Prototype pGetIndexedString(HidHandle, Index, *Buffer, Len)
Prototype pGetFeature(HidHandle, *Buffer, Len)
Prototype pSetFeature(HidHandle, *Buffer, Len)
Prototype pGetInputReport(HidHandle, *Buffer, Len)
Prototype pSetOutputReport(HidHandle, *Buffer, Len)
Prototype pSetupDiEnumDeviceInterfaces(*DeviceInfoSet, DeviceInfoData, *InterfaceClassGuid.GUID,
MemberIndex, *DeviceInterfaceData.SP_DEVICE_INTERFACE_DATA)
Prototype pSetupDiGetDeviceInterfaceDetail(*DeviceInfoSet, *DeviceInterfaceData.SP_DEVICE_INTERFACE_DATA,
DeviceInterfaceDetailData, DeviceInterfaceDetailDataSize,
*RequiredSize, *DeviceInfoData)
Define hid_Lib, Setupapi_Lib, OS_Version
Procedure HID_Init()
Shared hid_Lib, Setupapi_Lib, OS_Version
Static __HID_INIT = 0
If __HID_INIT
ProcedureReturn ; run once only
Else
__HID_INIT = 1
EndIf
OS_Version = OSVersion()
hid_Lib=LoadLibrary_("hid.dll")
Setupapi_Lib=LoadLibrary_("setupapi.dll")
Global fGetHidGuid.pGetHidGuid=GetProcAddress_(hid_Lib, ?GetHidGuid)
Global fGetAttributes.pGetAttributes=GetProcAddress_(hid_Lib, ?GetAttributes)
Global fGetPreparsedData.pGetPreparsedData=GetProcAddress_(hid_Lib, ?GetPreparsedData)
Global fFreePreparsedData.pFreePreparsedData=GetProcAddress_(hid_Lib, ?FreePreparsedData)
Global fGetCaps.pGetCaps=GetProcAddress_(hid_Lib, ?GetCaps)
Global fGetInputReport.pGetInputReport=GetProcAddress_(hid_Lib, ?GetInputReport)
Global fSetOutputReport.pSetOutputReport=GetProcAddress_(hid_Lib, ?SetOutputReport)
Global fGetFeature.pGetFeature=GetProcAddress_(hid_Lib, ?GetFeature)
Global fSetFeature.pSetFeature=GetProcAddress_(hid_Lib, ?SetFeature)
Global fGetNumInputBuffers.pGetNumInputBuffers=GetProcAddress_(hid_Lib, ?GetNumInputBuffers)
Global fGetManufacturerString.pGetManufacturerString=GetProcAddress_(hid_Lib, ?GetManufacturerString)
Global fGetProductString.pGetProductString=GetProcAddress_(hid_Lib, ?GetProductString)
Global fGetSerialNumberString.pGetSerialNumberString=GetProcAddress_(hid_Lib, ?GetSerialNumberString)
Global fGetIndexedString.pGetIndexedString=GetProcAddress_(hid_Lib, ?GetIndexedString)
Global fSetupDiEnumDeviceInterfaces.pSetupDiEnumDeviceInterfaces=GetProcAddress_(Setupapi_Lib,
?SetupDiEnumDeviceInterfaces)
Global fSetupDiGetDeviceInterfaceDetail.pSetupDiGetDeviceInterfaceDetail=GetProcAddress_(Setupapi_Lib,
?Setup_InterfaceDetail)
DataSection
GetHidGuid:
! db "HidD_GetHidGuid", 0, 0
GetAttributes:
! db "HidD_GetAttributes", 0, 0
GetPreparsedData:
! db "HidD_GetPreparsedData", 0, 0
FreePreparsedData:
! db "HidD_FreePreparsedData", 0, 0
GetCaps:
! db "HidP_GetCaps", 0, 0
GetInputReport:
! db "HidD_GetInputReport", 0, 0
SetOutputReport:
! db "HidD_SetOutputReport", 0, 0
GetFeature:
! db "HidD_GetFeature", 0, 0
SetFeature:
! db "HidD_SetFeature", 0, 0
GetNumInputBuffers:
! db "HidD_GetNumInputBuffers", 0, 0
GetManufacturerString:
! db "HidD_GetManufacturerString", 0, 0
GetProductString:
! db "HidD_GetProductString", 0, 0
GetSerialNumberString:
! db "HidD_GetSerialNumberString", 0, 0
GetIndexedString:
! db "HidD_GetIndexedString", 0, 0
SetupDiEnumDeviceInterfaces:
! db "SetupDiEnumDeviceInterfaces", 0, 0
Setup_InterfaceDetail:
CompilerIf #PB_Compiler_Unicode=0
! db "SetupDiGetDeviceInterfaceDetailA", 0, 0
CompilerElse
! db "SetupDiGetDeviceInterfaceDetailW", 0, 0
CompilerEndIf
EndDataSection
EndProcedure
Procedure HID_End()
Shared hid_Lib, Setupapi_Lib
If hid_Lib
FreeLibrary_(hid_Lib)
EndIf
If Setupapi_Lib
FreeLibrary_(Setupapi_Lib)
EndIf
EndProcedure
Procedure FunctInfo(hDevice, *Info.HID_DeviceInfo)
Protected Attributes.HIDD_ATTRIBUTES, i
Protected HIDP_CAPS.HID_CAPS
Attributes\Size = SizeOf(HIDD_ATTRIBUTES)
If fGetAttributes(hDevice, @Attributes)
i = *Info\CountDevice
*Info\CountDevice+1
*Info\DeviceInfo[i]\VendorID = Attributes\VendorID
*Info\DeviceInfo[i]\ProductID = Attributes\ProductID
*Info\DeviceInfo[i]\VersionNumber = Attributes\VersionNumber
*Info\DeviceInfo[i]\Manufacturer = GetManufacturerString(hDevice)
*Info\DeviceInfo[i]\Product = GetProductString(hDevice)
*Info\DeviceInfo[i]\SerialNumber = GetSerialNumberString(hDevice)
*Info\DeviceInfo[i]\NumInputBuffers = GetNumInputBuffers(hDevice)
GetCaps(hDevice, @HIDP_CAPS)
*Info\DeviceInfo[i]\InputReportByteLength = HIDP_CAPS\InputReportByteLength
*Info\DeviceInfo[i]\OutputReportByteLength = HIDP_CAPS\OutputReportByteLength
*Info\DeviceInfo[i]\FeatureReportByteLength = HIDP_CAPS\FeatureReportByteLength
EndIf
EndProcedure
; Preparing the HID for the installation
Procedure Open_HID_Device(PID.u, VID.u, VersionNumber.w=-1, Index.u=0, *Funct=0, *Info.HID_DeviceInfo=0)
Protected HidGuid.Guid
Protected devInfoData.SP_DEVICE_INTERFACE_DATA
Protected Attributes.HIDD_ATTRIBUTES, Security.SECURITY_ATTRIBUTES
Protected *detailData.PSP_DEVICE_INTERFACE_DETAIL_DATA
Protected Length.l, CurrentIndex.w, hDevInfo
Protected i, Result, DevicePath.s
Protected Required, hDevice
If fGetHidGuid=0 Or fSetupDiEnumDeviceInterfaces=0 Or
fSetupDiGetDeviceInterfaceDetail=0 Or fGetAttributes=0
ProcedureReturn 0
EndIf
devInfoData\cbSize = SizeOf(SP_DEVICE_INTERFACE_DATA)
Security\nLength=SizeOf(SECURITY_ATTRIBUTES)
Security\bInheritHandle=1
Security\lpSecurityDescriptor = 0
fGetHidGuid(@HidGuid)
hDevInfo=SetupDiGetClassDevs_(@HidGuid,0,0, #DIGCF_PRESENT|#DIGCF_DEVICEINTERFACE)
If hDevInfo=0
ProcedureReturn 0
EndIf
For i=0 To 255
Result=fSetupDiEnumDeviceInterfaces(hDevInfo, 0, @HidGuid, i, @devInfoData)
If Result
Result = fSetupDiGetDeviceInterfaceDetail(hDevInfo, @devInfoData, 0, 0,@Length, 0)
*detailData=AllocateMemory(Length)
*detailData\cbSize=SizeOf(PSP_DEVICE_INTERFACE_DETAIL_DATA)
Result = fSetupDiGetDeviceInterfaceDetail(hDevInfo, @devInfoData, *detailData, Length+1, @Required, 0)
DevicePath.s=PeekS(@*detailData\DevicePath)
FreeMemory(*detailData)
hDevice=CreateFile_(@DevicePath, #GENERIC_READ|#GENERIC_WRITE,
#FILE_SHARE_READ|#FILE_SHARE_WRITE, @Security, #OPEN_EXISTING, 0, 0)
If hDevice<>#INVALID_HANDLE_VALUE
If *Funct And *Info
CallFunctionFast(*Funct, hDevice, *Info)
CloseHandle_(hDevice)
Else
Attributes\Size = SizeOf(HIDD_ATTRIBUTES)
Result = fGetAttributes(hDevice, @Attributes)
If Attributes\ProductID=PID And Attributes\VendorID=VID And
(Attributes\VersionNumber=VersionNumber Or VersionNumber=-1 )
If CurrentIndex=Index
SetupDiDestroyDeviceInfoList_(hDevInfo)
ProcedureReturn hDevice
Else
CurrentIndex+1
CloseHandle_(hDevice)
EndIf
Else
CloseHandle_(hDevice)
EndIf
EndIf
EndIf
Else
Break
EndIf
Next i
SetupDiDestroyDeviceInfoList_(hDevInfo)
ProcedureReturn 0
EndProcedure
Procedure OpenDevice(PID.u, VID.u, VersionNumber.w=-1, Index.u=0)
ProcedureReturn Open_HID_Device(PID, VID, VersionNumber, Index, 0, 0)
EndProcedure
Procedure CloseDevice(hDevice) ; Çàêðûòèå HID óñòðîéñòâà.
If hDevice
ProcedureReturn CloseHandle_(hDevice)
Else
ProcedureReturn 0
EndIf
EndProcedure
Procedure TestDevice(PID.u, VID.u, VersionNumber.w=-1, Index.u=0)
Protected hHid.i, Result
hHid=OpenDevice(PID, VID, VersionNumber, Index)
If hHid
CloseDevice(hHid)
Result=#True
Else
Result=#False
EndIf
ProcedureReturn Result
EndProcedure
Procedure DeviceInfo(*Info.HID_DeviceInfo)
If *Info
ClearStructure(*Info, HID_DeviceInfo)
Open_HID_Device(0, 0, 0, 0, @FunctInfo(), *Info)
ProcedureReturn Bool(*Info\CountDevice>0)
EndIf
EndProcedure
Procedure ReadDevice(hDevice, *Buffer, Len) ; ×òåíèå äàííûõ èç HID óñòðîéñòâà
Protected Written.l=0
If hDevice=0 Or *Buffer=0 Or Len<=0
ProcedureReturn 0
EndIf
ReadFile_(hDevice, *Buffer, Len, @Written, 0)
ProcedureReturn Written
EndProcedure
Procedure WriteDevice(hDevice, *Buffer, Len) ; Çàïèñü äàííûõ â HID óñòðîéñòâî
Protected Written.l=0
If hDevice=0 Or *Buffer=0 Or Len<=0
ProcedureReturn 0
EndIf
WriteFile_(hDevice, *Buffer, Len, @Written, 0)
ProcedureReturn Written
EndProcedure
Procedure GetFeature(hDevice, *Buffer, Len)
If hDevice And *Buffer And Len>0 And fGetFeature
ProcedureReturn fGetFeature(hDevice, *Buffer, Len)
Else
ProcedureReturn 0
EndIf
EndProcedure
Procedure SetFeature(hDevice, *Buffer, Len)
If hDevice And *Buffer And Len>0 And fSetFeature
ProcedureReturn fSetFeature(hDevice, *Buffer, Len)
Else
ProcedureReturn 0
EndIf
EndProcedure
; Consumption of a fresh fire. THE WINDOWS PLEASE TAKE PARTNERSHIP ?
Procedure GetInputReport(hDevice, *Buffer, Len)
Shared OS_Version
If hDevice And *buffer And Len>0 And
OS_Version>=#PB_OS_Windows_XP And fGetInputReport
ProcedureReturn fGetInputReport(hDevice, *Buffer, Len)
Else
ProcedureReturn 0
EndIf
EndProcedure
; Çàïèñü â óñòðîéñòâî âûõîäíîãî ðåïîðòà. ÂÍÈÌÀÍÈÅ ôóíêöèÿ ïîÿâèëàñü òîëüêî â WinXP
Procedure SetOutputReport(hDevice, *Buffer, Len)
Shared OS_Version
If hDevice And *buffer And Len>0 And
OS_Version>=#PB_OS_Windows_XP And fSetOutputReport
ProcedureReturn fSetOutputReport(hDevice, *Buffer, Len)
Else
ProcedureReturn 0
EndIf
EndProcedure
Procedure GetCaps(hDevice, *Capabilities.HID_CAPS) ; Óçíà¸ì ðàçìåðû áóôåðîâ è äð. èíôîðìàöèþ îá óñòðîéñòâå
Protected result=0, PreparsedData
If hDevice And fGetPreparsedData And
fGetCaps And fFreePreparsedData
If fGetPreparsedData(hDevice, @PreparsedData)
fGetCaps(PreparsedData, *Capabilities)
fFreePreparsedData(PreparsedData)
result=1
EndIf
EndIf
ProcedureReturn result
EndProcedure
; Óçíà¸ì ïî õåíäëó óñòðîéñòâà, åãî PID, VID è íîìåð âåðñèè.
Procedure GetAttributes(hDevice, *DeviceInfo.HID_Attributes)
Protected Info.HIDD_ATTRIBUTES, Result=#False
If hDevice And *DeviceInfo And fGetAttributes
Info\Size = SizeOf(HIDD_ATTRIBUTES)
If fGetAttributes(hDevice, @Info)
CopyMemory(@Info+OffsetOf(HIDD_ATTRIBUTES\VendorID), *DeviceInfo, SizeOf(HID_Attributes))
Result=#True
EndIf
EndIf
ProcedureReturn Result
EndProcedure
Procedure GetNumInputBuffers(hDevice)
Protected NumInputBuffers=0
If hDevice And fGetNumInputBuffers
fGetNumInputBuffers(hDevice, @NumInputBuffers)
EndIf
ProcedureReturn NumInputBuffers
EndProcedure
Procedure.s GetManufacturerString(hDevice) ; Ïîëó÷àåì èäåíòèôèêàòîð èçãîòîâèòåëÿ
Protected Result.s="", *mem
If hDevice And fGetManufacturerString
*mem=AllocateMemory(256)
If *mem
If fGetManufacturerString(hDevice, *mem, 252)
Result=PeekS(*mem,-1,#PB_Unicode)
EndIf
FreeMemory(*mem)
EndIf
EndIf
ProcedureReturn Result
EndProcedure
Procedure.s GetProductString(hDevice) ; Ïîëó÷àåì èäåíòèôèêàòîð ïðîäóêòà
Protected Result.s="", *mem
If hDevice And fGetProductString
*mem=AllocateMemory(256)
If *mem
If fGetProductString(hDevice, *mem, 252)
Result=PeekS(*mem,-1,#PB_Unicode)
EndIf
FreeMemory(*mem)
EndIf
EndIf
ProcedureReturn Result
EndProcedure
Procedure.s GetSerialNumberString(hDevice) ; The beginning of the serial number of the project
Protected Result.s="", *mem
If hDevice And fGetSerialNumberString
*mem=AllocateMemory(256)
If *mem
If fGetSerialNumberString(hDevice, *mem, 252)
Result=PeekS(*mem,-1,#PB_Unicode)
EndIf
FreeMemory(*mem)
EndIf
EndIf
ProcedureReturn Result
EndProcedure
Procedure.s GetIndexedString(hDevice, Index) ; The differences between the scenes and the source of the structure
Protected Result.s="", *mem
If hDevice And fGetIndexedString
*mem=AllocateMemory(256)
If *mem
If fGetIndexedString(hDevice, Index, *mem, 252)
Result=PeekS(*mem,-1,#PB_Unicode)
EndIf
FreeMemory(*mem)
EndIf
EndIf
ProcedureReturn Result
EndProcedure
EndModule