Skip to content

feat(lua): add esp_now Lua module for ESP-NOW peer-to-peer messaging#140

Open
ChenQi0920 wants to merge 2 commits into
espressif:masterfrom
ChenQi0920:feat/esp-now
Open

feat(lua): add esp_now Lua module for ESP-NOW peer-to-peer messaging#140
ChenQi0920 wants to merge 2 commits into
espressif:masterfrom
ChenQi0920:feat/esp-now

Conversation

@ChenQi0920

Copy link
Copy Markdown

Description

Add a new esp_now Lua module that wraps the ESP-IDF ESP-NOW API, enabling
connectionless, low-latency peer-to-peer messaging between Espressif devices
without joining the same AP. It is source-compatible with ESP-IDF 5.5.x and 6.x.

Motivation: give Lua scripts and skills a first-class way to build direct
device-to-device links (sensor/remote nodes, broadcast fan-out) that do not
need TCP/IP, complementing the existing ble and socket-based options.

What's included:

  • Native module lua_module_esp_now exposing init/deinit, send, full peer
    management (add/mod/del/get/exists/fetch, peer counts), PMK/LMK encryption,
    channel and MAC helpers, and an event queue (on_event / process_events)
    delivering recv and send notifications.
  • Constants BROADCAST_MAC, MAX_DATA_LEN, IFIDX, SEND_STATUS and a
    stats() counter table.
  • Module registration and Kconfig wiring in components/common/app_claw/
    (app_lua_modules.c, Kconfig, idf_component.yml).
  • esp_now skill (SKILL.md) with ready-to-run scripts
    broadcast_espnow.lua and start_espnow_listener.lua.
  • Docs (README.md) covering API, options, events, and common errors.
  • Self-test script test/test_esp_now.lua.

Related

N/A

Testing

  • Built the firmware with the new module enabled and flashed to real hardware.
  • Ran the module through a full end-to-end scenario: a simulated ESP-NOW
    temperature/humidity sensor node with auto-pairing and deep-sleep reporting,
    talking to ESP-Claw.
  • Verified the complete flow: pairing (PAIR_REQ / PAIR_RESP) -> data
    reporting (DATA) -> acknowledgement (ACK) -> deep sleep -> wake and reuse.
  • All four message types were sent and received correctly in both directions.
  • Also exercised test/test_esp_now.lua for basic bring-up (init, version,
    peer management, broadcast send, event processing, deinit).

Checklist

Before submitting a Pull Request, please ensure the following:

  • 🚨 This PR does not introduce breaking changes.
  • All CI checks (GH Actions) pass.
  • Documentation is updated as needed.
  • Tests are updated or added as necessary.
  • Code is well-commented, especially in complex areas.
  • Git history is clean — commits are squashed to the minimum necessary.

Co-authored-by: Cursor <cursoragent@cursor.com>
@CLAassistant

CLAassistant commented Jul 9, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new ESP-NOW integration to ESP-Claw’s Lua environment, enabling low-latency, connectionless peer-to-peer messaging between nearby Espressif devices via a native Lua module plus accompanying skill scripts and documentation.

Changes:

  • Introduces require("esp_now") Lua native module wrapping ESP-IDF ESP-NOW APIs (init/deinit, send, peer management, radio helpers, events, stats).
  • Adds an event-queue-based delivery mechanism (on_event + process_events) for recv/send notifications.
  • Wires the module into build/config (Kconfig + component dependency + Lua module registry) and provides docs, skill scripts, and a bring-up self-test script.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
components/lua_modules/lua_module_esp_now/src/lua_module_esp_now.c Core Lua module API bindings and registration for ESP-NOW.
components/lua_modules/lua_module_esp_now/src/lua_module_esp_now_events.c Event allocation/queueing and Lua-side event dispatch (on_event / process_events).
components/lua_modules/lua_module_esp_now/src/lua_module_esp_now_priv.h Internal runtime state, event definitions, and private APIs shared across translation units.
components/lua_modules/lua_module_esp_now/src/lua_module_esp_now_compat.h ESP-IDF 5.5.x / 6.x compatibility helper for send-callback destination MAC extraction.
components/lua_modules/lua_module_esp_now/src/lua_module_esp_now.h Public header exposing luaopen_esp_now() and module registration entrypoint.
components/lua_modules/lua_module_esp_now/CMakeLists.txt Component build integration, gated by CONFIG_APP_CLAW_LUA_MODULE_ESP_NOW.
components/common/app_claw/Kconfig Adds APP_CLAW_LUA_MODULE_ESP_NOW toggle for enabling the module.
components/common/app_claw/idf_component.yml Adds conditional dependency entry for lua_module_esp_now.
components/common/app_claw/app_lua_modules.c Registers the new module in the app’s Lua module registry when enabled.
components/lua_modules/lua_module_esp_now/README.md Module API documentation, usage rules, constants, and common errors.
components/lua_modules/lua_module_esp_now/skills/esp_now/SKILL.md Skill documentation describing agent-facing usage patterns and constraints.
components/lua_modules/lua_module_esp_now/skills/esp_now/scripts/start_espnow_listener.lua Skill script to init ESP-NOW and print received frames in a loop.
components/lua_modules/lua_module_esp_now/skills/esp_now/scripts/broadcast_espnow.lua Skill script to perform a one-off broadcast send and wait for completion.
components/lua_modules/lua_module_esp_now/test/test_esp_now.lua Bring-up/self-test script covering init/version/peer mgmt/send/events/deinit.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +210 to +213
err = esp_wifi_get_mode(&mode);
if (err != ESP_OK) {
return lua_espnow_push_ok_or_err(L, ESP_ERR_INVALID_STATE);
}
Comment on lines +160 to +176
lua_getfield(L, 1, "lmk");
if (!lua_isnil(L, -1)) {
size_t len = 0;
const char *lmk = luaL_checklstring(L, -1, &len);
if (len != ESP_NOW_KEY_LEN) {
return luaL_error(L, "lmk must be a %d-byte raw string", ESP_NOW_KEY_LEN);
}
memcpy(peer->lmk, lmk, ESP_NOW_KEY_LEN);
peer->encrypt = true;
}
lua_pop(L, 1);

lua_getfield(L, 1, "encrypt");
if (!lua_isnil(L, -1)) {
peer->encrypt = lua_toboolean(L, -1);
}
lua_pop(L, 1);
Comment on lines +304 to +307
data = luaL_checklstring(L, 2, &len);
if (len == 0) {
return luaL_error(L, "data must be a non-empty string");
}
Co-authored-by: Cursor <cursoragent@cursor.com>
@ChenQi0920

Copy link
Copy Markdown
Author

Thanks for the review. I addressed the three comments:

  • marked the Wi-Fi mode variable as intentionally unused after the state check;
  • parse encrypt before lmk so LMK always forces encryption on;
  • reject ESP-NOW payloads larger than ESP_NOW_MAX_DATA_LEN before calling esp_now_send.

I also added a test case covering the LMK/encrypt behavior and rebuilt locally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants