Skip to main content

LuaHID

LuaHID provides an interface to access USB devices over the HID (human input device) protocol. It implements a thin LUA wrapper interface over the cross platform hidapi library (hidapi.dll, see signal11/hidapi (github.com), licensed under BSD License). The code is derived from ynezz/luahidapi (github.com) (MIT License) and adopted to OGS.

Note, that although USB mice and keyboards are technically HID devices, the Windows API does not allow to access these through the HID API (for security reasons). All other (custom) USB HID devices should work.

Module

The LuaHID module provides global functions to access the systems HID API. Before calling any other function of the module, the init() function must be called.

To actually connect to a physical device, an instance of the HidDevice object must be created (see #class_hiddevice below) by calling the open() function. This requires specifying the VID (vendor ID) and PID (device id) and (optionally) the SN (serial number). If the parameters are not known beforehand, they may be listed through calling the module level enumerate() function. This returns a table of all currently connected devices. Specific device actions (like reading/writing) can then be executed on the object returned from the open() call.

Properties

Property NameReturn TypeDescriptionTags
_VERSIONstringCurrent version of the LuaHID module. See also the version_mod() function to get more details about the DLL module version.Read-Only
_TIMESTAMPstringTimestamp of the last build of the LuaHID module.Read-Only

Functions

Function NameReturn TypeDescriptionTags
init()booleanInitializes the LuaHID library. Returns true on success, nil on failure.None
exit()booleanCleans up and terminates the LuaHID library. Returns true on success, nil on failure.None
enumerate(integer vid, integer pid)
enumerate()
hidenumReturns a HID device enumeration object for HID devices that matches given vid, pid pair. Enumerates all HID devices if no arguments provided or (0,0) used.
IMPORTANT: Mouse and keyboard devices are not visible on Windows
Returns nil if failed.
None
open(string path)
open(number vid, number pid)
HidDeviceOpens a HID device using a path name or a vid,pid pair. Returns a HID device object on success - specifying a serial number is currently not implemented. Returns nil on failure.None
write(HidDevice dev, string report)
write(HidDevice dev, integer report_id, string report)
integerWrites the given report data string to the report_id (0 by default). Returns number of bytes actually sent on success, nil on failure.None
read(HidDevice dev, integer report_size [, timeout_msec])stringReads data from the given device. If a device has multiple reports, the first byte indicates the report ID and one extra byte needs to be allocated via report_size. For a normal call, timeout_msec can be omitted and blocking will depend on the selected option setting. Passing timeout_msec == -1 will always block. Returns the report data (as string) on success, nil on failure.None
set(HidDevice dev, string option)integerSet the read blocking option, allowed parameters are noblock and block. Returns true on success, nil on failure.None
getstring(HidDevice dev, string option)stringReads the given option property from the device. Currently known option names are manufacturer, product and serial. Returns the option value on success, nil on failure.None
setfeature(HidDevice dev, integer feature_id, string data)integerSend a feature report for the given feature_id with the given feature_data. Returns the number of bytes actually sent on success, nil on failure.None
getfeature(HidDevice dev, integer feature_id, integer feature_size)stringGet (read) a feature report for the given feature_id. A 0 is used for a single feature report. Returns the feature data (as a string) on success, nil on failure.None
error(HidDevice dev)stringReturns a string (as ASCII) describing the last error occurred for the device or nil if there was no error.None
close(HidDevice dev)NoneCloses the given HidDevice object.None
msleep(integer milliseconds)NoneConvenience function to sleep a number of milliseconds.None
version_mod()string, tableReturns the file version info of the LuaHID DLL (as a string in the from "major.minor.build-hi,build-low" and a table with the same values.None

Class HidDeviceInfo

The HidDeviceInfo table provides information about a USB HID device connected to the system. The HidDeviceInfo is retrieved by calling the enumerate() function and iterating the result by calling the next() member.

Properties

Property NameReturn TypeDescriptionTags
pathstringSystem specific device path.Read-Only
vidintegerVendor ID of the device.Read-Only
pidintegerProduct ID of the device.Read-Only
serial_numberstringDevice serial number.Read-Only
releaseintegerRelease number (version) of the device.Read-Only
manufacturer_stringstringManufacturer name.Read-Only
product_ _stringstringProduct name.Read-Only
usage_pageintegerHID usage page of the device.Read-Only
usageintegerHID usage of the device.Read-Only
interfaceintegerInterface number of the device.Read-Only

Class HidEnum

The HidEnum class is actually an iterator and represents the list returned from calling the enumerate module function. Each element of the list represents a connected USB HID device and has the properties shown in the following section. To iterate the list, call the next() instance function - each call to next returns a HidDeviceInfo table and internally advances to the next item.

Functions

Function NameReturn TypeDescriptionTags
next()HidDeviceReturns the current device info data and steps on to the next element in the list.None
close()NoneCloses the iterator and frees any resources. There is normally no need to call this, as the object is garbage collected automatically.None

Class HidDevice

Functions

The member functions of HidDevice are wrapped functions of the module. The following lines are identical:

local hidapi = require('luahid')
-- ...
-- assume dev is a HidDevice object returned from calling open()
-- The following are identical:
hidapi.write(dev, report_id, report_data)
dev:write(report_id, report_data)

Function NameReturn TypeDescriptionTags
write(string report)
write(HidDevice dev, integer report_id, string report)
integerSee LuaHID.write(...)None
read(integer report_size [, timeout_msec])stringSee LuaHID.read(...)None
set(HidDevice dev, integer option)integerSee LuaHID.set(...)None
getstring(HidDevice dev, string option)stringSee LuaHID.getstring(...)None
setfeature(HidDevice dev, integer feature_id, string data)integerSee LuaHID.setfeature(...)None
getfeature(HidDevice dev, integer feature_id, integer feature_size)stringSee LuaHID.getfeature(...)None
error()stringSee LuaHID.error(...)None
close(HidDevice dev)NoneSee LuaHID.close(...)None

Examples

Enumerate connected devices

This sample lists all connected HID devices and shows some of their properties.

-- Load the LuaHID module
local hidapi = require('luahid')
print(string.format("Lib VERSION %s build on %s", hid._VERSION, hid._TIMESTAMP))

-- Initialize the library
if hidapi.init() then
print("hid library: init")
else
print("hid library: init error")
return
end

-- Enumerate the currently connected devices and print some information
local enum = hidapi.enumerate()
if not enum then
print("Enumeration: no device found or enumeration failed!")
return
else
while true do
local dev = enum:next()
if not dev then break end
print("Device found:")
print(string.format("path = '%s'", dev.path))
print(string.format("vid = 0x%04X", dev.vid))
print(string.format("pid = 0x%04X", dev.pid))
print(string.format("serial_number = '%s'", dev.serial_number))
end
end
end

-- Do a clean shutdown
if hidapi.exit() then
print("hid library: exit")
else
print("hid library: exit error")
return
end

Read/write device report data

This sample tries to connect to a specific device using a given VID/PID and writes/reads some data in non-blocking mode.

-- Load the LuaHID module
local hidapi = require('luahid')
print(string.format("Lib VERSION %s build on %s", hid._VERSION, hid._TIMESTAMP))

-- Initialize the library
if hid.init() then
print("hid library: init")
else
print("hid library: init error")
return
end

-- Open a device by VID/PID
USB_DEVICE_VID = 0x04D8
USB_DEVICE_PID = 0x8ABC
USB_REPORT_SIZE = 4
local dev = hidapi.open(USB_DEVICE_VID, USB_DEVICE_PID)
if not dev then
print("Open: unable to open test device")
return
end
print("Open: opened test device")

-- Read the serial number
local sn = dev:getstring("serial")
if sn then
print("Product String: "..sn)
else
print("Unable to read product string")
return
end

-- set non-blocking reads
if not dev:set("noblock") then
print("Failed to set non-blocking option")
return
end

-- Try to read from the device. There shoud be no
-- data here, but execution should not block.
local rx = dev:read(USB_REPORT_SIZE)
if rx then
print("Done non-blocking read test")
print("Size of report read = "..#rx)
else
print("Read error during non-blocking read test")
return
end

-- Prepare and write report; report 0 is implied
local tx = string.char(0x12, 0x34, 0x56, 0x78)
local res = dev:write(tx)
if not res then
print("Unable to write()")
print("Error: "..dev:error())
return
end

-- Try reading data
local rx
for i = 1, 10 do
-- a non-infinite read loop
-- since we read immediately right after writing, the device buffer
-- will be empty, it will NAK, and an empty string is returned
rx = dev:read(USB_REPORT_SIZE)
if not rx then
print("Unable to read()")
print("Error: "..dev:error())
return
elseif rx == "" then
print("Waiting...")
else
break
end
for j = 1,200000 do end -- short delay
end
if #rx > 0 then
print("Successfully read data from device!")
end

-- Close the device
dev:close()
print("Close: closed test device")

-- Do a clean shutdown
if hidapi.exit() then
print("hid library: exit")
else
print("hid library: exit error")
return
end

More samples

More samples can be found in then examples-folder in the GitHub repository at luahidapi/doc/examples at master · ynezz/luahidapi (github.com).