Replace QuickESPNow with local library#5624
Conversation
WalkthroughThis PR replaces the external ChangesESP-NOW Abstraction Migration
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
wled00/src/dependencies/espnow_wled/espnow_wled.cpp (1)
230-230: ⚡ Quick winUse debug macros instead of unconditional
Serial.printf.Line 230 bypasses
WLED_DEBUGgating and adds serial I/O in normal builds.As per coding guidelines: "Use `DEBUG_PRINTF()` / `DEBUG_PRINTLN()` for debug output (compiled out unless `-D WLED_DEBUG`)."Suggested fix
- if (err != 0 && isretransmit) Serial.printf("ESP-NOW send failed with error %d, inflight=%d\n", err, (int)espNow._inFlight); + if (err != 0 && isretransmit) DEBUG_PRINTF_P(PSTR("ESP-NOW send failed with error %d, inflight=%d\n"), err, (int)espNow._inFlight);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wled00/src/dependencies/espnow_wled/espnow_wled.cpp` at line 230, Replace the unconditional Serial.printf in espnow_wled.cpp (the ESP-NOW send failure log in the send callback/handler around the if (err != 0 && isretransmit) check) with the project debug macro so it is compiled out unless WLED_DEBUG is defined; use DEBUG_PRINTF (or DEBUG_PRINTLN if preferred) to emit the same formatted message ("ESP-NOW send failed with error %d, inflight=%d\n") and preserve the (int)espNow._inFlight cast and err variable so the debug output remains identical but gated by WLED_DEBUG.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@wled00/src/dependencies/espnow_wled/espnow_wled.cpp`:
- Around line 221-228: The retry branch that calls esp_now_send(...) when
isretransmit is set does not increment the in-flight counter, causing _inFlight
to go out of sync; update the block that contains the
esp_now_send(const_cast<uint8_t*>(BCAST), const_cast<uint8_t*>(data), len) call
so that after a successful send (check err == ESP_OK) you increment _inFlight,
e.g., modify the retry path around variables _inFlight and isretransmit to
++_inFlight when the send returns success to keep the in-flight counter
accurate.
- Around line 186-190: The ESP8266 branch currently ignores return values from
esp_now_register_recv_cb(_espnowRecvCB),
esp_now_register_send_cb(_espnowSentCB), and
esp_now_add_peer(const_cast<uint8_t*>(BCAST), ESP_NOW_ROLE_COMBO, channel,
nullptr, 0) and sets _running = true unconditionally; change this to check each
call's return code (like the ESP32 path), log or handle failures, avoid setting
_running when any registration/peer-add fails, and return/clean up appropriately
(e.g., deinit or unregister) on error so subsequent send/stop operations behave
consistently.
In `@wled00/src/dependencies/espnow_wled/espnow_wled.h`:
- Around line 7-11: The conditional include for ESP-NOW uses a generic `#else`
which can incorrectly include <esp_now.h> on non-target platforms; update the
guard to explicitly check for the ESP32 architecture by replacing the `#else` with
`#elif` defined(ARDUINO_ARCH_ESP32) so the block reads `#ifdef` ESP8266 / `#elif`
defined(ARDUINO_ARCH_ESP32) / `#endif` and include <espnow.h> for ESP8266 and
<esp_now.h> for ESP32 (references: macros ESP8266, ARDUINO_ARCH_ESP32 and the
include directives <espnow.h> and <esp_now.h>).
---
Nitpick comments:
In `@wled00/src/dependencies/espnow_wled/espnow_wled.cpp`:
- Line 230: Replace the unconditional Serial.printf in espnow_wled.cpp (the
ESP-NOW send failure log in the send callback/handler around the if (err != 0 &&
isretransmit) check) with the project debug macro so it is compiled out unless
WLED_DEBUG is defined; use DEBUG_PRINTF (or DEBUG_PRINTLN if preferred) to emit
the same formatted message ("ESP-NOW send failed with error %d, inflight=%d\n")
and preserve the (int)espNow._inFlight cast and err variable so the debug output
remains identical but gated by WLED_DEBUG.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 09fa15a1-2b61-4741-a209-3454a4ae2277
📒 Files selected for processing (6)
platformio.iniwled00/src/dependencies/espnow_wled/espnow_wled.cppwled00/src/dependencies/espnow_wled/espnow_wled.hwled00/udp.cppwled00/wled.cppwled00/wled.h
💤 Files with no reviewable changes (1)
- platformio.ini
| esp_now_set_self_role(ESP_NOW_ROLE_COMBO); // TODO: found no official documentation on this... quickespnow ESP_NOW_ROLE_SLAVE in STA mode and ESP_NOW_ROLE_CONTROLLER in AP mode | ||
| esp_now_register_recv_cb(_espnowRecvCB); | ||
| esp_now_register_send_cb(_espnowSentCB); | ||
| esp_now_add_peer(const_cast<uint8_t*>(BCAST), ESP_NOW_ROLE_COMBO, channel, nullptr, 0); | ||
| _running = true; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n wled00/src/dependencies/espnow_wled/espnow_wled.cpp | sed -n '175,200p'Repository: wled/WLED
Length of output: 1059
🏁 Script executed:
cat -n wled00/src/dependencies/espnow_wled/espnow_wled.cpp | sed -n '150,210p'Repository: wled/WLED
Length of output: 2527
🏁 Script executed:
find . -name "espnow_wled.h" -o -name "espnow_wled.hpp" | head -5Repository: wled/WLED
Length of output: 105
🏁 Script executed:
cat wled00/src/dependencies/espnow_wled/espnow_wled.h | grep -A 20 "init\|start\|begin"Repository: wled/WLED
Length of output: 2168
🏁 Script executed:
rg "esp_now_register_recv_cb|esp_now_register_send_cb|esp_now_add_peer" -A 2 -B 2Repository: wled/WLED
Length of output: 1961
Add error checks for ESP8266 callback and peer registration before marking as running.
The ESP8266 code path (lines 187–189) ignores return codes from esp_now_register_recv_cb(), esp_now_register_send_cb(), and esp_now_add_peer(), while the ESP32 path properly checks them. If any call fails, _running still becomes true and the function returns success, causing inconsistent behavior in subsequent send/stop operations.
Suggested fix
esp_now_set_self_role(ESP_NOW_ROLE_COMBO); // TODO: found no official documentation on this... quickespnow ESP_NOW_ROLE_SLAVE in STA mode and ESP_NOW_ROLE_CONTROLLER in AP mode
- esp_now_register_recv_cb(_espnowRecvCB);
- esp_now_register_send_cb(_espnowSentCB);
- esp_now_add_peer(const_cast<uint8_t*>(BCAST), ESP_NOW_ROLE_COMBO, channel, nullptr, 0);
+ if (esp_now_register_recv_cb(_espnowRecvCB) != 0) {
+ esp_now_deinit();
+ return false;
+ }
+ if (esp_now_register_send_cb(_espnowSentCB) != 0) {
+ esp_now_unregister_recv_cb();
+ esp_now_deinit();
+ return false;
+ }
+ if (esp_now_add_peer(const_cast<uint8_t*>(BCAST), ESP_NOW_ROLE_COMBO, channel, nullptr, 0) != 0) {
+ esp_now_unregister_recv_cb();
+ esp_now_unregister_send_cb();
+ esp_now_deinit();
+ return false;
+ }
_running = true;
return true;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@wled00/src/dependencies/espnow_wled/espnow_wled.cpp` around lines 186 - 190,
The ESP8266 branch currently ignores return values from
esp_now_register_recv_cb(_espnowRecvCB),
esp_now_register_send_cb(_espnowSentCB), and
esp_now_add_peer(const_cast<uint8_t*>(BCAST), ESP_NOW_ROLE_COMBO, channel,
nullptr, 0) and sets _running = true unconditionally; change this to check each
call's return code (like the ESP32 path), log or handle failures, avoid setting
_running when any registration/peer-add fails, and return/clean up appropriately
(e.g., deinit or unregister) on error so subsequent send/stop operations behave
consistently.
| else if (_inFlight > 0 && !isretransmit) { | ||
| uint8_t lastInFlight = _inFlight; | ||
| delay(2); // wait for a queued message to be sent, found that 2ms is usually enough, dont want to be too cautios (burst send is currently an edge case) | ||
| // note: delay and general approach might need some tweaking for real world use, based on burst tests sending 16 messages | ||
| if (_inFlight < lastInFlight) { | ||
| isretransmit = true; // try once more | ||
| err = esp_now_send(const_cast<uint8_t*>(BCAST), const_cast<uint8_t*>(data), len); // A message was sent and the sent callback was called, so we can retry now. | ||
| } |
There was a problem hiding this comment.
Increment _inFlight after a successful retry send.
On Line 227 retry success, _inFlight is never incremented, so the in-flight counter drifts low and throttling becomes inaccurate.
Suggested fix
if (_inFlight < lastInFlight) {
isretransmit = true; // try once more
err = esp_now_send(const_cast<uint8_t*>(BCAST), const_cast<uint8_t*>(data), len); // A message was sent and the sent callback was called, so we can retry now.
+ if (err == 0) _inFlight++;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| else if (_inFlight > 0 && !isretransmit) { | |
| uint8_t lastInFlight = _inFlight; | |
| delay(2); // wait for a queued message to be sent, found that 2ms is usually enough, dont want to be too cautios (burst send is currently an edge case) | |
| // note: delay and general approach might need some tweaking for real world use, based on burst tests sending 16 messages | |
| if (_inFlight < lastInFlight) { | |
| isretransmit = true; // try once more | |
| err = esp_now_send(const_cast<uint8_t*>(BCAST), const_cast<uint8_t*>(data), len); // A message was sent and the sent callback was called, so we can retry now. | |
| } | |
| else if (_inFlight > 0 && !isretransmit) { | |
| uint8_t lastInFlight = _inFlight; | |
| delay(2); // wait for a queued message to be sent, found that 2ms is usually enough, dont want to be too cautios (burst send is currently an edge case) | |
| // note: delay and general approach might need some tweaking for real world use, based on burst tests sending 16 messages | |
| if (_inFlight < lastInFlight) { | |
| isretransmit = true; // try once more | |
| err = esp_now_send(const_cast<uint8_t*>(BCAST), const_cast<uint8_t*>(data), len); // A message was sent and the sent callback was called, so we can retry now. | |
| if (err == 0) _inFlight++; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@wled00/src/dependencies/espnow_wled/espnow_wled.cpp` around lines 221 - 228,
The retry branch that calls esp_now_send(...) when isretransmit is set does not
increment the in-flight counter, causing _inFlight to go out of sync; update the
block that contains the esp_now_send(const_cast<uint8_t*>(BCAST),
const_cast<uint8_t*>(data), len) call so that after a successful send (check err
== ESP_OK) you increment _inFlight, e.g., modify the retry path around variables
_inFlight and isretransmit to ++_inFlight when the send returns success to keep
the in-flight counter accurate.
| #ifdef ESP8266 | ||
| #include <espnow.h> | ||
| #else | ||
| #include <esp_now.h> | ||
| #endif |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Locate the file
fd -type f "espnow_wled.h" 2>/dev/nullRepository: wled/WLED
Length of output: 35
🏁 Script executed:
# Check repository structure for espnow-related files
find . -name "*espnow*" -type f 2>/dev/null | head -20Repository: wled/WLED
Length of output: 159
🏁 Script executed:
# Look for platform-conditional include patterns in the codebase
rg "`#ifdef`.*ESP8266" -A 3 -B 1 | head -100Repository: wled/WLED
Length of output: 4376
🏁 Script executed:
cat -n wled00/src/dependencies/espnow_wled/espnow_wled.h | head -30Repository: wled/WLED
Length of output: 1286
🏁 Script executed:
# Check if esp_now.h is referenced elsewhere in the codebase
rg "esp_now\.h|espnow\.h" -iRepository: wled/WLED
Length of output: 359
🏁 Script executed:
# Check how other platform-specific includes are guarded in the project
rg "`#ifdef` (ARDUINO_ARCH|ESP32|ESP8266)" -B 1 -A 3 | head -150Repository: wled/WLED
Length of output: 7549
🏁 Script executed:
# Verify the guideline text is correctly cited
grep -r "Wrap platform-conditional includes" . 2>/dev/nullRepository: wled/WLED
Length of output: 35
🏁 Script executed:
# Check if ARDUINO_ARCH_ESP32 is used elsewhere in the codebase for `#ifdef` checks
rg "defined\(ARDUINO_ARCH_ESP32\)|`#ifdef` ARDUINO_ARCH_ESP32" | head -20Repository: wled/WLED
Length of output: 2098
Guard ESP-NOW header includes by explicit architecture checks.
Lines 7-11 use a generic #else that allows any non-ESP8266 build to fall through to #include <esp_now.h>, which does not match the coding guideline requiring explicit architecture guards. Use #elif defined(ARDUINO_ARCH_ESP32) instead of #else to prevent silent fallthrough on non-target platforms.
Suggested fix
-#ifdef ESP8266
- `#include` <espnow.h>
-#else
- `#include` <esp_now.h>
-#endif
+#ifdef ESP8266
+ `#include` <espnow.h>
+#elif defined(ARDUINO_ARCH_ESP32)
+ `#include` <esp_now.h>
+#endifPer coding guidelines: "Wrap platform-conditional includes in #ifdef ARDUINO_ARCH_ESP32 / #ifdef ESP8266."
🧰 Tools
🪛 Clang (14.0.6)
[error] 10-10: 'esp_now.h' file not found
(clang-diagnostic-error)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@wled00/src/dependencies/espnow_wled/espnow_wled.h` around lines 7 - 11, The
conditional include for ESP-NOW uses a generic `#else` which can incorrectly
include <esp_now.h> on non-target platforms; update the guard to explicitly
check for the ESP32 architecture by replacing the `#else` with `#elif`
defined(ARDUINO_ARCH_ESP32) so the block reads `#ifdef` ESP8266 / `#elif`
defined(ARDUINO_ARCH_ESP32) / `#endif` and include <espnow.h> for ESP8266 and
<esp_now.h> for ESP32 (references: macros ESP8266, ARDUINO_ARCH_ESP32 and the
include directives <espnow.h> and <esp_now.h>).
QuickESPNow is not well suited as it is focused on compatibility rather than speed and resources.
This PR replaces it with local functions directly based on esp API calls.
From my tests it is faster and more light weight:
I tested this with a send and receive function both with large packets and bursts of small packets. While large packets work quite well, small (5 bytes) packets tend to get lost on ESP8266 when sent in bursts longer than 6 packets.
The changes need testing in real applications but bench-top tests are very promising.
Summary by CodeRabbit
Release Notes