2026-09-29 00:37:12 +02:00
# 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.
```
2026-10-01 15:53:55 +02:00
robot/ (unchanged) ──► PlatformIO + Teensy core ──► Teensy 3.6 firmware
2026-09-29 00:37:12 +02:00
└─► 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 |
|---|---|
2026-10-01 15:53:55 +02:00
| `analogWrite()` hardware PWM (how `ServoDriver` drives the servos) | the pulse width per pin, worked out from duty, resolution and frequency, then decoded back to angles through `Config.h` 's per-servo `SERVOS` table |
2026-09-29 00:37:12 +02:00
| `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).
2026-10-01 15:53:55 +02:00
- Whether the calibration matches the real servos. The sim decodes pulses
through the same `SERVOS` table the firmware uses, so it shows what the
firmware *intends* , including pulse-limit clamping (which is how it
caught the old ±90° coxa offsets). Whether a real servo turns that way is
a bench check.
2026-09-29 00:37:12 +02:00
- The remote firmware. `SimRemote.cs` mirrors its packet rules instead.