2026-09-29 00:37:12 +02:00
|
|
|
#pragma once
|
|
|
|
|
// C API of hexapod_sim.dll: the unchanged robot firmware running against
|
|
|
|
|
// simulated hardware. Consumed by Unity (Assets/HexapodSim/HexapodSimNative.cs)
|
|
|
|
|
// and by the headless test (tests/sim_smoke.cpp).
|
|
|
|
|
//
|
|
|
|
|
// Conventions: robot body frame, mm and degrees - +X forward, +Y left, +Z up
|
|
|
|
|
// (same as shared/HexapodProtocol). Per-leg arrays are [leg][joint or xyz]
|
|
|
|
|
// flattened, leg order as in robot/Config.h, joints coxa/femur/tibia.
|
|
|
|
|
//
|
|
|
|
|
// One robot per loaded DLL: its globals are the firmware's globals. To reset
|
|
|
|
|
// the robot, unload and reload the DLL (Unity does this on every Play).
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
#define HXSIM_API extern "C" __declspec(dllexport)
|
|
|
|
|
#else
|
|
|
|
|
#define HXSIM_API extern "C" __attribute__((visibility("default")))
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Bump whenever a signature or struct below changes; the C# side checks it.
|
2026-10-01 15:53:55 +02:00
|
|
|
#define HXSIM_API_VERSION 6
|
2026-09-29 00:37:12 +02:00
|
|
|
|
|
|
|
|
#define HXSIM_NUM_LEGS 6
|
|
|
|
|
|
|
|
|
|
// All fields 4 bytes wide so C# can mirror this with LayoutKind.Sequential.
|
|
|
|
|
typedef struct {
|
|
|
|
|
int32_t num_legs;
|
|
|
|
|
float mount_x_mm[HXSIM_NUM_LEGS];
|
|
|
|
|
float mount_y_mm[HXSIM_NUM_LEGS];
|
|
|
|
|
float mount_angle_deg[HXSIM_NUM_LEGS];
|
|
|
|
|
float coxa_mm, femur_mm, tibia_mm;
|
|
|
|
|
float stance_radius_mm, stance_height_mm;
|
|
|
|
|
float coxa_min_deg, coxa_max_deg;
|
|
|
|
|
float femur_min_deg, femur_max_deg;
|
|
|
|
|
float tibia_min_deg, tibia_max_deg;
|
|
|
|
|
float control_hz;
|
|
|
|
|
float leg_clearance_mm; // LEG_CLEARANCE_MM: minimum gap between neighbouring legs' segments
|
|
|
|
|
float stance_height_range_mm; // CmdPacket.body_height +-1000 moves the height by this much
|
|
|
|
|
} HxGeometry;
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
int32_t robot_state; // 0 DISARMED, 1 SIT, 2 STAND, 3 WALKING
|
|
|
|
|
float sit_stand_blend; // 0 sit .. 1 stand
|
|
|
|
|
int32_t link_ok; // Comms::linkOk()
|
|
|
|
|
float heading_deg; // robot.ino's open-loop heading estimate
|
|
|
|
|
float imu_roll_deg; // RobotIMU filter output
|
|
|
|
|
float imu_pitch_deg;
|
|
|
|
|
int32_t imu_enabled; // robot-gyro toggle as the robot sees it
|
|
|
|
|
uint32_t sim_time_ms;
|
|
|
|
|
uint32_t control_ticks; // loop() passes that ran the control step
|
|
|
|
|
int32_t stance_mask; // bit i = leg i in stance per the gait engine
|
|
|
|
|
float pose_reach_scale; // 1 = full commanded body pose; less = scaled down to what the legs can reach
|
|
|
|
|
float twist_scale; // 1 = walk command as sent; less = slowed to a stride the legs can take
|
|
|
|
|
float stance_height_mm; // current (rate-limited) standing height: feet below hips
|
|
|
|
|
} HxStatus;
|
|
|
|
|
|
|
|
|
|
HXSIM_API int32_t hxsim_api_version(void);
|
|
|
|
|
HXSIM_API int32_t hxsim_packet_size(void); // sizeof(CmdPacket) as compiled
|
|
|
|
|
HXSIM_API void hxsim_get_geometry(HxGeometry* out);
|
|
|
|
|
|
|
|
|
|
// Hardware present at power-on. Call before hxsim_start().
|
|
|
|
|
HXSIM_API void hxsim_set_radio_present(int32_t present);
|
|
|
|
|
HXSIM_API void hxsim_set_imu_present(int32_t present);
|
|
|
|
|
|
|
|
|
|
HXSIM_API void hxsim_start(void); // runs setup() once
|
|
|
|
|
// Advances simulated time by dt_us, calling loop() every HXSIM_LOOP_PERIOD_US
|
|
|
|
|
// like the real busy loop (loop() itself holds the CONTROL_HZ rate).
|
|
|
|
|
HXSIM_API void hxsim_step(uint32_t dt_us);
|
|
|
|
|
|
|
|
|
|
// Inputs
|
|
|
|
|
HXSIM_API void hxsim_radio_send(const uint8_t* bytes, int32_t len); // one packet on air
|
|
|
|
|
HXSIM_API void hxsim_set_ground_tilt(float roll_deg, float pitch_deg); // tilt the world (robot-gyro tests)
|
|
|
|
|
// The MPU6050 is normally fed from the kinematic body model (SimBody.cpp).
|
|
|
|
|
// Calling this overrides it with fixed readings from then on.
|
|
|
|
|
HXSIM_API void hxsim_set_imu(float ax_g, float ay_g, float az_g,
|
|
|
|
|
float gx_dps, float gy_dps, float gz_dps);
|
|
|
|
|
HXSIM_API void hxsim_serial_input(const char* bytes, int32_t len); // TEST_SERIAL_INPUT keys
|
|
|
|
|
|
|
|
|
|
// Outputs
|
|
|
|
|
HXSIM_API int32_t hxsim_serial_output(char* out, int32_t cap); // drains robot Serial prints
|
|
|
|
|
HXSIM_API void hxsim_get_servos(int32_t* us_18, int32_t* attached_18, uint32_t* writes_18);
|
2026-10-01 15:53:55 +02:00
|
|
|
HXSIM_API void hxsim_get_joints_deg(float* deg_18); // servo pulses decoded through Config.h's per-servo SERVOS table
|
2026-09-29 00:37:12 +02:00
|
|
|
HXSIM_API void hxsim_get_feet_mm(float* xyz_18); // forward kinematics of those angles, body frame
|
|
|
|
|
HXSIM_API void hxsim_get_status(HxStatus* out);
|
|
|
|
|
// Kinematic body (SimBody.cpp): the body rests on the support plane of its
|
|
|
|
|
// feet and grounded feet don't slide. World frame = robot frame at power-on,
|
|
|
|
|
// ground at z = 0. Any pointer may be null.
|
|
|
|
|
HXSIM_API void hxsim_get_body(float* pos_mm_3, float* quat_wxyz_4, float* yaw_deg, int32_t* contact_mask);
|