# Firmware simulation (software-in-the-loop) This folder compiles the **unchanged** robot firmware (`robot/*.cpp` plus `robot.ino`) into a Windows DLL. The Unity project (`Hexapod_Simulation_Unity/`) runs that DLL, so the code you test in the simulation is exactly the code you flash to the Teensy. `robot/` has no `#ifdef SIM` anywhere. ``` robot/ (unchanged) ──► PlatformIO + Teensy core ──► Teensy 4.0 firmware └─► CMake + g++ + sim/shim/ ──► sim/build/hexapod_sim.dll ──► Unity ``` ## How it works `shim/` provides PC stand-ins for the only hardware the firmware touches: | Firmware uses | Simulated as | |---|---| | `Servo::writeMicroseconds()` | the pulse stored per pin, decoded back to angles with `Config.h`'s `SERVO_US_*` | | `Wire` + MPU6050 registers | a register-level fake MPU6050 at 0x68, fed from the simulated body's tilt and rotation | | `RF24` | a simulated radio: packets are received only if channel, address, data rate and **payload size** match, and the RX FIFO holds 3 | | `millis()` / `micros()` / `Serial` | a simulated clock that Unity advances, and captured serial I/O | `robot_ino.cpp` compiles `robot.ino` itself and exposes read-only views of its globals (state machine, heading, IMU filter, gait phase) for status display. `HexapodSimApi.h` is the C API. Its forward kinematics is written separately from `robot/Kinematics.cpp` on purpose, so it can catch IK mistakes. ## Build Needs a 64-bit g++ and CMake. Strawberry Perl's `C:\Strawberry\c\bin` has g++, cmake and ninja, and the script falls back to it automatically. From the repo root in PowerShell: ``` .\sim\build.ps1 # configure (first time) + build + smoke test .\sim\build.ps1 -NoTest # build only .\sim\build.ps1 -Clean # rebuild from scratch ``` To run the steps by hand, use separate lines. Windows PowerShell 5.1 has no `&&`: ``` cmake -S sim -B sim\build -G Ninja cmake --build sim\build .\sim\build\sim_smoke.exe ``` Rebuild after any change in `robot/` or `shared/`. In Unity, press **Play** (or **Reload firmware** in the panel) to pick up the new DLL. Unity copies the DLL before loading it, so it never locks the build output and you don't need to restart the editor. ## Headless smoke test (`tests/sim_smoke.cpp`) It drives the firmware the way the remote would and checks: - disarmed at power-on, - arm lands in SIT, - stand reaches STAND, - **IK gives valid solutions and the feet end up at `STANCE_HEIGHT`**, - `FLAG_WALK` leads to WALKING, - **`+vx` moves the body forward**, - link loss disarms, - a wrong-size packet is ignored, - the serial harness still works. ## Using it in Unity Open `Hexapod_Simulation_Unity` and press Play in `Main.unity`. No scene edits are needed: `HexapodSimBootstrap` adds a `HexapodSimulation` object, disables the old movement scripts (`Walk`, `RotationControl`, `Leg`, `HandleHoystick`), and binds the existing leg rig by name (`Leg` → `hip1` / `Hip2` / `Knie`). The on-screen panel (F1 toggles it) offers three input sources: - **Joystick:** the real remote as a USB controller, on input axes `"1"`–`"6"` like the old `HandleHoystick`. Axis assignment and inversion are set on the `SimRemote` component. The panel shows the raw axes so you can fix them. - **Keyboard:** test without a remote. WASD walks, Q/E turns, IJKL + U/O drive the right stick, 1–7 flip the toggles, Backspace disarms, +/- change speed. - **Robot serial:** no radio at all. Keystrokes go to `robot.ino`'s own `TEST_SERIAL_INPUT` harness. The panel also has every remote toggle, the speed slider, simulated handset tilt (remote gyro), a **ground tilt** slider (to test the robot gyro), and live firmware state, joint angles, foot contact, the packet on air and robot serial output. The body moves **kinematically**, computed in the DLL (`SimBody.cpp`), so Unity shows what the smoke test checks: - The body rests on the support plane of its feet, the plane all feet are on or above. - Grounded feet don't slide. - The simulated MPU6050 reads that body's tilt and rotation rate. The sphere at each foot (`Point`) is placed by the DLL's forward kinematics. If it doesn't sit on the tibia tip, the rig and `Config.h` disagree. Setup that lives in the Inspector on `HexapodSimulation`: - `forwardAxis`: robot forward = Unity −X by default, taken from the old scene's `invertForLeftLeg` naming. - `unitsPerMm`: 0.1, since the model is in cm. - `kneeRestDeg`: −90, the rig's knee pose at rotation 0. For a custom setup instead of the bootstrap, add a `HexapodSimulation` component to the scene yourself. ## What is not simulated - Physics (mass, falling, slipping, servo torque and speed). - Servo calibration. The sim decodes pulses with `Config.h`'s single `SERVO_US_AT_0DEG` / `SERVO_US_PER_DEG`, i.e. what the firmware *intends*, not per-servo offsets or directions. - The remote firmware. `SimRemote.cs` mirrors its packet rules instead.