Skip to content

Add support for Arduino Nano ESP32 - #39

Closed
cag63 wants to merge 5 commits into
tostmann:masterfrom
cag63:add-nano-esp32
Closed

cag63 wants to merge 5 commits into
tostmann:masterfrom
cag63:add-nano-esp32

Conversation

@cag63

@cag63 cag63 commented Jul 10, 2026

Copy link
Copy Markdown

The Arduino Nano ESP32 uses the ESP32-S3 chip family and comes with 16MB of flash and 8MB of PSRAM. It is equivalent to the esp32-s3-devkitc-1, but it only has one USB port instead of two. The image for the esp32-s3-devkitc-1 does not work on the Arduino Nano ESP32.

Modified platformio.ini, scripts/release.sh and webflasher/index.html to build the binaries and a target-specific manifest for the Arduino Nano ESP32. Download to the Arduino Nano ESP32 is done with dedicated buttons.

Edited README.md and webflasher/index.html in an attempt to clarify what the user should expect.

LEFT TO DO: Add links to the ESP32-S3 8MB binaries and the Arduino Nano ESP32 binaries. The webflasher/index.html file pulls the information from the manifest.json file - it ignores the target-specific manifest files.

TESTED: Upload of the factory.bin file to the Arduino Nano ESP32 using the web page. I

NOT TESTED: OTA updates from the web page.

cag63 added 5 commits July 10, 2026 16:05
…s with only 8MB of flash or the Arduino Nano ESP32.

* In the first section, specify that a data USB cable should be used.
* Change "USB socket" for "USB port" throughout.
* Simply the text in the section for 8MB ESP32-S3 boards.
* Add Arduino Nano ESP32 as a separate section with its own buttons.
* Add Arduino Nano ESP32 as a recommended target, because it is a ESP32-S3 family with 16MB of flash.
* Clarify what to expect in the "What you get" section.
…pecific ESP32-S3 manifests and corresponding download buttons on the web page.

* Add the Arduino Nano ESP-32 to the list of supported hardware.
* Add nano-esp32 to the build commands.
@tostmann

Copy link
Copy Markdown
Owner

Thanks for this — and for being explicit about what you tested and what you didn't. That made it a lot easier to review.

One concrete bug, worth fixing regardless of where this lands: SIXBACK_OTA_PREFIX is missing its trailing hyphen. ota_pull.cpp builds the artifact names by plain concatenation:

String fsName = prefix;  fsName += "littlefs.bin";

which is why every existing override ends in - ("sixback-s3-", "sixback-s3-8mb-"). As written, the puller would request sixback-nano-esp32littlefs.bin while build_release.sh produces sixback-nano-esp32-littlefs.bin, so every OTA pull on that target would 404. That lines up exactly with your "OTA updates not tested" note.

On the bigger picture: your PR made me look at this from the target-matrix angle rather than the board angle. What the Arduino Nano ESP32 is, for this build, is the combination S3 + 16 MB + no UART bridge. The env you added is identical to env:s3 apart from the USB mode — same partition table, same merge offsets, same feature flags. So what forces a separate target isn't really the board, it's the compile-time choice of which serial port Improv listens on. Left alone, that axis multiplies targets and web-flasher buttons for every board topology that shows up.

So I tested removing the axis instead. With ARDUINO_USB_MODE=1 and ARDUINO_USB_CDC_ON_BOOT=0, Serial stays on UART0 (console logs don't move) and a second ImprovWiFi instance can run on a self-instantiated HWCDC on the native USB-Serial-JTAG — the Arduino core doesn't declare a global one in that configuration, so there's no contention for the peripheral. Both ports then answer provisioning, and a first-come guard hands the session to whichever port receives a complete IMPROV frame first (matching on the magic rather than on the first byte, so stray terminal output on UART0 can't lock out a real USB host).

Verified on S3 test hardware: each port answers alone, both are live in a single boot window, the guard is fair in both directions, and the OTA path from the current release to the modified build is clean — the bootloader binary is byte-identical between the two, so ARDUINO_USB_MODE is purely an app-level flag and can't break OTA for existing installs.

That work isn't released yet, but if it holds up it means a 16 MB S3 board without a bridge chip is served by the standard S3 image, and the remaining question for this PR is much narrower:

Is the B1→GND + RST step actually required on the Nano ESP32? Or can esp-web-tools drive it into the ROM download mode over DTR/RTS the way it does on other native-USB boards? That's the part I can't check here — I don't have one on the bench. It matters because a board that needs a jumper before a fresh install doesn't fit the one-click flow the flasher page is built around, and would have to be documented as a deliberate exception rather than sitting next to the normal buttons.

If you're able to test that, it would be genuinely useful — and if it turns out no manual step is needed, the picture for this board gets a lot simpler.

@tostmann

Copy link
Copy Markdown
Owner

Follow-up on the target-matrix point from my earlier comment — the dual-transport idea now has hardware results, and they bear directly on this PR.

One 16 MB S3 image now answers Improv on both UART0 and the chip's native USB-Serial-JTAG. Whichever port receives a valid IMPROV frame first owns the session; the guard matches on the magic rather than on the first byte, so stray terminal output on one port can't lock out a real host on the other.

Verified on S3 hardware:

  • A complete web-flasher run over the native USB socket — factory flash plus the "Connect device to Wi-Fi" step — went through, twice.
  • Robustness: 894 Improv frames under sustained request load, with ~11 KB of concurrent console output interleaved into the same stream; zero corrupted frames. Measured with checksum validation rather than frame-scanning, since a tolerant parser will happily report success on a stream that a strict client chokes on.
  • OTA safety for existing installs: the bootloader binary is byte-identical between the current release and the modified build, so ARDUINO_USB_MODE is purely an app-level flag and cannot break OTA for devices already in the field.

What that means for this PR: a 16 MB S3 board without a bridge chip is served by the standard S3 image and would not need a build target of its own. The env here differs from env:s3 only in the USB configuration — same partition table, same merge offsets, same feature flags.

Two honest caveats. None of this is released yet. And one flash attempt over the native port did abort at Hard resetting via RTS pin before writing anything — that has not reproduced since, and command-line esptool flashes the same port without trouble, so it looks browser-side rather than board-side.

Which leaves the question from my earlier comment as the one that actually decides this: is the B1→GND + RST step really required on the Nano ESP32? I don't have one on the bench. If no manual step is needed, this gets simple. If one is, that is the part that determines how the board can be documented.

Either way, the missing trailing hyphen in SIXBACK_OTA_PREFIX is worth fixing — that one bites regardless of which direction this goes.

@cag63

cag63 commented Jul 28, 2026

Copy link
Copy Markdown
Author

I understand that you created version 0.8.35 in which you select the UART port at runtime, which eliminates the need for a different set of manifest constants for the Arduino Nano ESP32.

I confirm that the esp32-s3-devkitc-1 binaries work on the Arduino Nano ESP32.

The remaining question is whether the board requires to be in bootloader upload mode to upload Sixback.

ANSWER: I suspect the Arduino Nano ESP32 must be in bootloader upload mode to upload Sixback, i.e. B1 to GND, press RST, and remove B1 to GND is necessary on first install of SixBack.

Here is what I did:

  1. Connect the Arduino Nano ESP32 running SixBack 0.8.29 to the computer running Ubuntu 24.04.1 LTS.

  2. Go to https://sixback.io/.

  3. Hit the default connect button. The port is identified as USB JTAG/serial debug unit (ttyACM0).

  4. SixBack 0.8.35 installs and works. I was asked to re-enter the network SSID and password, but the configuration of my SoundTouch 10 was preserved from version 0.8.29.

Conclusion: It is not necessary to do the B1 to GND dance to update SixBack.

  1. Launch the Arduino IDE. install the Arduino Nano ESP32 board package, and follow the procedure at https://support.arduino.cc/hc/en-us/articles/9810414060188-Reset-the-Arduino-bootloader-on-the-Nano-ESP32 to reset the bootloader (B1->GND + RST) and on-board programmer to "factory default".

  2. Shut down the Arduino IDE (in case it conflicts with the webflasher for the USB port).

  3. Go to https://sixback.io/.

  4. Hit the default connect button.The port is identified as Nano ESP32 (ttyACM0) - Paired.

  5. While it is possible to select Install SixBack and check the Erase device box, the install fails.

The failure message says to try resetting the device or holding the BOOT button. Pressing the RST button does not work. The Arduino Nano ESP32 does not have a BOOT button.

  1. Re-launch the Arduino IDE.

  2. Load a simple sketch to flash the LEDs, just to check that the default Arduino bootloader works. It does.

  3. Connect B1 to GND, hit RST, remove the jumper.

  4. Go to https://sixback.io/ and reload SixBack 0.8.35. The port is now identified as USB JTAG/serial debug unit (ttyACM1) - Paired

Conclusion: It appears necessary to do the B1 to GND dance to install Sixback.

Note: I don't think I had "Paired" in the port ID in step 3.

Note: The "Purple" LED remains on when SixBack runs, as if the Arduino Nano ESP32 is still in bootloader upload mode.

Since I have the latest version now installed, I did not check the OTA function. The Install update button is not active. I will try this when the next version is released.

Final outcome: It may be sufficient to document that the Arduino Nano ESP32 is supported but requires the B1 to GND dance on first installation of Sixback.

Thank you for doing this. None of the edits in my PR are required; you added support the Arduino Nano ESP32 is a far more elegant way than I did. Let me know if I should do some more experiments or if I should do something to close my PR.

@tostmann

Copy link
Copy Markdown
Owner

Thanks — that's exactly the experiment that was missing, and the write-up is precise enough to act on.

Three things your test settles:

  1. The standard S3 image runs on this board. That confirms the dual-port direction: since v0.8.35 the 16 MB S3 image answers Improv provisioning on both UART0 and the chip's native USB-Serial-JTAG, so a 16 MB S3 board without a bridge chip needs no target of its own. Your env and env:s3 differ only along that axis — with the axis gone, the code changes aren't needed. Agreed.

  2. Updating needs no manual step. Native port, default button, full flash plus the Connect device to Wi-Fi step, on a board already running SixBack.

  3. A first install from the Arduino bootloader state does need one. B1→GND, RST, remove the jumper — and the port identity flips from "Nano ESP32" to "USB JTAG/serial debug unit". That marker is the practically useful part for anyone else with this board.

What I won't claim is why the flasher can't reach download mode in that first state. Your "I suspect" is the honest framing and I'd keep it there: what the steps establish is the difference in enumeration and that the manual step resolves it, not the mechanism behind it.

The purple LED I can't explain either. Nothing in the firmware drives an on-board RGB or status LED on the S3 targets, so SixBack isn't putting it in that state — beyond that I'd leave it open.

I've documented the board in the README as supported through the standard S3 button, with the bootloader-entry step called out as a first-install exception and your report linked. One point stays marked as untested there: OTA on this board. The artifacts it pulls are the standard sixback-s3-* channel, so when the next release lands, a successful update from your side would close the last gap.

So yes — please go ahead and close the PR. Nothing is lost: the outcome moved into v0.8.35 and into the docs. Thanks for the bench time; the bootloader question was the one part I couldn't answer from here.

@tostmann

Copy link
Copy Markdown
Owner

Closing this one myself so it doesn't sit open — no action needed on your side. The outcome lives in v0.8.35 and in the README now. Thanks again for the bench work.

@tostmann tostmann closed this Jul 28, 2026
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.

2 participants