LoRa mesh networking with MeshCore protocol
Provides access to the MeshCore mesh network for peer-to-peer communication. Supports node discovery, channel messaging, direct messages, and identity management with Ed25519 cryptography. The mesh runs in the background, automatically handling packet routing and retransmission. Subscribe to bus messages (channel/message, message/received) for incoming data.
| Parameter | Description |
|---|---|
| route_type | Route type constant (FLOOD=1, DIRECT=2) |
| payload_type | Payload type constant (ADVERT=4, GRP_TXT=5, etc.) |
| payload | Binary string payload (max 184 bytes) |
| path | Optional binary string of path hashes (default: empty) |
local pkt = ez.mesh.build_packet(
ez.mesh.ROUTE.FLOOD,
ez.mesh.PAYLOAD.GRP_TXT,
encrypted_message
)
ez.mesh.queue_send(pkt)
ez.mesh.clear_packet_queue() -- Discard unprocessed packets
ez.mesh.clear_tx_queue() -- Cancel all pending transmissions
| Parameter | Description |
|---|---|
| data | Binary string to sign |
local message = "Hello, mesh!"
local sig = ez.mesh.ed25519_sign(message)
local pubkey = ez.mesh.get_public_key()
assert(ez.mesh.ed25519_verify(message, sig, pubkey))
| Parameter | Description |
|---|---|
| data | Binary string that was signed |
| signature | 64-byte Ed25519 signature |
| pub_key | 32-byte Ed25519 public key |
local valid = ez.mesh.ed25519_verify(message, signature, sender_pubkey)
if valid then
print("Message authenticated!")
end
| Parameter | Description |
|---|---|
| enabled | Boolean to enable/disable |
ez.mesh.enable_packet_queue(true)
-- In main loop:
while ez.mesh.has_packets() do
local pkt = ez.mesh.pop_packet()
process_packet(pkt)
end
local interval = ez.mesh.get_announce_interval()
if interval == 0 then
print("Auto-announce disabled")
end
local count = ez.mesh.get_node_count()
print("Discovered " .. count .. " nodes")
local id = ez.mesh.get_node_id() -- e.g., "A1B2C3D4E5F6"
local name = ez.mesh.get_node_name() -- e.g., "Alice's T-Deck"
for _, node in ipairs(ez.mesh.get_nodes()) do
print(node.name, node.rssi .. "dBm", node.hops .. " hops")
end
local enabled = ez.mesh.get_path_check()
local hash = ez.mesh.get_path_hash() -- e.g., 0xA1 = 161
local pubkey = ez.mesh.get_public_key()
local shared = ez.mesh.calc_shared_secret(other_pubkey)
local hex = ez.mesh.get_public_key_hex()
print("Public key: " .. hex)
local rx = ez.mesh.get_rx_count()
local short = ez.mesh.get_short_id() -- e.g., "A1B2C3"
local tx = ez.mesh.get_tx_count()
local rx = ez.mesh.get_rx_count()
print("TX: " .. tx .. ", RX: " .. rx)
local cap = ez.mesh.get_tx_queue_capacity() -- e.g., 16
local pending = ez.mesh.get_tx_queue_size()
print(pending .. " packets pending")
local interval = ez.mesh.get_tx_throttle() -- e.g., 100
if ez.mesh.has_packets() then
local pkt = ez.mesh.pop_packet()
end
if ez.mesh.is_initialized() then
local nodes = ez.mesh.get_nodes()
end
if not ez.mesh.is_tx_queue_full() then
ez.mesh.queue_send(packet)
end
| Parameter | Description |
|---|---|
| route_type | Route type constant |
| payload_type | Payload type constant |
| version | Optional version (default: 0) |
local header = ez.mesh.make_header(ez.mesh.ROUTE.FLOOD, ez.mesh.PAYLOAD.GRP_TXT)
| Parameter | Description |
|---|---|
| callback | Function(packet_table) called with {channel_hash, data, sender_hash, rssi, snr} |
ez.mesh.on_group_packet(function(pkt)
local decrypted = Channel.decrypt(pkt.channel_hash, pkt.data)
end)
| Parameter | Description |
|---|---|
| callback | Function(node_table) called when node discovered |
ez.mesh.on_node_discovered(function(node)
print("Found: " .. node.name)
end)
| Parameter | Description |
|---|---|
| callback | Function(packet_table) returning handled, rebroadcast booleans |
ez.mesh.on_packet(function(pkt)
if pkt.payload_type == ez.mesh.PAYLOAD.ADVERT then
-- Handle ADVERT packet
end
return false, true -- not handled, do rebroadcast
end)
local count = ez.mesh.packet_count()
print(count .. " packets waiting")
| Parameter | Description |
|---|---|
| header_byte | Single byte header value |
local route, ptype, ver = ez.mesh.parse_header(pkt.header)
if ptype == ez.mesh.PAYLOAD.ADVERT then
-- Handle ADVERT
end
local pkt = ez.mesh.pop_packet()
if pkt then
print("Received:", pkt.payload_type, pkt.rssi .. "dBm")
end
| Parameter | Description |
|---|---|
| data | Binary string of serialized packet |
local pkt = ez.mesh.build_packet(ez.mesh.ROUTE.FLOOD, ez.mesh.PAYLOAD.GRP_TXT, data)
if ez.mesh.queue_send(pkt) then
print("Message queued")
end
| Parameter | Description |
|---|---|
| data | Binary string of raw packet bytes |
ez.mesh.on_packet(function(pkt)
-- Manually rebroadcast after processing
ez.mesh.schedule_rebroadcast(raw_bytes)
end)
ez.mesh.send_announce() -- Announce presence to mesh
| Parameter | Description |
|---|---|
| channel_hash | Single byte channel identifier (first byte of channel key hash) |
| encrypted_data | Pre-encrypted payload (MAC + ciphertext) |
local encrypted = Channel.encrypt("Hello everyone!")
ez.mesh.send_group_packet(channel.hash, encrypted)
| Parameter | Description |
|---|---|
| data | Binary string of serialized packet |
local pkt = ez.mesh.build_packet(ez.mesh.ROUTE.FLOOD, ez.mesh.PAYLOAD.GRP_TXT, data)
ez.mesh.send_raw(pkt) -- Immediate transmission
| Parameter | Description |
|---|---|
| ms | Integer - interval in milliseconds (0 to disable) |
ez.mesh.set_announce_interval(120000) -- Announce every 2 minutes
ez.mesh.set_announce_interval(0) -- Disable auto-announce
| Parameter | Description |
|---|---|
| name | New node name |
ez.mesh.set_node_name("Bob's T-Deck")
| Parameter | Description |
|---|---|
| enabled | Boolean - when true, packets with our hash in path are skipped |
ez.mesh.set_path_check(false) -- Disable for debugging
| Parameter | Description |
|---|---|
| ms | Milliseconds between transmissions (default 100) |
ez.mesh.set_tx_throttle(200) -- Slower, less channel usage
ez.mesh.set_tx_throttle(50) -- Faster, more responsive
-- In main loop
ez.mesh.update()
table {channel, sender, sender_name, text, timestamp, is_self}ez.bus.subscribe("channel/message", function(msg)
print(string.format("[%s] %s: %s",
msg.channel, msg.sender_name, msg.text))
end)
string Format: "channel_name:count"ez.bus.subscribe("channel/unread", function(data)
local channel, count = data:match("(.+):(%d+)")
self:update_badge(channel, tonumber(count))
end)
string Number of nodes as stringez.bus.subscribe("mesh/node_count", function(count)
self.node_count = tonumber(count) or 0
end)
string Message ID that was acknowledgedez.bus.subscribe("message/acked", function(msg_id)
self:mark_delivered(msg_id)
end)
table {from, from_name, text, timestamp, conversation_id}ez.bus.subscribe("message/received", function(msg)
Toast.show("DM from " .. msg.from_name)
end)