Add complete WiThrottle portable throttle example for M5StickC Plus 2#1
Open
ismangil wants to merge 14 commits into
Open
Add complete WiThrottle portable throttle example for M5StickC Plus 2#1ismangil wants to merge 14 commits into
ismangil wants to merge 14 commits into
Conversation
New examples/WiThrottleProtocol_M5StickCPlus2 sketch demonstrating WiThrottleProtocol on an M5StickC Plus 2 with the M5Stack ENCODER HAT. The encoder drives a centre-zero bipolar slider: clockwise from zero adds forward speed, counter-clockwise adds reverse, and zero-crossings issue an automatic setSpeed(0)/setDirection() sequence so the JMRI side stays in step. The sketch also includes captive-portal WiFi provisioning, mDNS server discovery, an on-device roster picker, F0-F12 controls and a reconnect ladder. No changes to the library itself. https://claude.ai/code/session_01VGvLbCWkXXnuCF6YNxPoR4
The M5StickC Plus 2 portable throttle example previously assumed the older M5Stack ENCODER HAT (A031, I2C 0x5E). Switch the I2C address, register map and LED byte order to match the MiniEncoderC HAT (SKU U157): * Address now 0x42 * Incremental counter still int32 little-endian at 0x10 * Absolute counter (int32) at 0x00 and reset register at 0x40 documented * Single RGB LED at 0x30, BGR byte order * setLeds(left, right) replaced with setLed(rgb888) Updated README, splash text and source comments to reference U157. https://claude.ai/code/session_01VGvLbCWkXXnuCF6YNxPoR4
Rotate the centre-zero slider 90 degrees so it runs along the right edge of the TFT and grows upward for forward speed, downward for reverse. The big signed speed number, direction arrow and F0-F9 indicators reflow into the left content area. The slider's centre tick now runs horizontally across the bar. https://claude.ai/code/session_01VGvLbCWkXXnuCF6YNxPoR4
Set the TFT rotation to 0 (portrait) and swap TFT_W/TFT_H so the existing layouts treat the display as 135 wide x 240 tall. The vertical slider on the drive view now has the full portrait height to grow into. https://claude.ai/code/session_01VGvLbCWkXXnuCF6YNxPoR4
Replace the cramped landscape-leaning UI with portrait-friendly layouts: * Title bar is now 22 px tall with size-2 left text and size-1 right text; drawHeader truncates the right side to ~60% of the bar and then trims the left if needed so the loco name always shows. * Add drawWrappedCentered/truncateToWidth helpers so any text that does not fit the 135 px portrait width wraps at word boundaries instead of clipping. Splash, provisioning and reconnect screens use it. * Roster rows are 26 px tall with size-2 name/address and an ellipsis-by-truncation when the name is too long. * Drive view: speed number dropped to size 3 to fit the ~95 px content column next to the slider; right-side status replaced with battery percentage (link state is implicit because outages drop to Reconnect). * Functions reflows to a 4x3 portrait grid; status screen stacks label above value because labels and values no longer fit side by side. https://claude.ai/code/session_01VGvLbCWkXXnuCF6YNxPoR4
Add updateThrottleLed() that drives the MiniEncoderC's RGB LED: green when moving forward, red when moving in reverse, off at centre. Called from applyDelta, the e-stop / soft-centre paths in tickDrive, absorbMirroredState (so external speed changes from JMRI track too), and enterDrive. The LED is explicitly turned off when entering Roster or Reconnect since the throttle is not actively driving in those states. Uses dim levels (0x002000 / 0x200000) because the on-board LED is otherwise unpleasantly bright. https://claude.ai/code/session_01VGvLbCWkXXnuCF6YNxPoR4
The MiniEncoderC firmware actually expects bytes in RGB order on the wire (high byte of 0xRRGGBB first), not BGR as I'd mis-noted from the upstream M5Unit driver. With BGR ordering the high byte was landing in the blue channel, so dim-red (0x200000) lit up as dim-blue while green still worked (G is the middle byte either way). Rename ENCODER_REG_LED_BGR -> ENCODER_REG_LED and update the comment in config.h to match. https://claude.ai/code/session_01VGvLbCWkXXnuCF6YNxPoR4
Restructure the example README with a numbered Getting started flow that walks from a bare M5StickC Plus 2 to driving a loco: install toolchain, flash firmware, captive-portal WiFi setup, start JMRI's WiThrottle server, pick a loco. Add a Troubleshooting section covering the most common first-run failures (HAT not detected, 5 GHz/ch-11 WiFi, JMRI not reachable). Also fix a few stale details: TFT dimensions now say 135x240 (portrait), the PlatformIO board id points to m5stick-c-plus2 with a fallback for older platform-espressif32 versions, and the Controls table calls out the encoder LED feedback added in earlier commits. https://claude.ai/code/session_01VGvLbCWkXXnuCF6YNxPoR4
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Insert a new Layout state between Functions and Status in the BtnB cycle. The screen has two tabs (Turnouts, Routes) selected by encoder long-press, with a scrollable size-2 list per tab. Controls on Layout: * Encoder rotate moves the highlight within the active tab. * Encoder short-press activates the highlighted item: turnouts call setTurnout(sysName, TurnoutToggle) with an optimistic local flip (server confirms via receivedTurnoutAction); routes call setRoute(sysName) and flash a "Route activated" banner. * Encoder long-press flips the tab. * BtnA returns to Drive, BtnB cycles to Status. AppDelegate gains TurnoutEntry/RouteEntry buffers and overrides for receivedTurnoutEntries/Entry/Action plus the route equivalents; the library does not keep these vectors for us. README updates the controls table with the Layout row group and step 5 mentions the new screen. https://claude.ai/code/session_01VGvLbCWkXXnuCF6YNxPoR4
Two issues seen on hardware: the Turnouts list arrived empty, and the tab toggle to Routes was undiscoverable. Cause for the empty list: the library dispatches the turnout/route callbacks in the opposite order to the roster. For roster, the count arrives first then entries; for turnouts and routes, each entry arrives first and the count arrives last (PTL / PRL parsing in WiThrottleProtocol.cpp). The delegate was clearing on the count call, which wiped every entry that had just been appended. Clear the vector when index == 0 of a fresh list (the natural "start of a new list" signal) and treat receivedTurnoutEntries/receivedRouteEntries as the "list finished" marker instead. UX: the active tab now shows a leading ">" marker and a footer line "hold push: switch tab" anchored to the bottom of the Layout screen, so the encoder long-press toggle is discoverable even when the current tab is empty. https://claude.ai/code/session_01VGvLbCWkXXnuCF6YNxPoR4
User reported it was too easy to slip onto the wrong loco while reaching for the encoder push to confirm. UI side: bump roster row height from 26 to 32 with 6 px of true gap between rows; add a 5 px left-edge accent stripe on the selected row so the selection is unmistakable; draw thin dividers between unselected rows. Input side: accumulate encoder detents and require two of them to move the selection by one row. Single accidental nudges no longer change the highlight. Reset the accumulator on every (re)entry to the picker. https://claude.ai/code/session_01VGvLbCWkXXnuCF6YNxPoR4
M5Stack's M5StickS3 shares the Plus 2's TFT (135x240 ST7789), the 8-pin HAT pinout (I2C on GPIO 0/26), and the M5Unified power/button APIs. A single source builds for either Stick — the only changes needed are cosmetic strings that no longer hardcode "Plus 2" plus a documented PlatformIO env for the S3. Sketch: generalise the file header and drop the Plus 2 model name out of the boot splash and the WiThrottle device name prefix (now "M5Stick" / "M5Stick-<last4>"). The MAC suffix already disambiguates devices on the JMRI client list. README: title and intro mention both Sticks; the hardware table acquires an M5StickS3 row; the PlatformIO section adds an [env:m5sticks3] block alongside the plus2 one, with a note on board id fallbacks; the Arduino IDE steps mention both board entries. Folder name stays as WiThrottleProtocol_M5StickCPlus2 to avoid breaking the merged PR and any external references — the README notes the historical name covers both devices. https://claude.ai/code/session_01VGvLbCWkXXnuCF6YNxPoR4
This reverts commit 1a60cef.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds a complete, production-ready example sketch demonstrating the WiThrottleProtocol library on an M5StickC Plus 2 microcontroller with an M5Stack MiniEncoderC HAT. The example implements a fully functional portable DCC throttle with WiFi provisioning, server discovery, roster management, and real-time locomotive control.
Key Changes
Main sketch (
WiThrottleProtocol_M5StickCPlus2.ino): Complete state machine implementing the throttle application with:AppDelegate (
AppDelegate.h): WiThrottleProtocolDelegate subclass that:EncoderHat (
EncoderHat.h/cpp): I2C driver for the MiniEncoderC HAT exposing:Provision (
Provision.h/cpp): Captive-portal WiFi setup with:UI (
UI.h/cpp): TFT rendering layer providing:config.h: Compile-time configuration including:
README.md: Complete build and usage documentation covering hardware setup, toolchain configuration (Arduino IDE and PlatformIO), first-boot provisioning flow, and driving controls.
Notable Implementation Details
[-126..+126]with automatic snap-through-zero to prevent fighting the encoder at direction changesCo-authored by Claude Code