Files
hexapod-robot/2026-current/remote/Config.h
T
mangavalkandClaude Opus 5.5 935526eab5 Firmware simulation, Unity project, and sim-verified robot fixes
Simulation
- sim/: the unchanged robot firmware compiled into hexapod_sim.dll
  against PC stand-ins (Arduino, Servo, Wire + register-level MPU6050,
  RF24 with matching-config radio), a kinematic body model (support
  plane + planted-foot odometry) that also drives the simulated IMU,
  and a headless smoke test (sim_smoke). build.ps1 builds and tests.
- Hexapod_Simulation_Unity/ folded in (history kept in a bundle
  outside the repo). Assets/HexapodSim loads the DLL, drives the
  existing leg rig, and offers joystick / keyboard / robot-serial
  input plus a test panel.

Robot (all found or confirmed in the simulation)
- Config.h geometry taken from the Unity model.
- IK femur elevation sign, gait stance direction, body pose
  rotation direction, yaw lever arm.
- Stance feet integrated from the current command; gait restarts at
  mid-stance; heading integrates the applied turn rate, only while
  walking.
- Leg clearance and reach checks (Limits::feetFeasible); walk command
  and body pose scaled as a whole, pose scale rate-limited, planted
  feet guarded so the body never snaps or legs collide.
- Robot-gyro self-leveling as an integrator.
- Heading mode: steering relative to the body; S walks backward.
- Standing height from the remote (STANCE_HEIGHT_RANGE_MM).

Protocol v3 (reflash both boards)
- CmdPacket gains body_height (22 bytes).

Remote
- Height slider (Nextion hHeight/nHeight, serial [ ]).
- Fixed unspecified I2C read order in RemoteIMU.

LEGACY/: owner's old Nextion HMI and remote hardware assets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 00:37:12 +02:00

114 lines
6.4 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
// Standing height slider -> CmdPacket.body_height (-1000..1000, 0 = the
// robot's default). No smoothing here: the robot rate-limits height itself.
constexpr int16_t HEIGHT_STEP = 100; // 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