Not yet verified on hardware. - shared/HexapodProtocol: CmdPacket (now 20 bytes, adds remote_roll / remote_pitch), FLAG_* bits and radio settings as the single protocol definition; static payload size = sizeof(CmdPacket) on both radios. - robot: use the shared protocol; vx = forward (harness w/x <-> q/e); InputMapper adds remote tilt on top of the right stick when FLAG_REMOTE_GYRO is set, and robot.ino no longer zeroes stick roll/pitch when it's off. - remote (Teensy 4.1): Sticks (calibration, EEPROM), RemoteIMU, RemoteState (interlocks, link-loss disarm, smoothing), PacketBuilder, RadioLink, NextionUI, SerialConsole. - CLAUDE.md, README.md, remote/README.md, remote/NEXTION.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
111 lines
6.1 KiB
C++
111 lines
6.1 KiB
C++
#pragma once
|
|
#include <Arduino.h>
|
|
|
|
// =====================================================================
|
|
// Remote handset config (Teensy 4.1). Everything here is a placeholder
|
|
// until checked against your real wiring and parts - see the BENCH notes.
|
|
// Radio address/channel/rate/PA/payload size are NOT here: they live in
|
|
// shared/HexapodProtocol so they can't drift from the robot.
|
|
// =====================================================================
|
|
|
|
// ---- Loop rate ----
|
|
// Must be >= the robot's CONTROL_HZ (60) so every robot control tick sees a
|
|
// fresh packet. 100 Hz gives margin for the odd dropped packet.
|
|
constexpr float TX_HZ = 100.0f;
|
|
|
|
// ---- Radio (nRF24L01) on SPI0: MOSI 11, MISO 12, SCK 13 ----
|
|
// BENCH: placeholder CE/CSN pins - match your wiring.
|
|
constexpr uint8_t RADIO_CE_PIN = 9;
|
|
constexpr uint8_t RADIO_CSN_PIN = 10;
|
|
// Auto-retransmit: (delay units of 250us, count). Kept short on purpose - a
|
|
// newer packet replaces a lost one 10 ms later anyway, and write() blocks for
|
|
// the whole retry sequence. Worst case here is ~4 attempts x ~1.7 ms.
|
|
constexpr uint8_t RADIO_RETRY_DELAY = 1;
|
|
constexpr uint8_t RADIO_RETRY_COUNT = 3;
|
|
// No acks for this long -> remote considers the link lost and disarms itself,
|
|
// mirroring the robot's LINK_TIMEOUT_SEC (0.3 s) failsafe, so the robot can't
|
|
// spring back up on its own when the link returns.
|
|
constexpr uint32_t LINK_LOST_MS = 300;
|
|
|
|
// ---- Joysticks: 2 x 3-axis analog potentiometer sticks ----
|
|
// Logical axis order used everywhere in the remote code.
|
|
enum StickAxis : uint8_t { L_X = 0, L_Y, L_TWIST, R_X, R_Y, R_TWIST, NUM_AXES };
|
|
|
|
// BENCH: placeholder pins. A4/A5 (18/19) are skipped because they're the I2C
|
|
// pins the MPU6050 uses.
|
|
constexpr uint8_t STICK_PINS[NUM_AXES] = { A0, A1, A2, A3, A6, A7 };
|
|
|
|
// Each logical axis must end up: X + = pushed RIGHT, Y + = pushed FORWARD
|
|
// (away from you), TWIST + = rotated COUNTERCLOCKWISE seen from above.
|
|
// BENCH: flip any axis that reads the wrong way on the serial 'p' status dump.
|
|
constexpr bool STICK_INVERT[NUM_AXES] = { false, false, false, false, false, false };
|
|
|
|
constexpr int ADC_BITS = 12; // 0..4095
|
|
constexpr int ADC_AVERAGING = 8; // hardware averaging per analogRead()
|
|
|
|
// Fallback calibration used until you run the on-device sweep calibration
|
|
// (which is saved to EEPROM and overrides these).
|
|
// BENCH: typical pot sticks don't use the full electrical range - run the
|
|
// sweep calibration once, then optionally paste the printed values here.
|
|
constexpr int16_t STICK_DEFAULT_MIN = 200;
|
|
constexpr int16_t STICK_DEFAULT_CENTER = 2048;
|
|
constexpr int16_t STICK_DEFAULT_MAX = 3900;
|
|
|
|
// At power-on the resting position of each axis is re-captured as its center
|
|
// (pots drift a little with temperature). Rejected if a stick is visibly
|
|
// deflected, i.e. further than this from the stored center (ADC counts).
|
|
constexpr int16_t CENTER_CAPTURE_TOLERANCE = 250;
|
|
// A sweep calibration is rejected unless every axis travels at least this
|
|
// far on both sides of center (ADC counts) - catches "forgot to move a stick".
|
|
constexpr int16_t CAL_MIN_HALF_SPAN = 600;
|
|
|
|
// Deadzone as a fraction of full deflection; output is rescaled so motion
|
|
// starts smoothly from 0 at the deadzone edge instead of jumping.
|
|
constexpr float STICK_DEADZONE[NUM_AXES] = { 0.06f, 0.06f, 0.10f, 0.06f, 0.06f, 0.10f };
|
|
// 0 = linear, 1 = fully cubic. A bit of expo gives fine control near center.
|
|
constexpr float STICK_EXPO[NUM_AXES] = { 0.25f, 0.25f, 0.30f, 0.20f, 0.20f, 0.20f };
|
|
// One-pole low-pass on raw ADC counts (1 = no filtering). Pots are noisy.
|
|
constexpr float STICK_FILTER_ALPHA = 0.5f;
|
|
|
|
// ---- Remote IMU: MPU6050 on I2C (Wire: SDA 18, SCL 19) ----
|
|
constexpr uint8_t IMU_ADDR = 0x68; // 0x69 if AD0 is pulled high
|
|
// Mounting remap: which chip axis (0=X,1=Y,2=Z) and sign gives each handset
|
|
// axis. Handset frame: +X = away from you, +Y = your left, +Z = up.
|
|
// BENCH: with the defaults the chip is assumed flat, component side up, its
|
|
// X arrow pointing away from you. Verify with the serial 'p' dump: tipping
|
|
// the far edge down must read +pitch, dipping the right side must read +roll.
|
|
constexpr uint8_t IMU_AXIS_FROM_CHIP[3] = { 0, 1, 2 };
|
|
constexpr int8_t IMU_AXIS_SIGN[3] = { +1, +1, +1 };
|
|
constexpr float IMU_ALPHA = 0.98f; // complementary filter, same as the robot
|
|
constexpr int IMU_GYRO_BIAS_SAMPLES = 200; // at boot; hold the remote still
|
|
|
|
// Handset tilt -> CmdPacket.remote_roll/pitch. This many degrees of tilt away
|
|
// from the zeroed "level" pose = full scale (1000); the robot then scales
|
|
// full scale to its own MAX_TILT_DEG.
|
|
constexpr float REMOTE_TILT_FULL_SCALE_DEG = 30.0f;
|
|
constexpr float REMOTE_TILT_DEADZONE_DEG = 2.0f; // ignore hand tremor around level
|
|
// Toggling "remote gyro" fades the tilt in/out at this rate (fraction/s)
|
|
// instead of stepping it. The robot rate-limits pose as well; this just
|
|
// keeps the flag change itself from being a hard edge.
|
|
constexpr float REMOTE_TILT_FADE_PER_SEC = 2.0f;
|
|
|
|
// ---- Operator setpoints ----
|
|
constexpr uint8_t SPEED_SCALE_DEFAULT = 128;
|
|
constexpr float SPEED_SCALE_RATE_PER_SEC = 255.0f; // slider jumps ramp over <= 1 s (robot doesn't smooth twist)
|
|
constexpr uint8_t SPEED_SCALE_STEP = 25; // serial console +/- step
|
|
// FLAG_WALK stays set this long after the left stick returns to center, so
|
|
// hovering at the deadzone edge doesn't chatter STAND <-> WALKING.
|
|
constexpr uint32_t WALK_LINGER_MS = 250;
|
|
// Arming is refused unless every stick axis is within this of center
|
|
// (post-deadzone, -1000..1000 scale), so the robot never arms mid-command.
|
|
constexpr int16_t ARM_CENTER_TOLERANCE = 50;
|
|
|
|
// ---- Nextion touchscreen on Serial2 (RX2 = pin 7, TX2 = pin 8) ----
|
|
#define NEXTION_SERIAL Serial2
|
|
// BENCH: Nextion ships at 9600. The HMI's Program.s must set `bauds=115200`
|
|
// (see remote/NEXTION.md) or change this to match.
|
|
constexpr uint32_t NEXTION_BAUD = 115200;
|
|
constexpr uint32_t NEXTION_FULL_REFRESH_MS = 2000; // re-push everything periodically (self-heals after a display reset)
|
|
constexpr uint32_t NEXTION_LINK_REFRESH_MS = 250; // link-quality text update rate
|
|
constexpr uint32_t STATUS_MESSAGE_MS = 2500; // how long transient status messages stay up
|