Lua USB
There are two lua objects in our environment that facilitate receiving events from USB HID peripherals via the USB-A ports on a core.
Note: USB device compatibility varies widely and usage is at-your-own-risk.
This will instantiate a new instance.
Syntax
monitor = UsbMonitor.New()
This function is called when a USB Peripheral is added or removed from the core.
Syntax
monitor = UsbMonitor.EventHandler
{
"devnode": "/dev/input/event2",
"devtype": "",
"sysname": "event2",
"syspath": "/sys/devices/LNXSYSTM:00/LNXPWRBN:00/input/input2/event2",
"subsystem": "input",
"properties": {
"ID_PATH_TAG": "acpi-LNXPWRBN_00",
"DEVPATH": "/devices/LNXSYSTM:00/LNXPWRBN:00/input/input2/event2",
"SUBSYSTEM": "input",
"USEC_INITIALIZED": "7474756",
"ID_PATH": "acpi-LNXPWRBN:00",
"MINOR": "66",
"ID_INPUT_KEY": "1",
"ID_INPUT": "1",
"DEVNAME": "/dev/input/event2",
"MAJOR": "13"
},
"tags": {},
"Added": true,
"sysnum": "",
"devpath": "/devices/LNXSYSTM:00/LNXPWRBN:00/input/input2/event2"
},
{
"devnode": "/dev/input/event3",
"devtype": "",
"sysname": "event3",
"syspath": "/sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/LNXVIDEO:00/input/input3/event3",
"subsystem": "input",
"properties": {
"ID_PATH_TAG": "acpi-LNXVIDEO_00",
"DEVPATH": "/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/LNXVIDEO:00/input/input3/event3",
"SUBSYSTEM": "input",
"USEC_INITIALIZED": "7498631",
"ID_PATH": "acpi-LNXVIDEO:00",
"MINOR": "67",
"ID_INPUT_KEY": "1",
"ID_INPUT": "1",
"DEVNAME": "/dev/input/event3",
"MAJOR": "13"
},
"tags": {},
"Added": true,
"sysnum": "",
"devpath": "/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/LNXVIDEO:00/input/input3/event3"
},
{
"devnode": "/dev/input/event0",
"devtype": "",
"sysname": "event0",
"syspath": "/sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0C:00/input/input0/event0",
"subsystem": "input",
"properties": {
"ID_PATH_TAG": "acpi-PNP0C0C_00",
"DEVPATH": "/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0C:00/input/input0/event0",
"SUBSYSTEM": "input",
"USEC_INITIALIZED": "7530358",
"ID_PATH": "acpi-PNP0C0C:00",
"MINOR": "64",
"ID_INPUT_KEY": "1",
"ID_INPUT": "1",
"DEVNAME": "/dev/input/event0",
"MAJOR": "13"
},
"tags": {},
"Added": true,
"sysnum": "",
"devpath": "/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0C:00/input/input0/event0"
},
{
"devnode": "/dev/input/event1",
"devtype": "",
"sysname": "event1",
"syspath": "/sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0E:00/input/input1/event1",
"subsystem": "input",
"properties": {
"ID_PATH_TAG": "acpi-PNP0C0E_00",
"DEVPATH": "/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0E:00/input/input1/event1",
"SUBSYSTEM": "input",
"USEC_INITIALIZED": "7522284",
"ID_PATH": "acpi-PNP0C0E:00",
"MINOR": "65",
"ID_INPUT_KEY": "1",
"ID_INPUT": "1",
"DEVNAME": "/dev/input/event1",
"MAJOR": "13"
},
"tags": {},
"Added": true,
"sysnum": "",
"devpath": "/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0E:00/input/input1/event1"
},
{
"devnode": "/dev/input/event4",
"devtype": "",
"sysname": "event4",
"syspath": "/sys/devices/pci0000:00/0000:00:1d.0/usb1/1-1/1-1.3/1-1.3.2/1-1.3.2:1.0/0003:13BA:0001.0002/input/input7/event4",
"subsystem": "input",
"properties": {
"ID_PATH_TAG": "pci-0000_00_1d_0-usb-0_1_3_2_1_0",
"DEVPATH": "/devices/pci0000:00/0000:00:1d.0/usb1/1-1/1-1.3/1-1.3.2/1-1.3.2:1.0/0003:13BA:0001.0002/input/input7/event4",
"ID_INPUT_KEYBOARD": "1",
"ID_MODEL_ENC": "0001",
"USEC_INITIALIZED": "983943837172",
"ID_USB_INTERFACE_NUM": "00",
"MINOR": "68",
"MAJOR": "13",
"ID_TYPE": "hid",
"ID_PATH": "pci-0000:00:1d.0-usb-0:1.3.2:1.0",
"ID_VENDOR_ID": "13ba",
"ID_VENDOR_ENC": "13ba",
"ID_MODEL": "0001",
"ID_MODEL_ID": "0001",
"ID_REVISION": "0100",
"DEVLINKS": "/dev/input/by-id/usb-13ba_0001-event-kbd /dev/input/by-path/pci-0000:00:1d.0-usb-0:1.3.2:1.0-event-kbd",
"ID_USB_INTERFACES": ":030101:",
"SUBSYSTEM": "input",
"ID_VENDOR": "13ba",
"ID_INPUT_KEY": "1",
"ID_USB_DRIVER": "usbhid",
"DEVNAME": "/dev/input/event4",
"ID_INPUT": "1",
"ID_BUS": "usb",
"ID_SERIAL": "13ba_0001"
},
"tags": {},
"Added": true,
"sysnum": "",
"devpath": "/devices/pci0000:00/0000:00:1d.0/usb1/1-1/1-1.3/1-1.3.2/1-1.3.2:1.0/0003:13BA:0001.0002/input/input7/event4"
},
{
"devnode": "/dev/input/event5",
"devtype": "",
"sysname": "event5",
"syspath": "/sys/devices/virtual/input/input5/event5",
"subsystem": "input",
"properties": {
"MINOR": "69",
"DEVPATH": "/devices/virtual/input/input5/event5",
"SUBSYSTEM": "input",
"USEC_INITIALIZED": "23118530",
"ID_INPUT": "1",
"ID_INPUT_KEY": "1",
"DEVNAME": "/dev/input/event5",
"ID_INPUT_JOYSTICK": "1",
"ID_SERIAL": "noserial",
"MAJOR": "13"
},
"tags": {},
"Added": true,
"sysnum": "",
"devpath": "/devices/virtual/input/input5/event5"
},
{
"devnode": "/dev/input/event6",
"devtype": "",
"sysname": "event6",
"syspath": "/sys/devices/virtual/input/input6/event6",
"subsystem": "input",
"properties": {
"MINOR": "70",
"DEVPATH": "/devices/virtual/input/input6/event6",
"ID_INPUT_KEYBOARD": "1",
"SUBSYSTEM": "input",
"USEC_INITIALIZED": "23334484",
"DEVNAME": "/dev/input/event6",
"ID_INPUT_KEY": "1",
"ID_INPUT": "1",
"ID_SERIAL": "noserial",
"MAJOR": "13"
},
"tags": {},
"Added": true,
"sysnum": "",
"devpath": "/devices/virtual/input/input6/event6"
},
{
"sysname": "event7",
"subsystem": "input",
"devpath": "/devices/pci0000:00/0000:00:1d.0/usb1/1-1/1-1.3/1-1.3.1/1-1.3.1:1.0/0003:0079:0006.0003/input/input8/event7",
"devnode": "/dev/input/event7",
"sysnum": "",
"devtype": "",
"Added": true,
"properties": {
"ID_MODEL_ID": "0006",
"ID_USB_INTERFACE_NUM": "00",
"ID_USB_INTERFACES": ":030000:",
"ID_MODEL_ENC": "Generic\\x20\\x20\\x20USB\\x20\\x20Joystick\\x20\\x20",
"ACTION": "add",
"ID_PATH_TAG": "pci-0000_00_1d_0-usb-0_1_3_1_1_0",
"DEVLINKS": "/dev/input/by-id/usb-DragonRise_Inc._Generic_USB_Joystick-event-joystick /dev/input/by-path/pci-0000:00:1d.0-usb-0:1.3.1:1.0-event-joystick",
"ID_INPUT_JOYSTICK": "1",
"ID_MODEL": "Generic_USB_Joystick",
"ID_USB_DRIVER": "usbhid",
"ID_SERIAL": "DragonRise_Inc._Generic_USB_Joystick",
"USEC_INITIALIZED": "9887686401",
"ID_TYPE": "hid",
"SUBSYSTEM": "input",
"DEVNAME": "/dev/input/event7",
"SEQNUM": "2774",
"ID_VENDOR": "DragonRise_Inc.",
"ID_REVISION": "0107",
"MAJOR": "13",
"MINOR": "71",
"ID_PATH": "pci-0000:00:1d.0-usb-0:1.3.1:1.0",
"ID_INPUT": "1",
"ID_VENDOR_ENC": "DragonRise\\x20Inc.\\x20\\x20",
"ID_VENDOR_ID": "0079",
"DEVPATH": "/devices/pci0000:00/0000:00:1d.0/usb1/1-1/1-1.3/1-1.3.1/1-1.3.1:1.0/0003:0079:0006.0003/input/input8/event7",
"ID_BUS": "usb"
},
"syspath": "/sys/devices/pci0000:00/0000:00:1d.0/usb1/1-1/1-1.3/1-1.3.1/1-1.3.1:1.0/0003:0079:0006.0003/input/input8/event7",
"tags": {}
}
This Lua library deals with individual USB Devices. This is like TCP sockets that are part of a TcpSocketServer.
This will instantiate a new instance.
Syntax
UsbDevice = Usb.New(dev.devnode)
devnode must begin with “/dev/input/event” or “/dev/tty”
“/dev/tty” looks to be for serial ports via USB
A lookup table for USB Event types. The keys are a string such as KEY and the value will be an integer such as 1.
https://www.kernel.org/doc/Documentation/input/event-codes.txt
Content
{
"SYN": 0, // Synchronization event
"KEY": 1, // Key or button event
"REL": 2, // Relative axis event
"ABS": 3, // Absolute axis event
"MSC": 4, // Miscellaneous event
"LED": 17, // LED event
"SND": 18, // Sound event
"REP": 20, // Used for autorepeating devices
"FF": 21, // Used to send force feedback commands to an input device
"PWR": 22, // special type for power button and switch input
"FF_STATUS": 23 // Force feedback status
}
Example Code
You can lookup a code by using:
local code = Usb.Key.KEY_BACKSPACE
or you can lookup a key name:
function GetType(typeCode)
for k,v in pairs(Usb.Type) do
if typeCode == v then
return(k)
end
end
return nil
end
A lookup table for Sync Event codes.
Content
{
"REPORT": 0,
"CONFIG": 1,
"MT_REPORT": 2,
"DROPPED": 3
}
A lookup table for Relative Event codes.
Content
{
"X": 0,
"Y": 1,
"Z": 2,
"RX": 3,
"RY": 4,
"RZ": 5,
"HWHEEL": 6,
"DIAL": 7,
"WHEEL": 8,
"MISC": 9
}
A lookup table for Absolute Event codes.
Content
{
"X": 0,
"Y": 1,
"Z": 2,
"RX": 3,
"RY": 4,
"RZ": 5,
"THROTTLE": 6,
"RUDDER": 7,
"WHEEL": 8,
"GAS": 9,
"BRAKE": 10,
"HAT0X": 16,
"HAT0Y": 17,
"HAT1X": 18,
"HAT1Y": 19,
"HAT2X": 20,
"HAT2Y": 21,
"HAT3X": 22,
"HAT3Y": 23,
"PRESSURE": 24,
"DISTANCE": 25,
"TILT_X": 26,
"TILT_Y": 27,
"TOOL_WIDTH": 28,
"VOLUME": 32,
"MISC": 40,
"MT_SLOT": 47,
"MT_TOUCH_MAJOR": 48,
"MT_TOUCH_MINOR": 49,
"MT_WIDTH_MAJOR": 50,
"MT_WIDTH_MINOR": 51,
"MT_ORIENTATION": 52,
"MT_POSITION_X": 53,
"MT_POSITION_Y": 54,
"MT_TOOL_TYPE": 55,
"MT_BLOB_ID": 56,
"MT_TRACKING_ID": 57,
"MT_PRESSURE": 58,
"MT_DISTANCE": 59,
"MT_TOOL_X": 60,
"MT_TOOL_Y": 61
}
Note: RFKILL_ALL and RADIO are the same. This is dictated by Linux input-event-codes.h: 
Content
{
"LID": 0,
"TABLET_MODE": 1,
"HEADPHONE_INSERT": 2,
"RFKILL_ALL": 3,
"RADIO": 3,
"MICROPHONE_INSERT": 4,
"DOCK": 5,
"LINEOUT_INSERT": 6,
"JACK_PHYSICAL_INSERT": 7,
"VIDEOOUT_INSERT": 8,
"CAMERA_LENS_COVER": 9,
"KEYPAD_SLIDE": 10,
"FRONT_PROXIMITY": 11,
"ROTATE_LOCK": 12,
"LINEIN_INSERT": 13
}
-
USB Devices should be either globally scoped or assigned to a globally scoped table to avoid issues with garbage collection.
-
The Usb.EventHandler can be called with no data, check if the parameter is nil

file
local UsbHelpers = {}
UsbHelpers.GetType = function(code)
for k, v in pairs(Usb.Type) do
if code == v then
return (k)
end
end
return nil
end
UsbHelpers.GetSync = function(code)
for k, v in pairs(Usb.Sync) do
if code == v then
return (k)
end
end
return nil
end
UsbHelpers.GetRelative = function(code)
for k, v in pairs(Usb.Relative) do
if code == v then
return (k)
end
end
return nil
end
UsbHelpers.GetAbsolute = function(code)
for k, v in pairs(Usb.Absolute) do
if code == v then
return (k)
end
end
return nil
end
UsbHelpers.GetSwitch = function(code)
for k, v in pairs(Usb.Switch) do
if code == v then
return (k)
end
end
return nil
end
UsbHelpers.GetMisc = function(code)
for k, v in pairs(Usb.Misc) do
if code == v then
return (k)
end
end
return nil
end
UsbHelpers.GetLed = function(code)
for k, v in pairs(Usb.Led) do
if code == v then
return (k)
end
end
return nil
end
UsbHelpers.GetRepeat = function(code)
for k, v in pairs(Usb.Repeat) do
if code == v then
return (k)
end
end
return nil
end
UsbHelpers.GetSound = function(code)
for k, v in pairs(Usb.Sound) do
if code == v then
return (k)
end
end
return nil
end
UsbHelpers.GetForceFeedback = function(code)
for k, v in pairs(Usb.FF) do
if code == v then
return (k)
end
end
return nil
end
UsbHelpers.GetKey = function(code)
for k, v in pairs(Usb.Key) do
if code == v then
return (k)
end
end
return nil
end
UsbHelpers.GetButton = function(code)
for k, v in pairs(Usb.Button) do
if code == v then
return (k)
end
end
return nil
end
UsbHelpers.GetKeyButton = function(code)
for k, v in pairs(Usb.Key) do
if code == v then
return (k)
end
end
for k, v in pairs(Usb.Button) do
if code == v then
return (k)
end
end
return nil
end
UsbHelpers.Actions = {
[0] = "Released",
[1] = "Pressed",
[2] = "Held"
}
return UsbHelpers
This example uses the Lua Helper module shown above
Local=UTC + bias
The bias is time zone offset plus the daylight savings if in effect. The bias is retrieve using the Lua function os.date and os.time. It assumes that the Lua function os.time returns the number of seconds since the start time (called "epoch").
If the time value is outside the allowable range of times, usually Jan 01 1970 00:00:00 to Jan 19 2038 03:14:07 the bias will be retrieved using the equivalent year inside the allowable range.
Note: Two years are considered equivalent if they have the same leap year and starting weekday.
The function that needs local time support are date(true) to get the current UTC time; date(false) to get the current local time; date(num_time), dateObj:getbias(), dateObject:fmt(str) when str has a '%Z' or '%z'.
Parsable date value is a lua value that can be converted to a dateObject. This value must be num_time, or tbl_date, or str_date or bool_now argument described in the date Lua USB method.
If a function needs a month value it must be a string or a number. If the value is a string, it must be the name of the month full or abbreviated. If the value is a number, that number must be 1-12 (January-December).
| Index | Abbreviation | Full Name |
|---|---|---|
|
1 |
Jan |
January |
|
2 |
Feb |
February |
|
3 |
Mar |
March |
|
4 |
Apr |
April |
|
5 |
May |
May |
|
6 |
Jun |
June |
|
7 |
Jul |
July |
|
8 |
Aug |
August |
|
9 |
Sep |
September |
|
10 |
Oct |
October |
|
11 |
Nov |
November |
|
12 |
Dec |
December |
If the value does not represent month, that is equivalent to passing a nil value.
dateObject is a table containing date and time value. It has a metatable for manipulation and retrieval of dates and times. Use the __call method of date to construct a dateObject.
How Date and Time are Stored in dateObject
Time is stored in dateObject as Ticks or Day Fraction. Date is stored in dateObject as Day Number. Ticks is time unit per seconds. For example, if the tick unit is 1000000. 0.25 seconds is equivalent to 250000 ticks (0.25*1000000). Day number, is the number days since the epoch, which is January 1, 0001 AD. Example.
dobj = date("15:49:59.3669")
If the tick unit is 1000000, dobj store this time as 56999366900 ticks and 0 days.
((((15*60)+49)*60)+59.3669)*1000000 == 56999366900
dobj = date("Jan-5-0001 10:30:15")
If the tick unit is 1000000, dobj stores this date and time as 37815000000 ticks and 4 days.
| Day # | Date |
|---|---|
|
0 |
Jan 1 0001 |
|
1 |
Jan 2 0001 |
|
2 |
Jan 3 0001 |
|
3 |
Jan 4 0001 |
|
4 |
Jan 5 0001 |
|
5 |
Jan 6 0001 |
|
... |
... |
Tip: The default tick unit is 1000000 (micro-second-ticks).
The dateObject module provides many different functions. dateObject: adddays, addhours, addminutes, addmonths, addseconds, addticks, addyears, copy, fmt, getbias, getclockhour, getdate, getday, getfracsec, gethours, getisoweekday, getisoweeknumber, getisoyear, getminutes, getmonth, getseconds, getticks, gettime, getweekday, getweeknumber, getyear, getyearday, setday, sethours, setisoweekday, setisoweeknumber, setisoyear, setminutes, setmonth, setseconds, setticks, setyear, spandays, spanhours, spanminutes, spanseconds, spanticks, tolocal, toutc.
Tip: To learn more about dateObject, see dateObject Methods.
The date module provides three different functions. .diff, .epoch, and .isleapyear. Additionally, there is a metamethod of date, which is the Lua USB function.
Subtracts the date and time value of two dateObject and returns the resulting dateObject.
Syntax
date.diff(var_date1, var_date2)
Arguments
var_date1: Requires a dateObject or
var_date2: Requires a parsable date value
Returns
The resulting dateObject if successful.
Returns nil in case of error.
Example
-- get the days between two dates
d = date.diff("Jan 7 1563", date(1563, 1, 2))
assert(d:spandays()==5)
Returns dateObject whose date and time value is the Operating System epoch.
Syntax
date.ephoch()
Arguments
var_date1: Requires a dateObject.
var_date2: Requires a parsable date value.
Returns
The resulting dateObject if successful.
Returns nil in case of error.
Example
assert(date.epoch()==("Jan 1 1920"))
Checks if a number or dateObject is a leapyear.
Syntax
date.isleapyear(var_year)
Arguments
var_year: A number, dateObject, or parsable date value.
Returns
Returns true if var_year is a leap year.
Returns false if var_year is not leap year.
Example
d = date(1776, 1, 1)
asert(date.isleapyear(d))
assert(date.isleapyear(d:getyear()))
assert(date.isleapyear(1776))
Constructs a dateObject. This is a metamethod of date. Remember, date:_call() is the same as date().
Syntax
date.(num_time)
date.(tbl_date)
date.(str_date)
date.(bool_now)
date.(int_year, var_month, int_day, [num_hour], [num_min], [num_sec], [int_ticks]).
Arguments
num_time: Required number value. Represents the number of seconds in Universal Coordinated Time between the specified value and the System epoch.
tbl_date: Required table value. Time (hour,,min, sec, or msec) must be supplied if date (year, month, and day) is not given, vice versa. The constructor will look for the value of this key:
year- an integer, the full year, for example, 1969. Required if month and day is given.month- a parsable month value. Required if year and day is given.day- an integer, the day of month from 1 to 31. Required if year and month is given.hour- a number, hours value, from 0 to 23, indicating the number of hours since midnight. (default = 0).min- a number, minutes value, from 0 to 59. (default = 0).sec- a number, seconds value, from 0 to 59. (default = 0).
str_date: Required string value. It must have number / words representing date and / or time. Use commas and spaces as delimiters. Strings enclosed by parenthesis is treated as a comment and is ignored. The stated day of the week is ignored whether its correct or not. A string containing an invalid date is an error. For example, a string containing two years or two months is an error. Time must be supplied if date is not given, vice versa.
- Time Format: Hours, minutes, and seconds are separated by colons, although all need not be specified. "10:", "10:11", and "10:11:12" are all valid. If the 24-hour clock is used, it is an error to specify "PM" for times later than 12 noon. For example, "23:15 PM" is an error.
- Time Zone Format: First character is a sign "+" (east of UTC) or "-" (west of UTC). Hours and minutes offset are separated by colons.
- Example:
assert( date("Jul 27 2006 03:56:28 +2:00") == date(2006,07,27,1,56,28))
- Another format is [sign][number] If [number] is less than 24, it is the offset in hours e.g. "-10" = -10 hours. Otherwise it is the offset in hundred hours e.g. "+75" = "+115" = +1.25 hours.
- Examples:
assert(date("Jul 27 2006 -75 ") == date(2006,07,27,1,15,0)), Orassert(date("Jul 27 2006 -115") == date(2006,07,27,1,15,0)).
bool_now: Required boolean value. if bool_now is false it returns the current local date and time. If bool_now is true it returns the current UTC date and time.
int_year: Required integer value. The year value.
var_month: Required. A parsable month value.
int_day: Required integer value. The day of month.
num_hour: Optional number value. Equal to the hours value. The default value is 0.
num_min: Optional number value. Equal to the minutes value. The default value is 0.
num_sec: Optional number value. Equal to the seconds value. The default value is 0.
int_ticks: Optional integer value. Equal to the ticks value. The default value is 0.
Returns
The resulting dateObject if successful.
Returns nil in case of error.
Example
a = date(2006, 8, 13) assert(a == date("Sun Aug 13 2006"))
b = date("Jun 13 1999") assert(b == date(1999, 6, 13))
c = date(1234483200) assert(c == date("Feb 13 2009"))
d = date({year=2009, month=11, day=13, min=6})
assert(d == date("Nov 13 2009 00:06:00"))
e = date() assert(e)
Portions of this topic are reprinted under permission of the LuaDate license.
