Record the working configuration

- Pin the toolchain versions, with the Legoino/core 3.x error text.
- Note DIR_* direction constants and the corrected control table.
- Add the swapped-hub-address story to step 4, and the Arduino
  prototype-ordering trap to the gotchas list.
This commit is contained in:
2026-09-10 11:40:34 +10:00
parent 39bc43a878
commit 8fac5c893e
+51 -7
View File
@@ -4,6 +4,9 @@ PS4 controller -> two LEGO Technic hubs, using two ESP32 boards.
Driving a Johnny 5 (Short Circuit) MOC — seven motors across two hubs.
**Status: working on hardware.** The configuration in this repo is the one that
runs, not the one that ought to.
## Why two boards
The obvious sketch — Bluepad32 for the gamepad, Legoino for the hubs, one ESP32 —
@@ -25,6 +28,28 @@ else. Three jumper wires between them.
BTstack, lives in `ps4-lego-onebrain`. It works, but it is a lot more code and
a lot more ways to be wrong.)
## Toolchain versions — pin these
| Board | Package / library | Version |
| --- | --- | --- |
| A | esp32_bluepad32 | 4.1.0 |
| B | esp32 (Espressif) | **2.0.17** |
| B | NimBLE-Arduino | **1.4.x** |
| B | Legoino | latest |
Board B is the fussy one. **Core 3.x will not build Legoino.** ESP-IDF 5 changed
`uint32_t` from `unsigned int` to `unsigned long`, so Legoino's own header and
.cpp no longer agree, and it never included `<string>` itself. The symptoms are:
```
'string' in namespace 'std' does not name a type
no declaration matches 'uint32_t LegoinoCommon::ReadUInt32LE(uint8_t*, int)'
```
Downgrade the core rather than patching the library — patching works, but it is
the first of several layers and any Library Manager update silently reverts it.
Board A is unaffected; it uses a separate package.
## Hardware
* 2x ESP32 dev boards. Both must be the **original ESP32** (WROOM/WROVER).
@@ -123,21 +148,28 @@ the sticks. If they do, board A is finished.
**3. Board B — the hub board.**
Boards Manager -> the standard **esp32** package by Espressif. Library Manager
-> **NimBLE-Arduino**, pinned to **1.4.x** (Legoino has not moved to the 2.x
API, and 2.x gives a wall of compile errors), then **Legoino**.
Boards Manager -> the standard **esp32** package by Espressif, version 2.0.17.
Library Manager -> **NimBLE-Arduino** pinned to **1.4.x**, then **Legoino**. See
the toolchain table above for why the versions matter.
Flash `tools/hub_scanner/hub_scanner.ino` first. Press each hub's green button
and note the addresses it reports, then paste them into `receiver.ino` as
`HUB0_ADDR` and `HUB1_ADDR`.
Nothing else may be holding the hubs. A hub accepts one connection at a time and
stops advertising once taken, so power down any other ESP32 and close the LEGO
Powered Up app before scanning.
**4. Verify the port map before you trust it.**
Flash `receiver/receiver.ino` with `DEBUG_MOTORS` set to 1. Every motor command
logs which hub and port it lands on. Move one control at a time and check the
log matches what physically moves. This is worth doing properly — a swapped hub
address or a motor in the wrong port looks exactly like a software bug and will
waste an afternoon.
log matches what physically moves.
This step is not optional busywork. On this build the two hub addresses were the
wrong way round, and the symptom — an arm moving when the track stick moved —
looks exactly like a software bug. It is not one, and no amount of reading the
code will find it.
Set `DEBUG_MOTORS` back to 0 once it checks out.
@@ -158,13 +190,17 @@ Tank drive — every input drives exactly one motor.
| D-pad left / right | Head turn (hub 1 port B) |
| R2 / L2 | Body lift up / down, proportional (hub 0 port D) |
| Square / Circle | Left arm up / down (hub 1 port C) |
| Triangle / Cross | Right arm up / down (hub 1 port D) |
| Cross / Triangle | Right arm up / down (hub 1 port D) |
| L1 held | Precision, 40% track speed |
| R1 held | Full, 100% track speed |
| L1 + R1 | All stop |
Default track scale is 75%. Stick X axes are unused.
**Motor directions** are the `DIR_*` constants at the top of `receiver.ino`. Flip
one to `-1` if an axis runs backwards — mirrored mountings are normal on a
symmetric model. `DIR_RIGHT_TRACK` is `-1` on this build.
**Everything except the tracks runs into a mechanical end stop**, and there is no
position feedback, so holding a direction at a stop stalls the motor. That is
what `HEAD_MAX`, `LIFT_MAX` and `ARM_MAX` are for. Lower them if an axis feels
@@ -189,10 +225,18 @@ stops all motors if nothing valid arrives for 400ms.
## Things that bite
* **`'Frame' has not been declared`, or any type "not declared" at its own
definition.** The Arduino IDE injects generated prototypes immediately before
the first function definition in the file. Any type used in a signature must be
declared above that point — which is why `HubLink`, `PortState` and `Frame` all
sit in one block near the top. Do not move a function above them.
* **Legoino will not compile.** Core 3.x. See the toolchain table.
* **Nothing arrives at board B.** TX/RX swapped, or no common ground. Both are
silent failures.
* **Random reboots under load.** Supply, not software. See the current budget
above.
* **Hubs never connect.** Something else is holding them, or they went to sleep.
Power down other controllers, close the LEGO app, press the green buttons.
* **One hub connects, the other does not.** Legoino shares a single NimBLE
scanner. `receiver.ino` connects them strictly one at a time for this reason —
do not "optimise" that into two parallel `init()` calls.