# 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`. ## Wiring (placeholders; edit `Config.h` to match) | 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 | | MPU6050 | SDA / SCL | 18 / 19 (Wire) | | Nextion | TX → RX2, RX ← TX2 | 7 / 8 (Serial2), 5 V supply | | Left stick | X / Y / twist | A0 / A1 / A2 | | Right stick | X / Y / twist | A3 / A6 / A7 | 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 | | 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. 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 | | `]` / `[` | height up / down 10% | | | | `c` | start / save calibration | `x` | cancel calibration | | `p` | print status (raw + mapped sticks, IMU, link, state, cal) | `d` | toggle 10 Hz packet dump | | `R` | reset stick cal to Config defaults | | | ## 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.