Skip to content

Add Waveshare S3 Tubes remote and v47 field UI - #71

Open
theysayheygreg wants to merge 14 commits into
SteveEisner:mainfrom
theysayheygreg:contrib/waveshare-s3-basic
Open

Add Waveshare S3 Tubes remote and v47 field UI#71
theysayheygreg wants to merge 14 commits into
SteveEisner:mainfrom
theysayheygreg:contrib/waveshare-s3-basic

Conversation

@theysayheygreg

@theysayheygreg theysayheygreg commented Aug 24, 2026

Copy link
Copy Markdown

Summary

Adds the Waveshare ESP32-S3-Touch-AMOLED-2.16 as a Tubes v47 handheld node, rebased directly on current main (d21b3850). The S3 runs Steve’s existing Tubes engine and modern Beat, Pattern, Palette, device-report, and fleet-update contracts.

Included

  • Waveshare S3 PlatformIO target and validated 16 MB dual-OTA geometry
  • Geometry-only 60-pixel null bus; no physical LED transport or private framebuffer
  • AMOLED Conductor rendering from the canonical completed WLED strip frame
  • One shared Field UI lifecycle: view manager, touch dispatcher, chrome, buttons, and retained-state redraws
  • Four baseline tiles: Conductor, Surveyor, Update, and Channels
  • Master-authorized Next through existing Pattern and Palette channel admission
  • Surveyor identity/topology rows using device ID, release, uplink, and last-heard data
  • Read-only Beat, Pattern, and Palette channel state
  • Optional carrier target embedding exact v47 Dig2Go and Athom C3 images and serving Steve’s fleet-pull protocol

Product boundaries

  • No Previous control
  • No invented remote-control protocol; Channels remains read-only
  • No S3 firmware image served by the carrier
  • No physical LED output
  • Carrier is a distinct build target; the standard S3 target has no embedded payload dependency
  • Further S3 feature work remains deferred until the Dig2Go migration test is complete

Verification

  • focused S3 retained-render, Field UI, Conductor, Surveyor, channel-authority, carrier, null-bus, partition, and effective-environment contracts pass
  • peer telemetry host test passes
  • pio run -e waveshare_s3_tubes_remote passes
  • pio run -e waveshare_s3_tubes_carrier passes
  • git diff --check passes
  • PR head is cleanly based on current v47 main

Hardware checkpoint

Device: Waveshare S3, ROM MAC a4:cb:8f:d7:66:f8.

Current carrier artifact:

  • Size: 3,910,416 bytes
  • SHA-256: 9dcacd3411daa9f02e600fb09bf4510417d7ecb0f5bf6111ebb47494fd17da98
  • Written at application offset 0x10000; esptool verified the write hash
  • Device boots and runs the live 60-cell Conductor pattern
  • Four-tile UI is installed; Greg’s final visual acceptance of the latest retained-render layout is still pending

The previously accepted PR71 canonical-frame/null-bus hardware proof remains preserved in this history; this update does not replace compile proof with a physical claim.

@SteveEisner SteveEisner left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I’m requesting changes because the PR currently alters several existing Tubes and WLED behaviors beyond what is needed to add the S3 profile.

The PR commit is already based on the current main, so a normal rebase may not resolve this by itself. Several of the edited files contain the shape of older code and directly delete newer behavior from main, which suggests the S3 work may have been applied from older local copies or reconciled at file level. Please compare each modified shared file against main, restore the current implementation, and then reapply only the S3-specific changes.

The regressions happen through these concrete changes:

  • Tubes.h removes readFromJsonState() and addToJsonInfo(). Without those usermod callbacks, WLED no longer forwards /json/state Tubes operations to controller.readJsonOperations(), and the existing Tubes v3 status disappears from /json/info.
  • Tubes.h also removes the HomeLight guards around setup, overlay drawing, and button handling. As a result, HomeLight Wi-Fi mode receives the Tubes transition, overlay, and button behavior even though that role is designed to leave its LEDs under normal WLED control.
  • controller.h and virtual_strip.h remove the runtime-palette argument from VirtualStrip::load(). Literal v3 palettes are still installed in the WLED segment, but internal Tubes layers reload the legacy palette ID instead of snapshotting the received colors, so coordinated devices can render different colors.
  • const.h removes the #ifndef DEFAULT_LED_COUNT guard. The header then redefines board-provided values such as 64 or 150 to 30 after they were supplied on the compiler command line; the Dig2Go build demonstrates this with redefinition warnings throughout the build, and core geometry receives the wrong default.
  • The new null bus uses type 95, which is inside WLED’s virtual-network range of 80–95. The factory special case constructs BusTubesNull, but existing code still sees isVirtual() == true; BusManager::on() then statically casts it to BusNetwork, which is an invalid downcast and can access the wrong object layout during hostname resolution.
  • On a fresh or factory-reset device, WLED initializes its default bus before usermod setup(). Because the S3 environment sets only PIXEL_COUNTS=60, that initial bus is a physical TYPE_WS2812_RGB bus. recoverLedBussesIfNeeded() immediately returns when it sees the existing bus, while the AMOLED preview accepts only TYPE_TUBES_NULL, so the intended null framebuffer is never installed and the preview remains black.
  • The generation-1 visual marker and the broader invalid-segment recovery are also removed from Tubes.h, changing current mixed-fleet and recovery behavior without being required by the S3 target.

Please review the resulting diff for minimal impact. Ideally, this PR should contain the Waveshare environment, board dependencies and partition layout, the display usermod, and the smallest core change needed for a safely classified memory-backed output. Existing Tubes role, protocol, palette, JSON, migration, and non-S3 board behavior should remain unchanged.

The current tests and builds pass because most of the new tests inspect source text or compile-time contracts; they do not execute first-boot bus selection, a power off/on cycle through BusManager::on(), HomeLight Wi-Fi rendering, or v3 literal-palette rendering. Please add focused behavioral coverage for those paths once the shared-code regressions are removed.

@theysayheygreg

Copy link
Copy Markdown
Author

Addressed the requested changes with a narrow additive correction over the existing PR commit.

What changed

  • Restored current-main Tubes behavior that the original S3 commit had unintentionally replaced:
    • JSON state/info callbacks;
    • HomeLight setup/overlay/button guards;
    • v3 runtime-palette propagation;
    • board-provided DEFAULT_LED_COUNT handling;
    • generation marker and non-S3 recovery behavior.
  • Moved the S3 memory framebuffer outside WLED’s virtual-network type range and kept it out of BusNetwork cast paths.
  • Corrected first/factory boot so the Waveshare target replaces a stale physical/default bus with one 60-pixel memory framebuffer.
  • Kept the S3 display target non-authoritative: it renders/observes the Tubes state but does not run physical Master setup/update/overlay operations. Other Tubes targets are unchanged.
  • Added a guard for the one-pixel mode_flow() divide-by-zero encountered during first-boot segment recovery.
  • Removed temporary investigation telemetry and unrelated experimental state.

Verification

  • npm test: 41 passed
  • focused PR71/S3 tests: passed
  • Tubes mesh and upgrade suites: passed
  • waveshare_s3_tubes_remote: built successfully
  • esp32_quinled_dig2go_tubes: built successfully
  • full application write/readback matched the built artifact
  • 60-second clean runtime; ESP-NOW reached started state
  • physical S3 check: normal synchronized Tubes pattern across the display, with the current generation marker steady yellow on pixels 0–9 and live pattern continuing on pixels 10–59

No force-push; this is a normal fast-forward update.

Comment thread wled00/bus_manager.h Outdated
void show() override {}
void setPixelColor(unsigned pix, uint32_t c) override;
uint32_t getPixelColor(unsigned pix) const override;
size_t getBusSize() const override { return sizeof(BusTubesNull) + _len * sizeof(uint32_t); }

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Stepping back, do we need a new core bus type here at all? The simplest version may be to configure a normal 60-pixel LED bus on a confirmed-unused pin and just leave the strip disconnected. That gives WLED the logical geometry it needs without adding another bus implementation.

If the no-GPIO guarantee is important enough to keep the null bus, I do not think it needs its own pixel buffer. WLED already maintains the logical frame that strip.getPixelColor() reads, so this bus could provide the 60-pixel geometry and make output a no-op while the AMOLED reads from the existing strip buffer. That would remove _data, its allocation, and the duplicate set/get storage path.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

null bus still probably useful for the S3 since it has no wired output and GPIO is pretty tight. will simplify.

Comment thread platformio_tubes.ini
-D TUBES_NULL_OUTPUT
-D TUBES_S3_FIELD_OS

-D PIXEL_COUNTS=60

@SteveEisner SteveEisner Aug 24, 2026

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

On a fresh configuration, this profile overrides the pixel count but still inherits the default LED type and pin, so cfg.cpp creates a 60-pixel WS2812 bus on GPIO 16. WLED calls beginStrip() before UsermodManager::setup(), which means that physical driver is initialized and sends a black frame before recoverLedBussesIfNeeded() gets a chance to replace it.

The later replacement fixes the AMOLED preview, but it does not make first boot free of physical output. If the null bus remains, could the environment make it the initial LED_TYPES value, with an appropriate sentinel DATA_PINS value, and reserve the recovery code for migrating stale configurations?

Comment thread tools/s3-partition-contract-test.js Outdated
});

test('Waveshare effective environment has unique artifact identity, one enabled USB CDC definition, and immutable pins', () => {
const output = execFileSync('pio', ['project', 'config', '--json-output'], {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Could you have your agent fix the test-environment mismatch here? The existing testCdata CI job runs npm ci followed by npm test, without installing PlatformIO, so this currently fails there with spawnSync pio ENOENT.

My first choice would be to move this effective-PlatformIO assertion into a PlatformIO/firmware validation step, or skip it with a clear reason when pio is unavailable. Installing requirements.txt in the Node test job would also work, although there may be a cleaner way to keep the fast Node suite independent of the firmware toolchain.

@SteveEisner SteveEisner left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

We've discussed the remaining points offline.

@theysayheygreg

Copy link
Copy Markdown
Author

Updated PR71 from Steve main 395f7409 with the reviewed S3 replay, preserving targeting, fade, identify, battery, and updater behavior. Resolved the three review comments: canonical frame read with a geometry-only null bus; target-scoped DATA_PINS=255 first boot; and PlatformIO validation moved to firmware CI. Also refreshed the stale release-15 and /json/cfg fixtures. Validation: npm/focused suites and three firmware builds; physical S3 artifact/readback match plus full 60-cell pattern proof. Pushed as an ordinary fast-forward merge update; no force push.

@theysayheygreg
theysayheygreg force-pushed the contrib/waveshare-s3-basic branch from f1e4871 to 6c61509 Compare August 26, 2026 00:35
@theysayheygreg theysayheygreg changed the title Add basic Waveshare S3 Tubes target Add Waveshare S3 Tubes remote and v47 field UI Aug 26, 2026
@theysayheygreg

Copy link
Copy Markdown
Author

Updated PR71 to current v47 main. This is now the four-tile S3 baseline: canonical-frame Conductor, identity/topology Surveyor, read-only modern Channels, and an optional carrier build serving exact v47 Dig2Go/C3 images through the existing fleet-pull protocol. Both standard and carrier targets build; focused S3 contracts pass. Current hardware artifact is flashed and booting; latest UI visual acceptance is explicitly still pending.

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