Skip to main content

I2C Helpers

Drive an I2C bus (native Linux i2c-dev or a CH347 USB bridge) from Lua. Each helper builds a request message and sends it on the configured I2C connection. All helpers return true once the request is queued, or raise a Lua runtime error on invalid parameters.

All helpers accept an optional trailing connection_id (default: 0) selecting the target connection, followed by an optional delay_ms that schedules the request delay_ms milliseconds in the future instead of sending it immediately (default: 0 — send now). This is handy for inserting settling time between bus operations, e.g. issuing a write and reading the result a few milliseconds later.

Request Functions​

i2c_scan(addr_start, addr_end, connection_id, delay_ms) → boolean​

Probe the bus for devices in the inclusive [addr_start, addr_end] address range.

  • addr_start (number, optional) - First 7-bit address to probe (default: 0x03)
  • addr_end (number, optional) - Last 7-bit address to probe (default: 0x77)
  • connection_id (number, optional) - Target connection (default: 0)
  • delay_ms (number, optional) - Delay delivery by this many milliseconds (default: 0)

The defaults match the conventional i2cdetect range (skipping reserved low/high addresses).

i2c_read(address, register, length, connection_id, delay_ms) → boolean​

Read length bytes from a device, optionally after writing a register/command byte first (the common write-register-then-read pattern).

  • address (number) - 7-bit slave address 0x00–0x7F (required; 10-bit addressing is not supported)
  • register (number, optional) - Register/command byte written before the read; pass nil to read directly
  • length (number) - Number of bytes to read (required, must be > 0)
  • connection_id (number, optional) - Target connection (default: 0)
  • delay_ms (number, optional) - Delay delivery by this many milliseconds (default: 0)

i2c_write(address, register, data, connection_id, delay_ms) → boolean​

Write bytes to a device, optionally prefixed by a register/command byte. A request must carry a register and/or payload bytes.

  • address (number) - 7-bit slave address 0x00–0x7F (required)
  • register (number, optional) - Register/command byte written before data; pass nil to write raw bytes
  • data (string) - Payload bytes to write (can be empty if register is set)
  • connection_id (number, optional) - Target connection (default: 0)
  • delay_ms (number, optional) - Delay delivery by this many milliseconds (default: 0)

Handling Responses​

Read and scan results arrive as ordinary RX messages in on_receive(). Use message:get_metadata("i2c_op") to identify the result type. Write confirmations and errors are emitted as engine events, not RX messages.

i2c_op metadataMessage kindPayload
read_responseRXThe bytes read; i2c_address (and i2c_register, if any) in metadata
scan_responseRXA 0x78-byte i2cdetect-style grid: grid[addr] = addr if a device ACKed
write_responseeventi2c_write event with i2c_address metadata
erroreventi2c_error event; the error value holds the message

Example: read a register and parse the response​

local SENSOR = 0x48   -- e.g. a temperature sensor
local TEMP_REG = 0x00

function on_timer(now_ms)
-- Write the register pointer, then read 2 bytes back
i2c_read(SENSOR, TEMP_REG, 2, 0)
end

function on_receive()
if message:get_metadata("i2c_op") ~= "read_response" then return false end

-- 12-bit temperature in the top bits of a big-endian 16-bit word
local raw = read_i16_be(message.payload, 1)
local celsius = raw / 256.0
message:add_float_value("temp_c", celsius)
return true
end

Example: scan the bus and log detected addresses​

function on_start()
i2c_scan(0x03, 0x77, 0)
end

function on_receive()
if message:get_metadata("i2c_op") ~= "scan_response" then return false end

for addr = 0, #message.payload - 1 do
-- grid[addr] == addr means the device ACKed at that address
if read_u8(message.payload, addr + 1) ~= 0 then
log("info", string.format("I2C device found at 0x%02X", addr))
end
end
return false
end