Skip to content

Prevent arduino core from assigning default MISO pin#5672

Open
DedeHai wants to merge 2 commits into
wled:mainfrom
DedeHai:clockedLED_MISO_fix
Open

Prevent arduino core from assigning default MISO pin#5672
DedeHai wants to merge 2 commits into
wled:mainfrom
DedeHai:clockedLED_MISO_fix

Conversation

@DedeHai

@DedeHai DedeHai commented Jun 6, 2026

Copy link
Copy Markdown
Collaborator

Adds invalid dummy pin if miso is undefined to prevent default fallback

fixes #5670

copy of comment from #5670:

I have found a fix that works around this arduino core quirk, to not say bug. In beginDotStar() adding this line fixes it:
if (miso == -1) miso = 127;
For reference:
in IDF passing a "-1" as a pin means unused
in arduino core a "-1" as apin means "use default"
the solution is to pass an invalid pin to the arduino function, it will then try to assign it, the underlaying IDF API rejects the pin, arduino core does not even check if it fails and just move on.
The default assignment by arduino may actually have some other side-effects when using clocked LEDs in combination with default MISO pins, all ESP32 flavours are affected.

Summary by CodeRabbit

  • Bug Fixes
    • Resolved an issue on non-ESP8266 boards where DotStar LED initialization could pick an incorrect default SPI pin, causing erratic or non-working LED output. This update ensures the SPI setup respects caller intent and prevents unintended pin reassignment during DotStar startup, improving LED reliability on affected platforms.

@DedeHai DedeHai added this to the 16.1 milestone Jun 6, 2026
@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ef5866f8-2f5b-4250-bb20-189219b85cc7

📥 Commits

Reviewing files that changed from the base of the PR and between 95f9588 and 7443f16.

📒 Files selected for processing (1)
  • wled00/bus_wrapper.h
🚧 Files skipped from review as they are similar to previous changes (1)
  • wled00/bus_wrapper.h

Walkthrough

In PolyBus::beginDotStar, when configuring DotStar LEDs via SPI on non-ESP8266 platforms, the code now overrides an invalid MISO pin value of -1 to 127, documented as an Arduino-core workaround to prevent unintended default MISO pin assignment.

Changes

DotStar SPI MISO Configuration Fix

Layer / File(s) Summary
MISO pin default override
wled00/bus_wrapper.h
PolyBus::beginDotStar now treats miso == -1 as "use default" by overriding it to 127, with a comment documenting this as an Arduino SPI workaround to prevent SPI.begin() from assigning the default MISO pin on non-ESP8266 platforms.

🎯 2 (Simple) | ⏱️ ~8 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and clearly describes the main change: preventing the Arduino core from assigning a default MISO pin.
Linked Issues check ✅ Passed The PR directly addresses issue #5670 by implementing a workaround to prevent default MISO pin assignment when MISO is intended to be unused, restoring clocked LED functionality.
Out of Scope Changes check ✅ Passed The single line change is narrowly scoped to the DotStar SPI setup in PolyBus::beginDotStar, directly addressing the MISO pin assignment issue without introducing unrelated modifications.

✏️ 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/bus_wrapper.h`:
- Line 361: The inline comment next to the miso default workaround in
bus_wrapper.h contains a typo ("meaans"); update that comment to read "means" so
it correctly documents that in the Arduino core -1 means "default" and passing
127 prevents SPI.begin() from assigning the default pin (refer to the miso
variable and the SPI.begin() mention in the same line).
🪄 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: cf6716cc-4df1-4e57-89b7-ad50d608a1e1

📥 Commits

Reviewing files that changed from the base of the PR and between 5f79107 and 95f9588.

📒 Files selected for processing (1)
  • wled00/bus_wrapper.h

Comment thread wled00/bus_wrapper.h Outdated
Comment thread wled00/bus_wrapper.h
#ifdef ESP8266
dotStar_strip->Begin();
#else
if (miso == -1) miso = 127; // note: in arduino core, -1 means "default" not "none", passing 127 as the MISO pin is a workaround to prevent SPI.begin() assign the default pin, see #5670

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@DedeHai is this workaround also working / needed for esp-idf V5 (arduino-esp32 3.5.x)?
If not, then we could wrap the line in #if ESP_IDF_VERSION_MAJOR == 4 .. #endif for future compatibility.

@DedeHai DedeHai Jun 6, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

from what I see, yes. the arduino.core code looks the same in the V5 branch

edit:
if it works the same I can't say - depends on what the IDF core does if you assign an out of scope pin but I would assume it wont tear down the SPI - just as it is in V4

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@coderabbitai please check if this workaround will still solve the problem in arduino-esp32 v3.5.x / esp-idf v5.x (see V5 branch). Review the related framework source code, don't rely solely on official espressif documentation.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch arduino-esp32 SPI.cpp from the V5 branch (arduino-esp32 3.x / esp-idf v5)
curl -s "https://raw.githubusercontent.com/espressif/arduino-esp32/idf-release/v5.1/libraries/SPI/src/SPI.cpp" | head -200

echo "---SEPARATOR---"

# Also check the master/3.x branch
curl -s "https://raw.githubusercontent.com/espressif/arduino-esp32/master/libraries/SPI/src/SPI.cpp" | head -200

Length of output: 5310


🏁 Script executed:

#!/bin/bash
# Check available branches for arduino-esp32 that correspond to 3.x/v5
curl -s "https://api.github.com/repos/espressif/arduino-esp32/branches?per_page=50" | python3 -c "
import json, sys
data = json.load(sys.stdin)
for b in data:
    name = b['name']
    if '3.' in name or 'v5' in name or 'idf5' in name or 'release' in name:
        print(name)
"

Length of output: 515


🏁 Script executed:

#!/bin/bash
# Fetch esp32-hal-spi.c from master (arduino-esp32 3.x) to check spiAttachMISO behavior
curl -s "https://raw.githubusercontent.com/espressif/arduino-esp32/master/cores/esp32/esp32-hal-spi.c" | grep -A 20 "spiAttachMISO"

echo "---SEPARATOR---"

# Also check the SPI.cpp begin() more fully (lines 60-160) to confirm the -1 guard
curl -s "https://raw.githubusercontent.com/espressif/arduino-esp32/master/libraries/SPI/src/SPI.cpp" | sed -n '60,160p'

echo "---SEPARATOR---"

# Check release/v3.1.x branch SPI.cpp as well (most likely what WLED targets)
curl -s "https://raw.githubusercontent.com/espressif/arduino-esp32/release/v3.1.x/libraries/SPI/src/SPI.cpp" | sed -n '60,160p'

Length of output: 5856



@softhack007 Here are the findings from reviewing the actual arduino-esp32 source code on both branches.


arduino-esp32 v2.x / esp-idf v4 (current situation — workaround needed ✅)

In v2.x, when any non--1 pin is supplied alongside miso=-1, the Arduino core fills in a default MISO pin. The miso=127 workaround forces the underlying ESP-IDF to reject that assignment, preserving the "unused" state.


arduino-esp32 v3.x / esp-idf v5 (the problematic case ⚠️)

SPIClass::begin() now has two important changes:

// 1. Default pins only assigned when ALL FOUR are -1
if (sck == -1 && miso == -1 && mosi == -1 && ss == -1) {
  _miso = MISO; // default
} else {
  _miso = miso; // stores -1 directly
}

// 2. MISO attachment explicitly guarded by >= 0
if (_miso >= 0 && !spiAttachMISO(_spi, _miso)) {
  goto err;
}

With miso=-1 (no workaround) in v3.x: Since sck/mosi are real pins, the else-branch stores _miso=-1. The guard -1 >= 0 is false, so spiAttachMISO is skipped entirely — correct behavior, no default pin assigned. ✅

With miso=127 (workaround) in v3.x: _miso=127, the guard 127 >= 0 is true, so it calls spiAttachMISO(spi, 127). Looking at the HAL:

bool spiAttachMISO(spi_t *spi, int8_t miso) {
  if (!spi || miso < 0) { return false; }  // 127 is positive, passes this check
  // ...
  pinMode(127, INPUT);           // invalid GPIO on all ESP32 variants
  pinMatrixInAttach(127, ...);   // invalid pin
  perimanSetPinBus(127, ...);    // likely fails → returns false

This means:

  • In v3.1.x (void begin): goto err is hit, logging "Attaching pins to SPI failed." on every boot — confusing but the SPI bus may still function without MISO.
  • In master (bool begin): begin() returns false — if NeoPixelBus checks this, the strip initialization fails silently.

Recommendation

Wrap the workaround in an IDF version guard so it only applies where it's needed:

`#if` ESP_IDF_VERSION_MAJOR < 5
    if (miso == -1) miso = 127; // note: in arduino core v2.x, -1 means "default" not "none", passing 127 as the MISO pin is a workaround to prevent SPI.begin() assigning the default pin, see `#5670`
`#endif`

In esp-idf v5.x the original -1 is handled correctly by the _miso >= 0 guard in SPIClass::begin(), and the workaround actively causes harm there (error log at minimum, possible begin() failure).


✏️ Learnings added
Learnt from: softhack007
Repo: wled/WLED PR: 5672
File: wled00/bus_wrapper.h:361-361
Timestamp: 2026-06-09T17:25:51.318Z
Learning: In arduino-esp32 v3.x (esp-idf v5.x), `SPIClass::begin()` in `libraries/SPI/src/SPI.cpp` correctly handles `miso=-1` via the guard `if (_miso >= 0 && !spiAttachMISO(_spi, _miso))` — meaning no workaround is needed. The `miso=127` workaround used in `PolyBus::beginDotStar()` (wled00/bus_wrapper.h) for arduino-esp32 v2.x/esp-idf v4.x is harmful in v3.x/v5.x because `int8_t(127)` is positive, passes the `< 0` guard in `spiAttachMISO`, and causes `begin()` to attempt attaching an invalid GPIO (127), resulting in an error log or `begin()` returning false. The correct fix is to guard the workaround with `#if ESP_IDF_VERSION_MAJOR < 5`.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: softhack007
Repo: wled/WLED PR: 5355
File: wled00/util.cpp:635-638
Timestamp: 2026-02-07T16:06:08.677Z
Learning: PSRAM-related compilation guards should enable PSRAM code only for ESP32 variants that actually include PSRAM: ESP32-C61, ESP32-C5, and ESP32-P4. Exclude ESP32-C3, ESP32-C6, and ESP8266 from these guards. Apply this rule across the codebase (not just wled00/util.cpp) by reviewing and updating PSRAM guards/macros in all relevant files (C/C++ headers and sources).

Learnt from: softhack007
Repo: wled/WLED PR: 4838
File: lib/NeoESP32RmtHI/src/NeoEsp32RmtHIMethod.cpp:30-35
Timestamp: 2026-03-27T12:33:48.499Z
Learning: In C/C++ preprocessor conditionals (`#if`, `#elif`) GCC/Clang treat `&&` as short-circuit evaluated during preprocessing. This means guards like `#if defined(ARDUINO_ARCH_ESP32) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)` are safe even if the macro/function-like macro on the RHS (e.g., `ESP_IDF_VERSION_VAL`) is not defined on some targets, because the RHS will not be expanded when the LHS is false (e.g., `defined(...)` evaluates to 0). During code review, avoid flagging such cases as “undefined function-like macro invocation” if they are protected by short-circuiting `defined(...) && ...`/`||` logic; some tools like cppcheck may not model this and can produce false positives. Also, don’t suggest refactoring that moves ESP32-specific includes/headers (e.g., `esp_idf_version.h`) outside of these guarded preprocessor blocks, since that will break targets (e.g., ESP8266) where the headers don’t exist.

Learnt from: softhack007
Repo: wled/WLED PR: 4843
File: usermods/elastic_collisions/Elastic_Collisions.cpp:1-1
Timestamp: 2026-05-06T22:10:27.517Z
Learning: In C/C++, do not flag precedence issues for expressions that mix additive operators (`+`, `-`) with shift operators (`<<`, `>>`) based on the assumption that shifts bind tighter. Per C/C++ operator precedence, `+`/`-` have higher precedence than `<<`/`>>` (e.g., `x - edge0 << 8` parses as `(x - edge0) << 8`). When reviewing WLED (and other) fixed-point/bit-manipulation code, confirm the intended parse using cppreference before reporting a precedence bug for mixed `-`/`+` and `<<`/`>>` expressions.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@softhack007 don't trust the rabbit on this one. he skipped the HAL call a few lines below its quote. what it wrote is also true for V4 but its incomplete.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

@willmmiles willmmiles left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Translating between our convention (-1 == unassigned) and whatever the arduino core does is the right way.

That said: this will break the setup of people who didn't think to set the pin because it turned out that they'd wired it on the default, and it just worked. I don't know if we want to think about some kind of upgrade handoff here. At the very least we'll need a "possibly breaking change" note on this one.

@DedeHai

DedeHai commented Jun 6, 2026

Copy link
Copy Markdown
Collaborator Author

Translating between our convention (-1 == unassigned) and whatever the arduino core does is the right way.

That said: this will break the setup of people who didn't think to set the pin because it turned out that they'd wired it on the default, and it just worked. I don't know if we want to think about some kind of upgrade handoff here. At the very least we'll need a "possibly breaking change" note on this one.

I do not follow (on both counts). This is only about the MISO i.e. input pin which is unused for LED purposes. Is there any dual-use of the SPI and is that even allowed? The scenario I imagine is that the busmanager claims the SPI via NPB and after that a UM also uses that same SPI - which would initialize it anew. Or what is the scenario?

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GPIO19 not working as an 2-Wire LED output in 16.0.0

3 participants