Skip to main content

SPI Helpers

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

The contract is identical for both backends. The cs (chip-select) argument is a 0-based line index for the CH347 bridge; it is ignored by the native spidev backend, where the chip-select is fixed by the device node. 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.

Request Functions​

spi_write(cs, data, connection_id, delay_ms) → boolean​

Clock data out on the given chip-select, discarding anything read back.

  • cs (number, optional) - Chip-select line index (default: 0)
  • data (string) - Payload bytes to write (required, must be non-empty)
  • connection_id (number, optional) - Target connection (default: 0)
  • delay_ms (number, optional) - Delay delivery by this many milliseconds (default: 0)

spi_read(cs, data, length, connection_id, delay_ms) → boolean​

Clock out an optional data preamble (e.g. a command/address), then clock in length bytes.

  • cs (number, optional) - Chip-select line index (default: 0)
  • data (string) - Preamble bytes written before the read (can be empty)
  • length (number) - Number of bytes to clock in (required, must be > 0)
  • connection_id (number, optional) - Target connection (default: 0)
  • delay_ms (number, optional) - Delay delivery by this many milliseconds (default: 0)

spi_transfer(cs, data, connection_id, delay_ms) → boolean​

Full-duplex transfer: clock data out while simultaneously clocking the same number of bytes in.

  • cs (number, optional) - Chip-select line index (default: 0)
  • data (string) - Payload bytes to transfer (required, must be non-empty)
  • connection_id (number, optional) - Target connection (default: 0)
  • delay_ms (number, optional) - Delay delivery by this many milliseconds (default: 0)

Handling Responses​

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

spi_op metadataMessage kindPayload
read_responseRXThe bytes clocked in; spi_cs in metadata
transfer_responseRXThe bytes read back during the duplex transfer; spi_cs
write_responseeventspi_write event with spi_cs metadata
erroreventspi_error event; the error value holds the message

Example: read a register over SPI​

local READ_CMD = 0x80   -- many SPI sensors OR the register with 0x80 to read
local WHO_AM_I = 0x0F

function on_start()
-- Send {0x80 | reg} as preamble, then clock in 1 byte
spi_read(0, string.char(READ_CMD | WHO_AM_I), 1, 0)
end

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

local id = read_u8(message.payload, 1)
log("info", string.format("WHO_AM_I = 0x%02X", id))
message:add_int_value("who_am_i", id)
return true
end

Example: full-duplex transfer​

function on_timer(now_ms)
-- Clock out two command bytes; the device returns two bytes in the same frame
spi_transfer(0, string.char(0x9F, 0x00), 0)
end

function on_receive()
if message:get_metadata("spi_op") ~= "transfer_response" then return false end

local status = read_u8(message.payload, 2) -- 2nd byte read back
message:add_int_value("status", status)
return true
end