117 lines
4.7 KiB
Markdown
117 lines
4.7 KiB
Markdown
# ps4-lego-bridge
|
|
|
|
PS4 controller -> two LEGO Powered Up / Technic hubs, using two ESP32 boards.
|
|
|
|
## Why two boards
|
|
|
|
The obvious sketch — Bluepad32 for the gamepad, Legoino for the hubs, one ESP32 —
|
|
does not work, and it is not a library-version problem.
|
|
|
|
* **Bluepad32** needs Bluetooth Classic (BR/EDR), because a DualShock 4 is a
|
|
BR/EDR device. It gets that from **BTstack**, and it ships as a whole
|
|
replacement ESP32 board package rather than a normal library.
|
|
* **Legoino** talks to the hubs over BLE using **NimBLE-Arduino**.
|
|
|
|
BTstack and NimBLE are both Bluetooth *host* stacks. There is one radio and one
|
|
VHCI interface on the chip, and whichever stack registers second wins. You
|
|
cannot run both in one firmware image. Every mature project in this space
|
|
(Anton's Mindstorms LMS-ESP32, the Pybricks bridges) solves it the same way:
|
|
one chip owns the gamepad, and it hands the data to something else over a wire.
|
|
|
|
So: board A runs Bluepad32 and nothing else. Board B runs Legoino and nothing
|
|
else. Three jumper wires between them.
|
|
|
|
## Hardware
|
|
|
|
* 2x ESP32 dev boards. Both must be the **original ESP32** (WROOM/WROVER).
|
|
S3, C3, C6 and H2 have no Bluetooth Classic, so a PS4 pad will not pair.
|
|
* 3 jumper wires.
|
|
|
|
```
|
|
Board A (Bluepad32) Board B (Legoino)
|
|
GPIO17 TX -------------> GPIO16 RX
|
|
GPIO16 RX <------------- GPIO17 TX
|
|
GND ------------- GND <- do not skip this one
|
|
```
|
|
|
|
Power both from the same supply if you can. Separate USB bricks are fine as
|
|
long as the grounds are tied together.
|
|
|
|
## Software setup
|
|
|
|
### Board A — transmitter
|
|
|
|
1. Arduino IDE -> Preferences -> Additional board manager URLs, add:
|
|
`https://raw.githubusercontent.com/ricardoquesada/esp32-arduino-lib-builder/master/bluepad32_files/package_esp32_bluepad32_index.json`
|
|
2. Boards Manager -> install **esp32_bluepad32**.
|
|
3. Tools -> Board -> **esp32_bluepad32** -> ESP32 Dev Module.
|
|
Selecting the board from the normal `esp32` package is what produces
|
|
`fatal error: Bluepad32.h: No such file or directory`. Bluepad32 is not in
|
|
the Library Manager and installing it as a ZIP library will not help.
|
|
4. Flash `transmitter/transmitter.ino`.
|
|
|
|
### Board B — receiver
|
|
|
|
1. Boards Manager -> the standard **esp32** package by Espressif.
|
|
2. Library Manager -> **NimBLE-Arduino**. Pin this to **1.4.x**. Legoino has
|
|
not moved to the NimBLE 2.x API, so 2.x gives a wall of compile errors.
|
|
3. Library Manager -> **Legoino**.
|
|
4. Flash `tools/hub_scanner/hub_scanner.ino` first, note both hub addresses,
|
|
paste them into `receiver.ino`, then flash `receiver/receiver.ino`.
|
|
|
|
## Controls as shipped
|
|
|
|
| Input | Action |
|
|
| ----------------- | ------------------------------- |
|
|
| Left stick Y | Left track, hub 1 port A |
|
|
| Right stick Y | Right track, hub 1 port B |
|
|
| Left stick X | Head rotation, hub 2 port A |
|
|
|
|
Change the mapping in the frame-handling block of `receiver.ino`.
|
|
|
|
## Link protocol
|
|
|
|
ASCII, newline terminated, 115200 8N1:
|
|
|
|
```
|
|
G,<lx>,<ly>,<rx>,<ry>,<buttons>*<XX>\n
|
|
```
|
|
|
|
* axes are Bluepad32 raw values, -512..511
|
|
* `buttons` is the 16-bit mask as a decimal number
|
|
* `XX` is a two-digit hex XOR checksum of everything before the `*`
|
|
|
|
Being plain text means you can watch the link with a USB-serial adapter when
|
|
something misbehaves.
|
|
|
|
## Things that bite
|
|
|
|
* **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.
|
|
* **Motors stutter or the hub drops out.** You are sending commands faster than
|
|
the hub can chew. `MOTOR_MIN_INTERVAL_MS` throttles per port; raise it before
|
|
blaming the radio.
|
|
* **More than 3 hubs later on.** Edit `CONFIG_BT_NIMBLE_MAX_CONNECTIONS` in
|
|
`NimBLE-Arduino/src/nimconfig.h`, then restart the IDE to force a rebuild.
|
|
* **Wrong motor command.** Technic/Control+ motors want `setTachoMotorSpeed`;
|
|
train and simple PU motors want `setBasicMotorSpeed`. Toggle
|
|
`USE_TACHO_MOTORS` in `receiver.ino`.
|
|
* **Bluepad32 API drift.** v4 renamed `GamepadPtr` to `ControllerPtr` and
|
|
`BP32_MAX_GAMEPADS` to `BP32_MAX_CONTROLLERS`. This code uses the v4 names.
|
|
|
|
## If you really want one board
|
|
|
|
Two options, both more work than a second ESP32:
|
|
|
|
* Write the LEGO Wireless Protocol GATT client directly against BTstack inside
|
|
the Bluepad32 sketch. Bluepad32 v4 does support BLE, so the radio can do it —
|
|
you would just be reimplementing the parts of Legoino you need. The motor
|
|
command is only eight bytes.
|
|
* Put Pybricks on the hubs and drive them from an LMS-ESP32 running the
|
|
prebuilt Bluepad32 LPF2 firmware over the LEGO wire.
|
|
|
|
---
|
|
|
|
Created by: Jess Rogerson (yelling commands at Claude.AI)
|