2026-09-28 18:14:00 +02:00
|
|
|
|
# Hexapod remote firmware (Teensy 4.1)
|
|
|
|
|
|
|
|
|
|
|
|
Handheld transmitter for the hexapod. It reads two 3-axis twist-grip
|
|
|
|
|
|
joysticks and an MPU6050, takes operator commands from a Nextion
|
|
|
|
|
|
touchscreen, and sends a `CmdPacket` (defined in
|
|
|
|
|
|
`../shared/HexapodProtocol/HexapodProtocol.h`) to the robot over nRF24L01
|
|
|
|
|
|
at 100 Hz.
|
|
|
|
|
|
|
|
|
|
|
|
## Building
|
|
|
|
|
|
|
|
|
|
|
|
From the repo root: `pio run -e remote`, flash with
|
|
|
|
|
|
`pio run -e remote -t upload`, monitor with `pio device monitor -e remote`.
|
|
|
|
|
|
|
2026-10-01 15:29:21 +02:00
|
|
|
|
## Wiring (from the old remote sketch in `LEGACY/remote/teensy/`; edit `Config.h` if yours differs)
|
2026-09-28 18:14:00 +02:00
|
|
|
|
|
|
|
|
|
|
| Part | Signal | Teensy 4.1 pin |
|
|
|
|
|
|
|------|--------|----------------|
|
|
|
|
|
|
| nRF24L01 | MOSI / MISO / SCK | 11 / 12 / 13 (SPI0) |
|
|
|
|
|
|
| nRF24L01 | CE / CSN | 9 / 10 |
|
|
|
|
|
|
| nRF24L01 | VCC | 3.3 V, with a 10–100 µF cap right at the module |
|
2026-10-01 15:29:21 +02:00
|
|
|
|
| MPU6050 | SDA / SCL | **25 / 24 (Wire2)**: not 18/19, those are stick pins A4/A5 |
|
|
|
|
|
|
| Nextion | TX → RX1, RX ← TX1 | 0 / 1 (Serial1), 5 V supply |
|
|
|
|
|
|
| Sticks | 6 axes | A0–A5. Which axis is on which pin isn't known yet: reorder `STICK_PINS` until the `p` dump reads right |
|
|
|
|
|
|
|
|
|
|
|
|
**MPU6050 rewiring:** the old remote had its MPU code switched off, probably
|
|
|
|
|
|
because A4/A5 double as Wire's pins. Move the MPU's SDA/SCL to pins 25/24.
|
2026-09-28 18:14:00 +02:00
|
|
|
|
|
|
|
|
|
|
Stick pots are wired 3.3 V – wiper – GND. **Never feed 5 V into a Teensy
|
|
|
|
|
|
4.x analog pin.**
|
|
|
|
|
|
|
|
|
|
|
|
## Control scheme
|
|
|
|
|
|
|
|
|
|
|
|
| Input | Packet field | Meaning on the robot |
|
|
|
|
|
|
|-------|--------------|----------------------|
|
|
|
|
|
|
| Left stick forward/back | `vx` | walk forward/back |
|
|
|
|
|
|
| Left stick left/right | `vy` | strafe (mode A); heading target direction (mode B) |
|
|
|
|
|
|
| Left stick twist | `yaw_rate` | turn rate (mode A only; ignored in mode B) |
|
|
|
|
|
|
| Right stick, **ROTATE** (default) | `rx/ry/rz` | body roll / pitch / yaw |
|
|
|
|
|
|
| Right stick, **MOVE** (`FLAG_RSTICK_XLATE`) | `rx/ry/rz` | body X / Y / Z (height, via twist) offset |
|
|
|
|
|
|
| Handset tilt | `remote_roll/pitch` | extra body roll/pitch while REMOTE GYRO is on |
|
2026-09-29 00:37:12 +02:00
|
|
|
|
| Height slider | `body_height` | standing height, default ± `STANCE_HEIGHT_RANGE_MM` (robot rate-limits the change) |
|
|
|
|
|
|
|
|
|
|
|
|
In **HEADING** mode the left stick steers, relative to where the robot
|
|
|
|
|
|
faces at that moment. Forward/back walks forward/back, and sideways
|
|
|
|
|
|
steers: forward+left or back+left walk while turning left, and left alone
|
|
|
|
|
|
turns on the spot for as long as you hold it. Let go and the robot holds
|
|
|
|
|
|
its heading.
|
2026-09-28 18:14:00 +02:00
|
|
|
|
|
|
|
|
|
|
Touchscreen: ARM / DISARM, SIT/STAND, TRIPOD/WAVE, DIRECT/HEADING,
|
|
|
|
|
|
R: ROTATE/MOVE, REMOTE GYRO, ROBOT GYRO, speed slider, LEVEL (re-zero the
|
|
|
|
|
|
handset tilt), CAL / CANCEL CAL. `NEXTION.md` has the HMI build guide.
|
|
|
|
|
|
|
|
|
|
|
|
### Safety interlocks (in `RemoteState`)
|
|
|
|
|
|
|
|
|
|
|
|
- **Arming** is refused unless every stick axis is centered, and never
|
|
|
|
|
|
allowed during calibration.
|
|
|
|
|
|
- **Disarming** always clears the stand request. The robot arms into SIT,
|
|
|
|
|
|
so re-arming never stands it up by itself.
|
|
|
|
|
|
- **Link loss:** if no ack arrives for `LINK_LOST_MS` (300 ms, matching the
|
|
|
|
|
|
robot's failsafe), the remote disarms itself. When the link comes back
|
|
|
|
|
|
the robot stays down until you re-arm deliberately.
|
|
|
|
|
|
- **Sit/stand** requests are only accepted while armed. `FLAG_WALK` is only
|
|
|
|
|
|
sent while standing is requested.
|
|
|
|
|
|
- While disarmed or calibrating, all motion fields are sent as zero.
|
|
|
|
|
|
|
|
|
|
|
|
### Smoothing
|
|
|
|
|
|
|
|
|
|
|
|
- **Speed scale** ramps toward the slider at `SPEED_SCALE_RATE_PER_SEC`.
|
|
|
|
|
|
The robot uses speed scale directly on the walking twist, so a slider
|
|
|
|
|
|
jump would otherwise be a velocity step.
|
|
|
|
|
|
- **Remote tilt** fades in/out over ~0.5 s when REMOTE GYRO is toggled, and
|
|
|
|
|
|
the handset re-zeroes its "level" at the moment you enable it.
|
|
|
|
|
|
- **FLAG_WALK** lingers 250 ms after the left stick centers, so hovering at
|
|
|
|
|
|
the deadzone edge doesn't make the robot chatter between STAND and
|
|
|
|
|
|
WALKING.
|
|
|
|
|
|
- Body pose smoothing is left to the robot's `BodyPose` rate limiters.
|
|
|
|
|
|
|
|
|
|
|
|
## Stick calibration
|
|
|
|
|
|
|
|
|
|
|
|
1. Power on with both sticks released and the remote held still. Each
|
|
|
|
|
|
axis's resting center is captured automatically.
|
|
|
|
|
|
2. For full calibration, disarm, then tap **CAL** (or press `c` on the
|
|
|
|
|
|
serial console). Move every axis of both sticks to both limits, release
|
|
|
|
|
|
them, and tap **SAVE CAL** (`c`). The result is saved to EEPROM.
|
|
|
|
|
|
3. If any axis moved less than `CAL_MIN_HALF_SPAN` either side of center,
|
|
|
|
|
|
the save is refused and you stay in calibration mode. **CANCEL CAL**
|
|
|
|
|
|
(`x`) aborts.
|
|
|
|
|
|
4. `p` prints the calibration in a form you can paste into `Config.h`
|
|
|
|
|
|
defaults. `R` wipes EEPROM back to the `Config.h` defaults.
|
|
|
|
|
|
|
|
|
|
|
|
## Serial console (USB, 115200, single keystrokes)
|
|
|
|
|
|
|
|
|
|
|
|
Every touchscreen control is available here, so you can bench-test with no
|
|
|
|
|
|
display attached.
|
|
|
|
|
|
|
|
|
|
|
|
| Key | Action | Key | Action |
|
|
|
|
|
|
|-----|--------|-----|--------|
|
|
|
|
|
|
| `a` | toggle arm | `k` | disarm |
|
|
|
|
|
|
| `s` | toggle sit/stand | `g` | tripod ↔ wave |
|
|
|
|
|
|
| `m` | direct ↔ heading-hold | `t` | right stick rotate ↔ translate |
|
|
|
|
|
|
| `r` | remote gyro on/off | `b` | robot gyro on/off |
|
|
|
|
|
|
| `+` / `-` | speed ±25 | `z` | zero handset level |
|
2026-09-29 00:37:12 +02:00
|
|
|
|
| `]` / `[` | height up / down 10% | | |
|
2026-09-28 18:14:00 +02:00
|
|
|
|
| `c` | start / save calibration | `x` | cancel calibration |
|
|
|
|
|
|
| `p` | print status (raw + mapped sticks, IMU, link, state, cal) | `d` | toggle 10 Hz packet dump |
|
2026-10-01 15:29:21 +02:00
|
|
|
|
| `R` | reset stick cal to Config defaults | `U` | Nextion upload mode (USB ↔ display; power-cycle to leave) |
|
2026-09-28 18:14:00 +02:00
|
|
|
|
|
|
|
|
|
|
## Bench-check checklist
|
|
|
|
|
|
|
|
|
|
|
|
1. **Stick directions:** with `p`, confirm each logical axis reads
|
|
|
|
|
|
positive for right / forward / counterclockwise twist. Flip it in
|
|
|
|
|
|
`STICK_INVERT[]` if not.
|
|
|
|
|
|
2. **IMU mounting:** with `p`, dip the right edge down to get +roll, and
|
|
|
|
|
|
tip the far edge down to get +pitch. If not, fix `IMU_AXIS_FROM_CHIP` /
|
|
|
|
|
|
`IMU_AXIS_SIGN`.
|
|
|
|
|
|
3. **Nextion baud:** `bauds=115200` must be in Program.s, or the display
|
|
|
|
|
|
shows nothing useful.
|
|
|
|
|
|
4. **Radio:** with the robot powered, `tLink` / `p` should show ~100%. If
|
|
|
|
|
|
it reads NO LINK, check that both boards were built from the same
|
|
|
|
|
|
`HexapodProtocol.h`. The static payload size is part of the handshake.
|
|
|
|
|
|
5. **End to end with the robot on a stand:** use `d` to dump packets and
|
|
|
|
|
|
confirm each field changes the way the table above says.
|