Update for Johnny 5 build: controls doc, notification decoding, end-stop warning
This commit is contained in:
@@ -3,12 +3,16 @@
|
|||||||
One ESP32. PS4 controller over Bluetooth Classic, two LEGO Powered Up hubs over
|
One ESP32. PS4 controller over Bluetooth Classic, two LEGO Powered Up hubs over
|
||||||
BLE, both on the same Bluetooth stack.
|
BLE, both on the same Bluetooth stack.
|
||||||
|
|
||||||
|
Driving a Johnny 5 (Short Circuit) MOC — seven motors across two Technic hubs.
|
||||||
|
|
||||||
This is the harder of the two routes. If you want the boring one that works
|
This is the harder of the two routes. If you want the boring one that works
|
||||||
first go, see `ps4-lego-bridge` — two boards and a UART between them.
|
first go, see `ps4-lego-bridge` — two boards and a UART between them.
|
||||||
|
|
||||||
**Build and deploy instructions: [docs/BUILD.md](docs/BUILD.md).** This will not
|
**Build and deploy: [docs/BUILD.md](docs/BUILD.md).** This will not compile out
|
||||||
compile out of the box — the board package ships the BTstack headers but does
|
of the box — the board package ships the BTstack headers but does not put them
|
||||||
not put them on the include path, and that has to be fixed first.
|
on the include path, and that has to be fixed first.
|
||||||
|
|
||||||
|
**Controls and port map: [docs/CONTROLS.md](docs/CONTROLS.md).**
|
||||||
|
|
||||||
## How it avoids the stack collision
|
## How it avoids the stack collision
|
||||||
|
|
||||||
@@ -21,9 +25,9 @@ it supports BLE gamepads like the Xbox v5 firmware pads. That machinery is
|
|||||||
sitting there unused for our purposes, so this sketch borrows it and speaks the
|
sitting there unused for our purposes, so this sketch borrows it and speaks the
|
||||||
LEGO Wireless Protocol directly. No Legoino, no NimBLE.
|
LEGO Wireless Protocol directly. No Legoino, no NimBLE.
|
||||||
|
|
||||||
What you give up: everything Legoino did for you. Sensor callbacks, port device
|
What you give up: everything Legoino did for you. Sensor callbacks, tacho
|
||||||
detection, tacho positioning, hub name reads. What you get back is one board and
|
positioning, hub name reads. What you get back is one board and about 450 lines.
|
||||||
about 300 lines.
|
Port detection we got back cheaply — see below.
|
||||||
|
|
||||||
## The two rules
|
## The two rules
|
||||||
|
|
||||||
@@ -33,9 +37,9 @@ anywhere is `btstack_run_loop_execute_on_main_thread()`.
|
|||||||
|
|
||||||
So the LEGO side lives entirely inside a repeating BTstack timer, which is
|
So the LEGO side lives entirely inside a repeating BTstack timer, which is
|
||||||
scheduled once at boot via that one safe call. `loop()` never touches BTstack —
|
scheduled once at boot via that one safe call. `loop()` never touches BTstack —
|
||||||
it reads the sticks and writes three plain ints into `gDesired`, and the timer
|
it reads the sticks and writes plain ints into `gDesired`, and the timer picks
|
||||||
picks them up. Any BTstack call added to `loop()` will crash at random places,
|
them up. Any BTstack call added to `loop()` will crash at random places, which
|
||||||
which is the worst kind of bug to chase.
|
is the worst kind of bug to chase.
|
||||||
|
|
||||||
**2. Boot order.** Gamepad discovery starts *disabled*. Both hubs connect first,
|
**2. Boot order.** Gamepad discovery starts *disabled*. Both hubs connect first,
|
||||||
then `BP32.enableNewBluetoothConnections(true)` opens up for the pad. Bluepad32's
|
then `BP32.enableNewBluetoothConnections(true)` opens up for the pad. Bluepad32's
|
||||||
@@ -47,11 +51,12 @@ and the hub connects unreliably or not at all.
|
|||||||
```
|
```
|
||||||
ps4_lego_onebrain/ps4_lego_onebrain.ino the sketch
|
ps4_lego_onebrain/ps4_lego_onebrain.ino the sketch
|
||||||
docs/BUILD.md install, include-path fix, first run
|
docs/BUILD.md install, include-path fix, first run
|
||||||
|
docs/CONTROLS.md port map, control layout, power caps
|
||||||
docs/platform.local.txt the include-path override, for restoring
|
docs/platform.local.txt the include-path override, for restoring
|
||||||
after a Bluepad32 package update
|
after a Bluepad32 package update
|
||||||
```
|
```
|
||||||
|
|
||||||
## The protocol, in full
|
## The protocol
|
||||||
|
|
||||||
Everything the motors need is one 8-byte write, no response, to characteristic
|
Everything the motors need is one 8-byte write, no response, to characteristic
|
||||||
`00001624-1212-EFDE-1623-785FEABCD123` inside service `...1623...`:
|
`00001624-1212-EFDE-1623-785FEABCD123` inside service `...1623...`:
|
||||||
@@ -76,44 +81,34 @@ There is no tacho-vs-basic distinction to get wrong here. The trade-off is that
|
|||||||
you get no closed-loop speed control — power is power, and the motor slows under
|
you get no closed-loop speed control — power is power, and the motor slows under
|
||||||
load. `StartSpeed` (subcommand `0x07`) does regulate, if you want it later.
|
load. `StartSpeed` (subcommand `0x07`) does regulate, if you want it later.
|
||||||
|
|
||||||
|
Going the other way, the sketch subscribes to notifications on the same
|
||||||
|
characteristic and decodes two inbound messages:
|
||||||
|
|
||||||
|
* **`0x04` Hub Attached I/O** — the hub announces every populated port the
|
||||||
|
instant notifications go live, so the serial log tells you what is actually
|
||||||
|
plugged in rather than what you think is. Device type IDs come from
|
||||||
|
[pybricks/technical-info](https://github.com/pybricks/technical-info/blob/master/assigned-numbers.md).
|
||||||
|
* **`0x05` Generic Error** — rejected commands used to vanish silently.
|
||||||
|
|
||||||
Full spec: https://lego.github.io/lego-ble-wireless-protocol-docs/
|
Full spec: https://lego.github.io/lego-ble-wireless-protocol-docs/
|
||||||
|
|
||||||
## What to expect on the bench
|
|
||||||
|
|
||||||
Verified: the Bluepad32 board-package install, the threading contract and
|
|
||||||
`btstack_run_loop_execute_on_main_thread()`, the BTstack GATT client call
|
|
||||||
signatures, the LWP3 message encoding above, and — on Jess's machine — that
|
|
||||||
package 4.1.0 ships every header this needs under `tools\sdk\esp32\include\`.
|
|
||||||
|
|
||||||
Not verified, because it needs the hardware powered up:
|
|
||||||
|
|
||||||
* **Address type.** LEGO's OUI is `90:84:2B`, a public range, so the sketch uses
|
|
||||||
`BD_ADDR_TYPE_LE_PUBLIC`. If the connect times out repeatedly with no LE
|
|
||||||
connection-complete event, switch `HUB_ADDR_TYPE` to `BD_ADDR_TYPE_LE_RANDOM`.
|
|
||||||
* **Whether BLE is enabled in your Bluepad32 build.** If `gap_connect` never
|
|
||||||
produces a connection-complete, this is the next thing to check.
|
|
||||||
* **The LE connection-complete event name.** BTstack moved this from
|
|
||||||
`HCI_EVENT_LE_META`/`HCI_SUBEVENT_LE_CONNECTION_COMPLETE` to
|
|
||||||
`HCI_EVENT_META_GAP`/`GAP_SUBEVENT_LE_CONNECTION_COMPLETE`. The sketch handles
|
|
||||||
both, with the newer one behind `#ifdef`, so whichever your version emits will
|
|
||||||
land. Guarded by connection state so a BLE gamepad connecting can't be
|
|
||||||
mistaken for a hub.
|
|
||||||
|
|
||||||
Turn on verbose BTstack logging before you start guessing. Nearly every failure
|
|
||||||
here shows up as a specific HCI event you can read.
|
|
||||||
|
|
||||||
## Known rough edges
|
## Known rough edges
|
||||||
|
|
||||||
* Commands are throttled to one per port per 60ms (`MOTOR_MIN_GAP_MS`). Push
|
* **No end-stop protection.** Every axis except the tracks runs into a
|
||||||
that lower and the hub's BLE queue backs up and it drops the link. Stops are
|
mechanical stop and there is no position feedback, so holding a direction at
|
||||||
exempt and always go out immediately.
|
a stop stalls the motor. The power caps in `docs/CONTROLS.md` are the only
|
||||||
|
thing standing between you and stripped gears. Control+ motors have absolute
|
||||||
|
encoders, so this is fixable properly with Port Input Format Setup (`0x41`).
|
||||||
* Write-without-response can fail silently when ACL buffers are full. The tick
|
* Write-without-response can fail silently when ACL buffers are full. The tick
|
||||||
retries 25ms later, so a dropped command is invisible in practice, but that is
|
retries 25ms later, so a dropped command is invisible in practice, but do not
|
||||||
why you should not treat a single write as a guarantee.
|
treat a single write as a guarantee.
|
||||||
* There is no failsafe timer on the gamepad side beyond `isConnected()`. If the
|
* No failsafe timer on the gamepad side beyond `isConnected()`. If the pad goes
|
||||||
pad goes out of range the stack can take 20 seconds to notice. The Bluepad32
|
out of range the stack can take 20 seconds to notice. The Bluepad32 FAQ has a
|
||||||
FAQ has a `hasData()` plus timeout pattern worth adding if this ends up
|
`hasData()` plus timeout pattern worth adding.
|
||||||
driving something that can fall off a table.
|
* Rate limits are tuned by feel, not measurement — see `docs/CONTROLS.md`.
|
||||||
|
|
||||||
|
If something misbehaves, turn on verbose BTstack logging first. Nearly every
|
||||||
|
failure here shows up as a specific HCI event you can read.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user