Add ESP-NOW pendant/peripheral support with pairing mode#1744
Add ESP-NOW pendant/peripheral support with pairing mode#1744figamore wants to merge 15 commits into
Conversation
…r drops an ok/error/ALARM or truncates, with per-connection TCP_NODELAY + gate ESP-NOW motion so a JogCancel can't be overrun by queued jogs.
| TMCStepper@>=0.7.0,<1.0.0 | ||
| thingpulse/ESP8266 and ESP32 OLED driver for SSD1306 displays@4.4.1 | ||
| ; Enable this with OLED*.cpp/SSD1306_I2C.cpp below when building OLED display support - disabled to save ~2% flash capcity | ||
| ; thingpulse/ESP8266 and ESP32 OLED driver for SSD1306 displays@4.4.1 |
There was a problem hiding this comment.
This seems like something that should be discussed instead of just breaking existing functionality.
There was a problem hiding this comment.
The ESP-NOW implementation plus existing OLED support pushes the standard wifi build past the 4 MB ESP32 upload limit. I tried to make both fit together, but it does not seem realistically feasible with the current partition size. The original non-ESP-NOW build with OLED is already around 98.3% flash usage. ESP-NOW without OLED is around 98.0%, but enabling both exceeds the available flash space.
There was a problem hiding this comment.
I chose OLED as the thing to strip because the FluidNC wiki states that it would likely be removed at some point. That made it feel like the least bad tradeoff. If other ideas come to mind to shave off some flash space, please let me know.
There was a problem hiding this comment.
This is the sort of global-impact thing that should be discussed in the developers-forum channel in Discord.
| // A realtime jog cancel is a stream barrier. Discard any | ||
| // partially assembled or already-buffered commands from the | ||
| // issuing channel so stale jog input cannot execute afterward. | ||
| channel.flushRx(); |
There was a problem hiding this comment.
What can this break?
There was a problem hiding this comment.
I changed this so the global realtime jog cancel path no longer flushes every channel. Instead, ESP-NOW overrides flushRx() and only clears its own receive/reassembly buffers when an ESP-NOW jog cancel is received. That avoids affecting Telnet/WebSocket/serial behavior while still preventing stale fragmented ESP-NOW jog data from executing after cancel.
Please see 4bde9f1
There was a problem hiding this comment.
Again, this is a discussion topic. It might be the case that flushing is appropriate on all channels.
| size_t queueFree() const; | ||
| bool queueLine(const uint8_t* data, size_t len, size_t reserve); | ||
| void flushQueue(int sockfd); | ||
| void queueCompletedLine(); |
There was a problem hiding this comment.
The Telnet task watchdog issue occurs when a Telnet client with an auto-report interval disconnects abruptly DURING a job that is running. For example, it happens if I use a Telnet g-code sender and suddenly disconnect from WiFi or otherwise ungracefully close the connection. I put together this simple python script to test this out.
The Telnet task watchdog issue occurs when a Telnet connection I added a simple Python test that subscribes to an auto-report interval via Telnet.
Steps to induce machine stall + task watchdog trigger:
- Ensure current version is FluidNC v4.0.3
- Run the following Python script BEFORE running a job.
- Start a job or long-homing run.
- Disconnect the WiFi entirely while the job is in progress.
- Observe a stall after 15-30 seconds, with eventual task watchdog trigger about a minute or more later
- The same procedure does not induce a failure after the proposed fix.
Here is the backtrace:
E (109355) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (109355) task_wdt: - async_tcp (CPU 0)
E (109355) task_wdt: Tasks currently running:
E (109355) task_wdt: CPU 0: IDLE0
E (109355) task_wdt: CPU 1: IDLE1
E (109355) task_wdt: Aborting.
abort() was called at PC 0x4014921c on core 0
#0 0x4014921c in task_wdt_isr at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_system/task_wdt.c:176 (discriminator 3)
Backtrace: 0x400853ad:0x3ffbf0bc |<-CORRUPTED
#0 0x400853ad in panic_abort at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_system/panic.c:408
#1 0x3ffbf0bc in port_IntStack at ??:?
ELF file SHA256: fd8828ae6dd09da9
Rebooting...
Test:
#!/usr/bin/env python3
"""
Reproduce a FluidNC Telnet auto-report WiFi-disconnect crash on FluidNC v4.0.3
Instructions:
1. Ensure current version is FluidNC v4.0.3
2. Run this script before running a job.
3. Start a job or long-homing run.
4. Disconnect the WiFi entirely while the job is in progress.
5. Observe a stall after 15-30 seconds, with eventual task watchdog trigger about a minute or more later.
Usage:
python3 test.py 192.168.1.201
"""
import argparse
import socket
import sys
import time
def connect(host, port):
s = socket.socket()
s.connect((host, port))
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) # like FluidDial
s.settimeout(0.2)
return s
def drain(s, seconds=0.5):
"""Read whatever arrives for up to `seconds`; return it as one line."""
s.settimeout(seconds)
out = b""
try:
while True:
chunk = s.recv(4096)
if not chunk:
break
out += chunk
except (socket.timeout, OSError):
pass
return " ".join(out.decode(errors="replace").split())
def send_line(s, line):
s.sendall(line.encode("ascii") + b"\n")
def main():
ap = argparse.ArgumentParser(description="FluidNC Telnet auto-report WiFi-disconnect repro")
ap.add_argument("host", help="FluidNC IP or hostname")
ap.add_argument("--port", type=int, default=23)
ap.add_argument("--interval", type=int, default=200, help="auto-report interval in ms")
args = ap.parse_args()
sock = connect(args.host, args.port)
greeting = drain(sock, 1.0)
if greeting:
print(greeting[:160])
send_line(sock, "$I")
print(drain(sock, 1.0)[:160])
send_line(sock, "$G")
print(drain(sock, 1.0)[:160])
send_line(sock, f"$RI={args.interval}")
response = drain(sock, 1.0)
print(response[:160])
if "auto report interval set" not in response:
print("WARNING: did not see auto-report confirmation")
print()
print("1. Start a new job AFTER this script is already running")
print("2. Disconnect this computer from WiFi.")
print("Leave this script running until the network disconnects. A stall should occur within 16-30 seconds, followed by a task watchdog trigger and reboot")
try:
while True:
data = drain(sock, 1.0)
if data:
print(data[:160])
time.sleep(0.1)
except OSError as exc:
print(f"Socket ended after WiFi/network disconnect: {exc}")
return 0
if __name__ == "__main__":
try:
sys.exit(main())
except KeyboardInterrupt:
sys.exit(130)…og cancel path no longer flushes every channel
…ormant extender/listener features + re-enable OLED support
| -<Extenders> | ||
| -<Pins/ExtPinDetail.cpp> | ||
| -<Listeners> | ||
| -<Status_outputs.cpp> |
There was a problem hiding this comment.
Omitting Status_outputs is probably not a good idea because it is pretty common for people to use it with Rodent, and in fact the config snippet on the Rodent wiki page includes status_outputs: .
There was a problem hiding this comment.
Status_outputs is back in the build. I hadn't realized the Rodent's dependence on it.
| #include "Configuration/Configurable.h" | ||
| #include "CoolantControl.h" | ||
| #include "Kinematics/Kinematics.h" | ||
| #ifndef DISABLE_PIN_EXTENDERS |
There was a problem hiding this comment.
I am going to merge this with the Portability branch for testing but it conflicts with the defines I used. I have
#if SUPPORT_LISTENERS
and
#if SUPPORT_PIN_EXTENDERS
with this in platformio.ini
; Reinstate the following to support pin extenders and listeners
; and define SUPPORT_PIN_EXTENDERS=1 and SUPPORT_LISTENERS=1
-<Extenders/*>
-<Pins/ExtPinDetail.cpp>
-<Listeners/*>
There was a problem hiding this comment.
I switched the extender/listener gating from my DISABLE_* defines over to your SUPPORT_PIN_EXTENDERS / SUPPORT_LISTENERS so it matches the Portability branch. I also ran a test merge against Portability to be sure - MachineConfig, Main, and Protocol merge cleanly now, so the define clash should be gone. There's still some overlap in platformio.ini from the build system changes on the Portability branch (plus a one-line include in Pin.cpp). Let me know if you'd like me to address those, or if you'll be resolving the conflicts during the merge.
…cros to match the Portability branch
|
I have a clean merge with my local copy of Portability and wifi builds cleanly. The macos build fails due to it trying to compile ESPNow/ . I can fix that with an exclusion rule but it brings up the fact that a core axiom of the Portability work was to remove all direct dependencies on ESP from src. To that end, I propose to move ESPNow underneath esp32/ |
|
The move underneath esp32/ succeeded. Now I have to deal with a problem whereby the include of lwip/sockets.h in Telnet breaks the posix build. |
|
I am having trouble making TelnetClient work in the rp2040 build. I am questioning the wisdom of mixing Arduino API and direct socket calls. I am also questioning whether the Telnet changes are within the scope of the ESPNow work. |
|
I managed to do the non-blocking TelnetClient using Arduino functions, more or less. I pushed the merged version of Portability upstream. I compile-tested all of the ESP and RP targets, plus the macos target, but I haven't tested anything yet. |
|
Great to hear that! 👏 Please let me know if there's anything you'd like me to revise. |
|
Testing would be appreciated |
This PR adds ESP-NOW as a native FluidNC channel for direct communication with FluidDial pendants and peripherals.
Features
Pairing and Security
Pairing is allowed only during a user-initiated 60-second pairing window.
The protocol uses:
Commands
$espnow/pairopens the pairing window.$espnow/cancelcloses the pairing window.$espnow/listlists paired pendants.$espnow/unpair=<index>removes one pendant.$espnow/unpair=0removes all paired pendants.Additional Stability Work
This branch also makes Telnet output non-blocking and cleans up stalled clients to reduce the risk of task-watchdog resets.
Note
OLED support has been sacrificed from the ESP32 WiFi build to make room for the ESP-NOW channel, pairing, crypto, and peer-management code within the existing flash partition constraints. The OLED code is not deleted from the tree; it is excluded from this build variant and can be re-enabled later if the partition layout or feature budget changes.