From c192e0da213ea2057d52039d4cdae9cacd4a8d62 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Wed, 14 Jan 2026 16:22:16 -0600 Subject: [PATCH 01/67] docs: Document CMAKE_BUILD_TYPE=Release for production builds Add documentation about using CMAKE_BUILD_TYPE=Release to save disk space when building firmware for production/CI/releases. Key points: - Default RelWithDebInfo mode uses ~109 GB for all targets - Release mode uses ~4-6 GB (96% reduction) - Debug symbols are stripped from final .hex files anyway - Final binaries are identical in both modes Updated files: - docs/development/Building in Linux.md - Add brief note about debug symbols in single target section - Add new "Building All Targets or Release Builds" section with disk usage comparison and Release mode instructions - docs/development/release-create.md - Add "Building Firmware Locally" section with Release mode instructions and disk usage information Also fixes typo: "Bulding" -> "Building" --- docs/development/Building in Linux.md | 29 +++++++++++++++++++++++++-- docs/development/release-create.md | 19 ++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/docs/development/Building in Linux.md b/docs/development/Building in Linux.md index 944872d530c..d85af2f2d5c 100644 --- a/docs/development/Building in Linux.md +++ b/docs/development/Building in Linux.md @@ -92,7 +92,7 @@ cmake -DCOMPILER_VERSION_CHECK=OFF .. `cmake` will generate a number of files in your `build` directory, including a cache of generated build settings `CMakeCache.txt` and a `Makefile`. -## Bulding the firmware +## Building the firmware Once `cmake` has generated the `build/Makefile`, this `Makfile` (with `make`) is used to build the firmware, again from the `build` directory. It is not necessary to re-run `cmake` unless the INAV cmake configuration is changed (i.e. a new release) or you wish to swap between the ARM SDK compiler and a distro or other external compiler. @@ -101,7 +101,7 @@ The generated `Makefile` uses different a target selection mechanism from the ol Typically, to build a single target, just pass the target name to `make`; note that unlike earlier releases, `make` without a target specified will build **all** targets. ``` -# Build the MATEKF405 firmware +# Build the MATEKF405 firmware (includes debug symbols by default) make MATEKF405 ``` @@ -112,6 +112,31 @@ One can also build multiple targets from a single `make` command: make -j $(($(nproc)-1)) MATEKF405 MATEKF722 ``` +### Building All Targets or Release Builds + +**⚠️ Important for CI or building many targets:** By default, INAV builds with `-DCMAKE_BUILD_TYPE=RelWithDebInfo`, which includes maximum debug symbols. When building all ~100 targets, this uses **~109 GB of disk space**. The debug symbols are automatically stripped from the final `.hex` files, so they provide no benefit for production builds. + +**For production builds, CI, or when building many targets, use Release mode:** + +```bash +cd inav +mkdir build-release +cd build-release +cmake -DCMAKE_BUILD_TYPE=Release .. + +# Build specific targets +make MATEKF405 + +# Or build all official release targets +make release +``` + +**Disk usage comparison:** +- `RelWithDebInfo` (default): ~109 GB for all targets +- `Release`: ~4-6 GB for all targets (96% reduction) + +The final `.hex` and `.bin` files are identical in both cases. + The resultant hex file are in the `build` directory. You can then use the INAV Configurator to flash the local `build/inav_x.y.z_TARGET.hex` file, or use `stm32flash` or `dfu-util` directly from the command line. diff --git a/docs/development/release-create.md b/docs/development/release-create.md index ec362da0911..29639cee9d6 100644 --- a/docs/development/release-create.md +++ b/docs/development/release-create.md @@ -208,6 +208,25 @@ gh release list --repo iNavFlight/inav-nightly --limit 5 gh release download --repo iNavFlight/inav-nightly --pattern "*.hex" ``` +#### Building Firmware Locally (if needed) + +**⚠️ Important:** Always use Release mode when building firmware for releases to save disk space: + +```bash +cd inav +mkdir build-release +cd build-release +cmake -DCMAKE_BUILD_TYPE=Release .. + +# Build all official release targets +make release + +# Or build specific targets +make MATEKF405 MATEKF722 +``` + +**Disk usage:** Release mode uses ~4-6 GB vs ~109 GB for default RelWithDebInfo mode (96% reduction). The debug symbols are stripped from final `.hex` files anyway, so Release mode produces identical output. + #### Renaming Firmware Files Remove CI suffix and add RC number for RC releases: From 311acd1d8fc82ee3cf98214e65b50e27020fd989 Mon Sep 17 00:00:00 2001 From: Scavanger Date: Mon, 12 Jan 2026 09:38:13 -0300 Subject: [PATCH 02/67] XPlane: HITL -> SIM V3, SITL --- docs/SITL/SITL.md | 2 +- docs/SITL/X-Plane.md | 3 + ...e In The Loop (HITL) plugin for X-Plane.md | 6 + src/main/fc/fc_msp.c | 395 +++++---- src/main/fc/runtime_config.c | 4 +- src/main/fc/runtime_config.h | 16 +- src/main/io/displayport_msp_osd.c | 2 + .../navigation/navigation_pos_estimator.c | 2 +- src/main/rx/sim.c | 12 +- src/main/rx/sim.h | 3 +- src/main/sensors/battery.c | 6 + src/main/target/SITL/sim/xplane.c | 763 ++++++++++++------ src/main/target/SITL/sim/xplane.h | 1 + src/main/target/SITL/target.h | 2 - src/main/target/common.h | 2 + 15 files changed, 768 insertions(+), 451 deletions(-) diff --git a/docs/SITL/SITL.md b/docs/SITL/SITL.md index 96ed4c19f98..49868ce1e1c 100644 --- a/docs/SITL/SITL.md +++ b/docs/SITL/SITL.md @@ -18,7 +18,7 @@ INAV SITL communicates for sensor data and control directly with the correspondi AS SITL is still an inav software, but running on PC, it is possible to use HITL interface for communication. -INAV-X-Plane-HITL plugin https://github.com/RomanLut/INAV-X-Plane-HITL can be used with SITL. +[INAV-X-Plane-HITL](https://github.com/RomanLut/INAV-X-Plane-HITL) or [INAV-X-Plane-XITL](https://github.com/Scavanger/INAV-X-Plane-XITL) plugin can be used with SITL. ## Sensors The following sensors are emulated: diff --git a/docs/SITL/X-Plane.md b/docs/SITL/X-Plane.md index 6a838c19f59..0b50be1bd5a 100644 --- a/docs/SITL/X-Plane.md +++ b/docs/SITL/X-Plane.md @@ -7,6 +7,9 @@ X-Plane is not a model flight simulator, but is based on real world data and is ## Aircraft It is recommended to use the "AR Wing" of the INAV HITL project: https://github.com/RomanLut/INAV-X-Plane-HITL +## INAV Plugin +For advanced SITL featues (like OSD, virtual RX, simulated hardware failures, power train simulation) you can use the [INAV-X-Plane-XITL plugin](https://github.com/Scavanger/INAV-X-Plane-XITL) + ## General settings In Settings / Network select "Accept incoming connections". The port can be found under "UDP PORTS", "Port we receive on". If no connection is established, the port can be changed. diff --git a/docs/development/Hardware In The Loop (HITL) plugin for X-Plane.md b/docs/development/Hardware In The Loop (HITL) plugin for X-Plane.md index c166c5117ac..5a97b1b1a14 100644 --- a/docs/development/Hardware In The Loop (HITL) plugin for X-Plane.md +++ b/docs/development/Hardware In The Loop (HITL) plugin for X-Plane.md @@ -8,4 +8,10 @@ https://github.com/RomanLut/INAV-X-Plane-HITL +or for latest HITL features (INAV >= 9.0) + + **INAV-X-Plane-XITL** + + https://github.com/Scavanger/INAV-X-Plane-XITL + HITL technique can be used to test features during development. Please check page above for installation instructions. diff --git a/src/main/fc/fc_msp.c b/src/main/fc/fc_msp.c index 3e81b028de2..dacf047fcf4 100644 --- a/src/main/fc/fc_msp.c +++ b/src/main/fc/fc_msp.c @@ -106,6 +106,7 @@ #include "msp/msp.h" #include "msp/msp_protocol.h" #include "msp/msp_serial.h" +#include "io/rangefinder.h" #include "navigation/navigation.h" #include "navigation/navigation_private.h" //for MSP_SIMULATOR @@ -115,6 +116,7 @@ #include "rx/msp.h" #include "rx/srxl2.h" #include "rx/crsf.h" +#include "rx/sim.h" #include "scheduler/scheduler.h" @@ -1628,7 +1630,7 @@ static bool mspFcProcessOutCommand(uint16_t cmdMSP, sbuf_t *dst, mspPostProcessF case MSP2_INAV_OUTPUT_MAPPING_EXT: for (uint8_t i = 0; i < timerHardwareCount; ++i) if (!(timerHardware[i].usageFlags & (TIM_USE_PPM | TIM_USE_PWM))) { - #if defined(SITL_BUILD) + #if defined(SITL_BUILD) || defined(WASM_BUILD) sbufWriteU8(dst, i); #else sbufWriteU8(dst, timer2id(timerHardware[i].tim)); @@ -1639,19 +1641,19 @@ static bool mspFcProcessOutCommand(uint16_t cmdMSP, sbuf_t *dst, mspPostProcessF break; case MSP2_INAV_OUTPUT_MAPPING_EXT2: { - #if !defined(SITL_BUILD) && defined(WS2811_PIN) + #if !(defined(SITL_BUILD) || defined(WASM_BUILD)) && defined(WS2811_PIN) ioTag_t led_tag = IO_TAG(WS2811_PIN); #endif for (uint8_t i = 0; i < timerHardwareCount; ++i) if (!(timerHardware[i].usageFlags & (TIM_USE_PPM | TIM_USE_PWM))) { - #if defined(SITL_BUILD) + #if defined(SITL_BUILD) || defined(WASM_BUILD) sbufWriteU8(dst, i); #else sbufWriteU8(dst, timer2id(timerHardware[i].tim)); #endif sbufWriteU32(dst, timerHardware[i].usageFlags); - #if defined(SITL_BUILD) || !defined(WS2811_PIN) + #if defined(SITL_BUILD) || defined(WASM_BUILD) || !defined(WS2811_PIN) sbufWriteU8(dst, 0); #else // Extra label to help identify repurposed PINs. @@ -3897,6 +3899,7 @@ static bool mspParameterGroupsCommand(sbuf_t *dst, sbuf_t *src) } #ifdef USE_SIMULATOR + bool isOSDTypeSupportedBySimulator(void) { #ifdef USE_OSD @@ -4042,11 +4045,219 @@ void mspWriteSimulatorOSD(sbuf_t *dst) sbufWriteU8(dst, 0); } } + +static void readMspSimulatorValues(sbuf_t *src, const int dataSize, const uint8_t simMspVersion) +{ + if (!ARMING_FLAG(SIMULATOR_MODE_HITL)) { // Just once +#ifdef USE_BARO + if ( requestedSensors[SENSOR_INDEX_BARO] != BARO_NONE ) { + sensorsSet(SENSOR_BARO); + setTaskEnabled(TASK_BARO, true); + DISABLE_ARMING_FLAG(ARMING_DISABLED_HARDWARE_FAILURE); + baroStartCalibration(); + } #endif +#ifdef USE_MAG + if (compassConfig()->mag_hardware != MAG_NONE) { + sensorsSet(SENSOR_MAG); + ENABLE_STATE(COMPASS_CALIBRATED); + DISABLE_ARMING_FLAG(ARMING_DISABLED_HARDWARE_FAILURE); + mag.magADC[X] = 800; + mag.magADC[Y] = 0; + mag.magADC[Z] = 0; + } +#endif + ENABLE_ARMING_FLAG(SIMULATOR_MODE_HITL); + ENABLE_STATE(ACCELEROMETER_CALIBRATED); + LOG_DEBUG(SYSTEM, "Simulator enabled"); + } + + if (dataSize < 14) { + DISABLE_STATE(GPS_FIX); + return; + } + + if (feature(FEATURE_GPS) && SIMULATOR_HAS_OPTION(HITL_HAS_NEW_GPS_DATA)) { + gpsSolDRV.fixType = sbufReadU8(src); + gpsSolDRV.hdop = gpsSolDRV.fixType == GPS_NO_FIX ? 9999 : 100; + gpsSolDRV.numSat = sbufReadU8(src); + + if (gpsSolDRV.fixType != GPS_NO_FIX) { + gpsSolDRV.flags.validVelNE = true; + gpsSolDRV.flags.validVelD = true; + gpsSolDRV.flags.validEPE = true; + gpsSolDRV.flags.validTime = false; + + gpsSolDRV.llh.lat = sbufReadU32(src); + gpsSolDRV.llh.lon = sbufReadU32(src); + gpsSolDRV.llh.alt = sbufReadU32(src); + gpsSolDRV.groundSpeed = (int16_t)sbufReadU16(src); + gpsSolDRV.groundCourse = (int16_t)sbufReadU16(src); + + gpsSolDRV.velNED[X] = (int16_t)sbufReadU16(src); + gpsSolDRV.velNED[Y] = (int16_t)sbufReadU16(src); + gpsSolDRV.velNED[Z] = (int16_t)sbufReadU16(src); + + gpsSolDRV.eph = 100; + gpsSolDRV.epv = 100; + } else { + sbufAdvance(src, sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) * 3); + } + // Feed data to navigation + gpsProcessNewDriverData(); + gpsProcessNewSolutionData(false); + } else { + sbufAdvance(src, sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) * 3); + } + + if (!SIMULATOR_HAS_OPTION(HITL_USE_IMU)) { + attitude.values.roll = (int16_t)sbufReadU16(src); + attitude.values.pitch = (int16_t)sbufReadU16(src); + attitude.values.yaw = (int16_t)sbufReadU16(src); + } else { + sbufAdvance(src, sizeof(uint16_t) * XYZ_AXIS_COUNT); + } + + // Get the acceleration in 1G units + acc.accADCf[X] = ((int16_t)sbufReadU16(src)) / 1000.0f; + acc.accADCf[Y] = ((int16_t)sbufReadU16(src)) / 1000.0f; + acc.accADCf[Z] = ((int16_t)sbufReadU16(src)) / 1000.0f; + acc.accVibeSq[X] = 0.0f; + acc.accVibeSq[Y] = 0.0f; + acc.accVibeSq[Z] = 0.0f; + + // Get the angular velocity in DPS + gyro.gyroADCf[X] = ((int16_t)sbufReadU16(src)) / 16.0f; + gyro.gyroADCf[Y] = ((int16_t)sbufReadU16(src)) / 16.0f; + gyro.gyroADCf[Z] = ((int16_t)sbufReadU16(src)) / 16.0f; + + if (sensors(SENSOR_BARO)) { + baro.baroPressure = (int32_t)sbufReadU32(src); + baro.baroTemperature = DEGREES_TO_CENTIDEGREES(SIMULATOR_BARO_TEMP); + } else { + sbufAdvance(src, sizeof(uint32_t)); + } + + if (sensors(SENSOR_MAG)) { + mag.magADC[X] = ((int16_t)sbufReadU16(src)) / 20; // 16000 / 20 = 800uT + mag.magADC[Y] = ((int16_t)sbufReadU16(src)) / 20; //note that mag failure is simulated by setting all readings to zero + mag.magADC[Z] = ((int16_t)sbufReadU16(src)) / 20; + } else { + sbufAdvance(src, sizeof(uint16_t) * XYZ_AXIS_COUNT); + } + + if (SIMULATOR_HAS_OPTION(HITL_EXT_BATTERY_VOLTAGE)) { + simulatorData.vbat = sbufReadU8(src); + } else { + simulatorData.vbat = SIMULATOR_FULL_BATTERY; + } + + if (SIMULATOR_HAS_OPTION(HITL_AIRSPEED)) { + simulatorData.airSpeed = sbufReadU16(src); + } else if (SIMULATOR_HAS_OPTION(HITL_EXTENDED_FLAGS)) { + sbufReadU16(src); + } + + if (simMspVersion == SIMULATOR_MSP_VERSION_3) { + + if (SIMULATOR_HAS_OPTION(HITL_RANGEFINDER)) { + simulatorData.rangefinder = sbufReadU16(src); + if (simulatorData.rangefinder == 0xFFFF) { + fakeRangefindersSetData(-1); + } else { + fakeRangefindersSetData(simulatorData.rangefinder); + } + + } else { + sbufReadU16(src); + } + + if (SIMULATOR_HAS_OPTION(HITL_CURRENT_SENSOR)) { + simulatorData.current = sbufReadU16(src); + } else { + sbufReadU16(src); + } + + if (SIMULATOR_HAS_OPTION(HITL_SIM_RC_INPUT)) { + for (int i = 0; i < HITL_SIM_MAX_RC_INPUTS; i++) { + simulatorData.rcInput[i] = sbufReadU16(src); + } + rxSimSetChannelValue(simulatorData.rcInput, HITL_SIM_MAX_RC_INPUTS); + simulatorData.rssi = sbufReadU16(src); + rxSimSetRssi(simulatorData.rssi); + } else { + sbufAdvance(src, sizeof(uint16_t) * HITL_SIM_MAX_RC_INPUTS + sizeof(uint16_t)); // + RSSI + } + + rxSimSetFailsafe(SIMULATOR_HAS_OPTION(HITL_FAILSAFE_TRIGGERED)); + } + + // Backward compatibility for HITL Plugin 1.X + if (simMspVersion == SIMULATOR_MSP_VERSION_2 && SIMULATOR_HAS_OPTION(HITL_EXTENDED_FLAGS)) { + simulatorData.flags |= ((uint16_t)sbufReadU8(src)) << 8; + } +} + +static mspResult_e mspProcessSimulatorCommand(sbuf_t *dst, sbuf_t *src, const int dataSize) +{ + const uint8_t simMspVersion = sbufReadU8(src); // Get the Simulator MSP version + // Check the MSP version of simulator + if (simMspVersion != SIMULATOR_MSP_VERSION_2 && simMspVersion != SIMULATOR_MSP_VERSION_3) { + return MSP_RESULT_ERROR; + } + + // Backward compatibility for HITL Plugin 1.X + simulatorData.flags = sbufReadU8(src); + + if (simMspVersion == SIMULATOR_MSP_VERSION_3) { + simulatorData.flags |= ((uint16_t)sbufReadU8(src)) << 8; + } + + // In case of SITL mode, we do not read any input from the simulator (done vis DREFs) + if (!SIMULATOR_HAS_OPTION(HITL_SITL_MODE)) { + // Check if simulator is disabled and was previously enabled (flags != 0) + if (!SIMULATOR_HAS_OPTION(HITL_ENABLE) && simulatorData.flags) { + fcReboot(false); + return MSP_RESULT_NO_REPLY; + } else { + readMspSimulatorValues(src, dataSize, simMspVersion); + } + } + + sbufWriteU16(dst, (uint16_t)simulatorData.input[INPUT_STABILIZED_ROLL]); + sbufWriteU16(dst, (uint16_t)simulatorData.input[INPUT_STABILIZED_PITCH]); + sbufWriteU16(dst, (uint16_t)simulatorData.input[INPUT_STABILIZED_YAW]); + sbufWriteU16(dst, (uint16_t)(ARMING_FLAG(ARMED) ? simulatorData.input[INPUT_STABILIZED_THROTTLE] : -500)); + + simulatorData.debugIndex++; + if (simulatorData.debugIndex == 8) { + simulatorData.debugIndex = 0; + } + + const uint8_t debugIndex = simulatorData.debugIndex | + ((mixerConfig()->platformType == PLATFORM_AIRPLANE) ? 128 : 0) | + (ARMING_FLAG(ARMED) ? 64 : 0) | + (!feature(FEATURE_OSD) ? 32: 0) | + (!isOSDTypeSupportedBySimulator() ? 16 : 0); + + sbufWriteU8(dst, debugIndex); + sbufWriteU32(dst, debug[simulatorData.debugIndex]); + + sbufWriteU16(dst, attitude.values.roll); + sbufWriteU16(dst, attitude.values.pitch); + sbufWriteU16(dst, attitude.values.yaw); + + mspWriteSimulatorOSD(dst); + + return MSP_RESULT_ACK; +} + +#endif + + bool mspFCProcessInOutCommand(uint16_t cmdMSP, sbuf_t *dst, sbuf_t *src, mspResult_e *ret) { - uint8_t tmp_u8; const unsigned int dataSize = sbufBytesRemaining(src); switch (cmdMSP) { @@ -4153,179 +4364,7 @@ bool mspFCProcessInOutCommand(uint16_t cmdMSP, sbuf_t *dst, sbuf_t *src, mspResu #endif #ifdef USE_SIMULATOR case MSP_SIMULATOR: - tmp_u8 = sbufReadU8(src); // Get the Simulator MSP version - - // Check the MSP version of simulator - if (tmp_u8 != SIMULATOR_MSP_VERSION) { - break; - } - - simulatorData.flags = sbufReadU8(src); - - if (!SIMULATOR_HAS_OPTION(HITL_ENABLE)) { - - if (ARMING_FLAG(SIMULATOR_MODE_HITL)) { // Just once - DISABLE_ARMING_FLAG(SIMULATOR_MODE_HITL); - -#ifdef USE_BARO - if ( requestedSensors[SENSOR_INDEX_BARO] != BARO_NONE ) { - baroStartCalibration(); - } -#endif -#ifdef USE_MAG - DISABLE_STATE(COMPASS_CALIBRATED); - compassInit(); -#endif - simulatorData.flags = HITL_RESET_FLAGS; - // Review: Many states were affected. Reboot? - - disarm(DISARM_SWITCH); // Disarm to prevent motor output!!! - } - } else { - if (!ARMING_FLAG(SIMULATOR_MODE_HITL)) { // Just once -#ifdef USE_BARO - if ( requestedSensors[SENSOR_INDEX_BARO] != BARO_NONE ) { - sensorsSet(SENSOR_BARO); - setTaskEnabled(TASK_BARO, true); - DISABLE_ARMING_FLAG(ARMING_DISABLED_HARDWARE_FAILURE); - baroStartCalibration(); - } -#endif - -#ifdef USE_MAG - if (compassConfig()->mag_hardware != MAG_NONE) { - sensorsSet(SENSOR_MAG); - ENABLE_STATE(COMPASS_CALIBRATED); - DISABLE_ARMING_FLAG(ARMING_DISABLED_HARDWARE_FAILURE); - mag.magADC[X] = 800; - mag.magADC[Y] = 0; - mag.magADC[Z] = 0; - } -#endif - ENABLE_ARMING_FLAG(SIMULATOR_MODE_HITL); - ENABLE_STATE(ACCELEROMETER_CALIBRATED); - LOG_DEBUG(SYSTEM, "Simulator enabled"); - } - - if (dataSize >= 14) { - - if (feature(FEATURE_GPS) && SIMULATOR_HAS_OPTION(HITL_HAS_NEW_GPS_DATA)) { - gpsSolDRV.fixType = sbufReadU8(src); - gpsSolDRV.hdop = gpsSolDRV.fixType == GPS_NO_FIX ? 9999 : 100; - gpsSolDRV.numSat = sbufReadU8(src); - - if (gpsSolDRV.fixType != GPS_NO_FIX) { - gpsSolDRV.flags.validVelNE = true; - gpsSolDRV.flags.validVelD = true; - gpsSolDRV.flags.validEPE = true; - gpsSolDRV.flags.validTime = false; - - gpsSolDRV.llh.lat = sbufReadU32(src); - gpsSolDRV.llh.lon = sbufReadU32(src); - gpsSolDRV.llh.alt = sbufReadU32(src); - gpsSolDRV.groundSpeed = (int16_t)sbufReadU16(src); - gpsSolDRV.groundCourse = (int16_t)sbufReadU16(src); - - gpsSolDRV.velNED[X] = (int16_t)sbufReadU16(src); - gpsSolDRV.velNED[Y] = (int16_t)sbufReadU16(src); - gpsSolDRV.velNED[Z] = (int16_t)sbufReadU16(src); - - gpsSolDRV.eph = 100; - gpsSolDRV.epv = 100; - } else { - sbufAdvance(src, sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) * 3); - } - // Feed data to navigation - gpsProcessNewDriverData(); - gpsProcessNewSolutionData(false); - } else { - sbufAdvance(src, sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) * 3); - } - - if (!SIMULATOR_HAS_OPTION(HITL_USE_IMU)) { - attitude.values.roll = (int16_t)sbufReadU16(src); - attitude.values.pitch = (int16_t)sbufReadU16(src); - attitude.values.yaw = (int16_t)sbufReadU16(src); - } else { - sbufAdvance(src, sizeof(uint16_t) * XYZ_AXIS_COUNT); - } - - // Get the acceleration in 1G units - acc.accADCf[X] = ((int16_t)sbufReadU16(src)) / 1000.0f; - acc.accADCf[Y] = ((int16_t)sbufReadU16(src)) / 1000.0f; - acc.accADCf[Z] = ((int16_t)sbufReadU16(src)) / 1000.0f; - acc.accVibeSq[X] = 0.0f; - acc.accVibeSq[Y] = 0.0f; - acc.accVibeSq[Z] = 0.0f; - - // Get the angular velocity in DPS - gyro.gyroADCf[X] = ((int16_t)sbufReadU16(src)) / 16.0f; - gyro.gyroADCf[Y] = ((int16_t)sbufReadU16(src)) / 16.0f; - gyro.gyroADCf[Z] = ((int16_t)sbufReadU16(src)) / 16.0f; - - if (sensors(SENSOR_BARO)) { - baro.baroPressure = (int32_t)sbufReadU32(src); - baro.baroTemperature = DEGREES_TO_CENTIDEGREES(SIMULATOR_BARO_TEMP); - } else { - sbufAdvance(src, sizeof(uint32_t)); - } - - if (sensors(SENSOR_MAG)) { - mag.magADC[X] = ((int16_t)sbufReadU16(src)) / 20; // 16000 / 20 = 800uT - mag.magADC[Y] = ((int16_t)sbufReadU16(src)) / 20; //note that mag failure is simulated by setting all readings to zero - mag.magADC[Z] = ((int16_t)sbufReadU16(src)) / 20; - } else { - sbufAdvance(src, sizeof(uint16_t) * XYZ_AXIS_COUNT); - } - - if (SIMULATOR_HAS_OPTION(HITL_EXT_BATTERY_VOLTAGE)) { - simulatorData.vbat = sbufReadU8(src); - } else { - simulatorData.vbat = SIMULATOR_FULL_BATTERY; - } - - if (SIMULATOR_HAS_OPTION(HITL_AIRSPEED)) { - simulatorData.airSpeed = sbufReadU16(src); - } else { - if (SIMULATOR_HAS_OPTION(HITL_EXTENDED_FLAGS)) { - sbufReadU16(src); - } - } - - if (SIMULATOR_HAS_OPTION(HITL_EXTENDED_FLAGS)) { - simulatorData.flags |= ((uint16_t)sbufReadU8(src)) << 8; - } - } else { - DISABLE_STATE(GPS_FIX); - } - } - - sbufWriteU16(dst, (uint16_t)simulatorData.input[INPUT_STABILIZED_ROLL]); - sbufWriteU16(dst, (uint16_t)simulatorData.input[INPUT_STABILIZED_PITCH]); - sbufWriteU16(dst, (uint16_t)simulatorData.input[INPUT_STABILIZED_YAW]); - sbufWriteU16(dst, (uint16_t)(ARMING_FLAG(ARMED) ? simulatorData.input[INPUT_STABILIZED_THROTTLE] : -500)); - - simulatorData.debugIndex++; - if (simulatorData.debugIndex == 8) { - simulatorData.debugIndex = 0; - } - - tmp_u8 = simulatorData.debugIndex | - ((mixerConfig()->platformType == PLATFORM_AIRPLANE) ? 128 : 0) | - (ARMING_FLAG(ARMED) ? 64 : 0) | - (!feature(FEATURE_OSD) ? 32: 0) | - (!isOSDTypeSupportedBySimulator() ? 16 : 0); - - sbufWriteU8(dst, tmp_u8); - sbufWriteU32(dst, debug[simulatorData.debugIndex]); - - sbufWriteU16(dst, attitude.values.roll); - sbufWriteU16(dst, attitude.values.pitch); - sbufWriteU16(dst, attitude.values.yaw); - - mspWriteSimulatorOSD(dst); - - *ret = MSP_RESULT_ACK; + *ret = mspProcessSimulatorCommand(dst, src, dataSize); break; #endif #ifndef SITL_BUILD diff --git a/src/main/fc/runtime_config.c b/src/main/fc/runtime_config.c index 892df87b3ac..325ce862ce6 100644 --- a/src/main/fc/runtime_config.c +++ b/src/main/fc/runtime_config.c @@ -167,6 +167,8 @@ flightModeForTelemetry_e getFlightModeForTelemetry(void) simulatorData_t simulatorData = { .flags = 0, .debugIndex = 0, - .vbat = 0 + .vbat = 0, + .current = 0, + .rangefinder = 0 }; #endif diff --git a/src/main/fc/runtime_config.h b/src/main/fc/runtime_config.h index 452256a6b64..153eae1be4a 100644 --- a/src/main/fc/runtime_config.h +++ b/src/main/fc/runtime_config.h @@ -178,7 +178,9 @@ flightModeForTelemetry_e getFlightModeForTelemetry(void); #ifdef USE_SIMULATOR -#define SIMULATOR_MSP_VERSION 2 // Simulator MSP version +#define SIMULATOR_MSP_VERSION_2 2 // Simulator MSP version +#define SIMULATOR_MSP_VERSION_3 3 +#define HITL_SIM_MAX_RC_INPUTS 8 #define SIMULATOR_BARO_TEMP 25 // °C #define SIMULATOR_FULL_BATTERY 126 // Volts*10 #define SIMULATOR_HAS_OPTION(flag) ((simulatorData.flags & flag) != 0) @@ -194,7 +196,12 @@ typedef enum { HITL_AIRSPEED = (1 << 6), HITL_EXTENDED_FLAGS = (1 << 7), // Extend MSP_SIMULATOR format 2 HITL_GPS_TIMEOUT = (1 << 8), - HITL_PITOT_FAILURE = (1 << 9) + HITL_PITOT_FAILURE = (1 << 9), + HITL_CURRENT_SENSOR = (1 << 10), + HITL_SIM_RC_INPUT = (1 << 11), // Simulate RC input from Joystick inputs in XPlane + HITL_RANGEFINDER = (1 << 12), // Simulate Rangefinder data + HITL_FAILSAFE_TRIGGERED = (1 << 13), // Simulate Failsafe triggered condition + HITL_SITL_MODE = (1 << 14), // For INAV XITL in Sitl mode (sends no emulated sensor data) } simulatorFlags_t; typedef struct { @@ -203,6 +210,11 @@ typedef struct { uint8_t vbat; // 126 -> 12.6V uint16_t airSpeed; // cm/s int16_t input[4]; + uint16_t rcInput[HITL_SIM_MAX_RC_INPUTS]; + uint16_t rssi; + uint16_t current; + uint16_t rangefinder; // cm + } simulatorData_t; extern simulatorData_t simulatorData; diff --git a/src/main/io/displayport_msp_osd.c b/src/main/io/displayport_msp_osd.c index 9dc99889bc1..9188bee74b0 100644 --- a/src/main/io/displayport_msp_osd.c +++ b/src/main/io/displayport_msp_osd.c @@ -273,6 +273,8 @@ static int writeString(displayPort_t *displayPort, uint8_t col, uint8_t row, con */ static int drawScreen(displayPort_t *displayPort) // 250Hz { + vtxActive = SIMULATOR_HAS_OPTION(HITL_SITL_MODE); + static uint8_t counter = 0; if ((!cmsInMenu && IS_RC_MODE_ACTIVE(BOXOSD)) || (counter++ % DRAW_FREQ_DENOM)) { // 62.5Hz diff --git a/src/main/navigation/navigation_pos_estimator.c b/src/main/navigation/navigation_pos_estimator.c index cd861dacd6c..ee3fc28c47b 100644 --- a/src/main/navigation/navigation_pos_estimator.c +++ b/src/main/navigation/navigation_pos_estimator.c @@ -231,7 +231,7 @@ void onNewGPSData(void) /* Use VELNED provided by GPS if available, calculate from coordinates otherwise */ float gpsScaleLonDown = constrainf(cos_approx((ABS(gpsSol.llh.lat) / 10000000.0f) * 0.0174532925f), 0.01f, 1.0f); - if (!ARMING_FLAG(SIMULATOR_MODE_SITL) && gpsSol.flags.validVelNE) { + if (!(ARMING_FLAG(SIMULATOR_MODE_SITL) || ARMING_FLAG(SIMULATOR_MODE_HITL)) && gpsSol.flags.validVelNE) { posEstimator.gps.vel.x = gpsSol.velNED[X]; posEstimator.gps.vel.y = gpsSol.velNED[Y]; } diff --git a/src/main/rx/sim.c b/src/main/rx/sim.c index 62d9a9b864c..c2747d81fc9 100644 --- a/src/main/rx/sim.c +++ b/src/main/rx/sim.c @@ -32,6 +32,7 @@ static uint16_t channels[MAX_SUPPORTED_RC_CHANNEL_COUNT]; static bool hasNewData = false; static uint16_t rssi = 0; +static bool isFailsafe = false; static uint16_t rxSimReadRawRC(const rxRuntimeConfig_t *rxRuntimeConfigPtr, uint8_t chan) { UNUSED(rxRuntimeConfigPtr); @@ -56,6 +57,11 @@ static uint8_t rxSimFrameStatus(rxRuntimeConfig_t *rxRuntimeConfig) { } hasNewData = false; + + if (isFailsafe) { + return RX_FRAME_COMPLETE | RX_FRAME_FAILSAFE; + } + return RX_FRAME_COMPLETE; } @@ -68,7 +74,11 @@ void rxSimInit(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig) { rxRuntimeConfig->rcFrameStatusFn = rxSimFrameStatus; } -void rxSimSetRssi(uint16_t value) { +void rxSimSetRssi(const uint16_t value) { rssi = value; } + +void rxSimSetFailsafe(const bool value) { + isFailsafe = value; +} #endif diff --git a/src/main/rx/sim.h b/src/main/rx/sim.h index 9c8ff36d1b0..e29d8d34cf2 100644 --- a/src/main/rx/sim.h +++ b/src/main/rx/sim.h @@ -20,5 +20,6 @@ #include "rx/rx.h" void rxSimSetChannelValue(uint16_t* values, uint8_t count); -void rxSimSetRssi(uint16_t value); +void rxSimSetRssi(const uint16_t value); +void rxSimSetFailsafe(const bool value); void rxSimInit(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig); diff --git a/src/main/sensors/battery.c b/src/main/sensors/battery.c index 38d410610e1..34e67aa5abc 100644 --- a/src/main/sensors/battery.c +++ b/src/main/sensors/battery.c @@ -633,6 +633,12 @@ void currentMeterUpdate(timeUs_t timeDelta) break; } +#ifdef USE_SIMULATOR + if (ARMING_FLAG(SIMULATOR_MODE_HITL) && SIMULATOR_HAS_OPTION(HITL_CURRENT_SENSOR)) { + amperage = ((uint16_t)simulatorData.current) * 10; + } +#endif + // Clamp amperage to positive values amperage = MAX(0, amperage); diff --git a/src/main/target/SITL/sim/xplane.c b/src/main/target/SITL/sim/xplane.c index 612f4a54a0a..782c022ba5a 100644 --- a/src/main/target/SITL/sim/xplane.c +++ b/src/main/target/SITL/sim/xplane.c @@ -22,46 +22,56 @@ * along with this program. If not, see http://www.gnu.org/licenses/. */ +#include "target/SITL/sim/xplane.h" + +#include +#include +#include +#include +#include +#include +#include #include #include #include #include -#include #include -#include -#include #include -#include -#include -#include -#include - -#include "platform.h" -#include "target.h" -#include "target/SITL/sim/xplane.h" -#include "target/SITL/sim/simHelper.h" -#include "fc/runtime_config.h" -#include "drivers/time.h" +#include "common/maths.h" +#include "common/utils.h" #include "drivers/accgyro/accgyro_fake.h" #include "drivers/barometer/barometer_fake.h" -#include "sensors/battery_sensor_fake.h" -#include "sensors/acceleration.h" -#include "drivers/pitotmeter/pitotmeter_fake.h" #include "drivers/compass/compass_fake.h" +#include "drivers/pitotmeter/pitotmeter_fake.h" #include "drivers/rangefinder/rangefinder_virtual.h" -#include "io/rangefinder.h" -#include "common/utils.h" -#include "common/maths.h" +#include "drivers/time.h" +#include "fc/runtime_config.h" +#include "flight/imu.h" #include "flight/mixer.h" #include "flight/servos.h" -#include "flight/imu.h" #include "io/gps.h" +#include "io/rangefinder.h" +#include "platform.h" #include "rx/sim.h" +#include "sensors/acceleration.h" +#include "sensors/battery_sensor_fake.h" +#include "target.h" +#include "target/SITL/sim/simHelper.h" #define XP_PORT 49000 #define XPLANE_JOYSTICK_AXIS_COUNT 8 +#define XITL_DREF_VERSION 2 +typedef enum +{ + DISCONNECTED, + CONNECTING, + INIT_CONNECTION, + CONNECTED, +} connectionState_t; + +static connectionState_t connectionState = CONNECTING; static uint8_t pwmMapping[XP_MAX_PWM_OUTS]; static uint8_t mappingCount; @@ -70,9 +80,10 @@ static struct sockaddr_storage serverAddr; static socklen_t serverAddrLen; static int sockFd; static pthread_t listenThread; -static bool initialized = false; static bool useImu = false; + + static float lattitude = 0; static float longitude = 0; static float elevation = 0; @@ -93,11 +104,27 @@ static float gyro_x = 0; static float gyro_y = 0; static float gyro_z = 0; static float barometer = 0; -static bool hasJoystick = false; +static bool hasJoystick = false; static float joystickRaw[XPLANE_JOYSTICK_AXIS_COUNT]; +// INAV-XITL specific variables +static int inavXitlDrefVersion = 0; +static int numSats = 16; +static int fixType = GPS_FIX_3D; +static float magX = 0; +static float magY = 0; +static float magZ = 0; +static int32_t rangefinderAltitude = -1; // cm +static float batteryVoltage = 12.6f; +static float batteryCurrent = 0; +static uint16_t rssi = 0; +static bool failsafe = false; + typedef enum { + DREF_XITL_DataRef_Version, + + // Common and XPLane DREFs DREF_LATITUDE, DREF_LONGITUDE, DREF_ELEVATION, @@ -128,25 +155,41 @@ typedef enum DREF_JOYSTICK_VALUES_CH6, DREF_JOYSTICK_VALUES_CH7, DREF_JOYSTICK_VALUES_CH8, + + // INAV-XITL DREFs + DREF_XITL_HEARTBEAT, + DREF_XITL_NUM_SATS, + DREF_XITL_FIX_TYPE, + DREF_XITL_MAG_X, + DREF_XITL_MAG_Y, + DREF_XITL_MAG_Z, + DREF_XITL_RANGEFINDER, + DREF_XITL_BATTERY_VOLTAGE, + DREF_XITL_BATTERY_CURRENT, + DREF_XITL_RSSI, + DREF_XITL_FAILSAFE, + + DREF_LAST } dref_t; -uint32_t xint2uint32 (uint8_t * buf) +uint32_t xint2uint32(uint8_t* buf) { - return buf[3] << 24 | buf [2] << 16 | buf [1] << 8 | buf [0]; + return buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0]; } -float xflt2float (uint8_t * buf) +float xflt2float(uint8_t* buf) { - union { - float f; - uint32_t i; - } v; + union + { + float f; + uint32_t i; + } v; - v.i = xint2uint32 (buf); - return v.f; + v.i = xint2uint32(buf); + return v.f; } -static void registerDref(dref_t id, char* dref, uint32_t freq) +static bool registerDref(dref_t id, char* dref, uint32_t freq) { char buf[413]; memset(buf, 0, sizeof(buf)); @@ -156,7 +199,8 @@ static void registerDref(dref_t id, char* dref, uint32_t freq) memcpy(buf + 9, &id, 4); memcpy(buf + 13, dref, strlen(dref) + 1); - sendto(sockFd, (void*)buf, sizeof(buf), 0, (struct sockaddr*)&serverAddr, serverAddrLen); + ssize_t sentBytes = sendto(sockFd, (void*)buf, sizeof(buf), 0, (struct sockaddr*)&serverAddr, serverAddrLen); + return (sentBytes >= 0); } static void sendDref(char* dref, float value) @@ -167,250 +211,421 @@ static void sendDref(char* dref, float value) memset(buf + 9, ' ', sizeof(buf) - 9); strcpy(buf + 9, dref); - sendto(sockFd, (void*)buf, sizeof(buf), 0, (struct sockaddr*)&serverAddr, serverAddrLen); + ssize_t sentBytes = sendto(sockFd, (void*)buf, sizeof(buf), 0, (struct sockaddr*)&serverAddr, serverAddrLen); + if (sentBytes < 0) { + connectionState = DISCONNECTED; + } } -static void* listenWorker(void* arg) +static void registerXplaneDrefs(int32_t freq) { - UNUSED(arg); + // GPS + registerDref(DREF_LATITUDE, "sim/flightmodel/position/latitude", freq); + registerDref(DREF_LONGITUDE, "sim/flightmodel/position/longitude", freq); + registerDref(DREF_ELEVATION, "sim/flightmodel/position/elevation", freq); + registerDref(DREF_AGL, "sim/flightmodel/position/y_agl", freq); + registerDref(DREF_LOCAL_VX, "sim/flightmodel/position/local_vx", freq); + registerDref(DREF_LOCAL_VY, "sim/flightmodel/position/local_vy", freq); + registerDref(DREF_LOCAL_VZ, "sim/flightmodel/position/local_vz", freq); + + // Speed + registerDref(DREF_GROUNDSPEED, "sim/flightmodel/position/groundspeed", freq); + registerDref(DREF_TRUE_AIRSPEED, "sim/flightmodel/position/true_airspeed", freq); +} +static void registerInavXitlDrefs(int32_t freq) +{ + // GPS + registerDref(DREF_XITL_NUM_SATS, "inav_xitl/gps/numSats", freq); + registerDref(DREF_XITL_FIX_TYPE, "inav_xitl/gps/fix", freq); + registerDref(DREF_LATITUDE, "inav_xitl/gps/latitude", freq); + registerDref(DREF_LONGITUDE, "inav_xitl/gps/longitude", freq); + registerDref(DREF_ELEVATION, "inav_xitl/gps/elevation", freq); + registerDref(DREF_LOCAL_VX, "inav_xitl/gps/velocities[0]", freq); + registerDref(DREF_LOCAL_VY, "inav_xitl/gps/velocities[1]", freq); + registerDref(DREF_LOCAL_VZ, "inav_xitl/gps/velocities[2]", freq); + + // Speed + registerDref(DREF_GROUNDSPEED, "inav_xitl/gps/groundspeed", freq); + registerDref(DREF_TRUE_AIRSPEED, "inav_xitl/sensors/airspeed", freq); + registerDref(DREF_XITL_MAG_X, "inav_xitl/sensors/magnetometer[0]", freq); + registerDref(DREF_XITL_MAG_Y, "inav_xitl/sensors/magnetometer[1]", freq); + registerDref(DREF_XITL_MAG_Z, "inav_xitl/sensors/magnetometer[2]", freq); + registerDref(DREF_XITL_RANGEFINDER, "inav_xitl/sensors/rangefinder", freq); + + // Battery + registerDref(DREF_XITL_BATTERY_VOLTAGE, "inav_xitl/sensors/battery_voltage", freq); + registerDref(DREF_XITL_BATTERY_CURRENT, "inav_xitl/sensors/battery_current", freq); + + // RC + registerDref(DREF_XITL_RSSI, "inav_xitl/rc/rssi", freq); + registerDref(DREF_XITL_FAILSAFE, "inav_xitl/rc/failsafe", freq); +} + +static void registerCommonDrefs(int32_t freq) +{ + registerDref(DREF_XITL_DataRef_Version, "inav_xitl/plugin/xitlDrefVersion", + freq); + registerDref(DREF_POS_PHI, "sim/flightmodel/position/phi", freq); + registerDref(DREF_POS_THETA, "sim/flightmodel/position/theta", freq); + registerDref(DREF_POS_PSI, "sim/flightmodel/position/psi", freq); + registerDref(DREF_POS_HPATH, "sim/flightmodel/position/hpath", freq); + registerDref(DREF_FORCE_G_AXI1, "sim/flightmodel/forces/g_axil", freq); + registerDref(DREF_FORCE_G_SIDE, "sim/flightmodel/forces/g_side", freq); + registerDref(DREF_FORCE_G_NRML, "sim/flightmodel/forces/g_nrml", freq); + + registerDref(DREF_POS_P, "sim/flightmodel/position/P", freq); + registerDref(DREF_POS_Q, "sim/flightmodel/position/Q", freq); + registerDref(DREF_POS_R, "sim/flightmodel/position/R", freq); + + registerDref(DREF_POS_BARO_CURRENT_INHG, "sim/weather/barometer_current_inhg", freq); + registerDref(DREF_HAS_JOYSTICK, "sim/joystick/has_joystick", freq); + registerDref(DREF_JOYSTICK_VALUES_PITCH, "sim/joystick/joy_mapped_axis_value[1]", freq); + registerDref(DREF_JOYSTICK_VALUES_ROll, "sim/joystick/joy_mapped_axis_value[2]", freq); + registerDref(DREF_JOYSTICK_VALUES_YAW, "sim/joystick/joy_mapped_axis_value[3]", freq); + // Abusing cowl flaps for other channels + registerDref(DREF_JOYSTICK_VALUES_THROTTLE, "sim/joystick/joy_mapped_axis_value[57]", freq); + registerDref(DREF_JOYSTICK_VALUES_CH5, "sim/joystick/joy_mapped_axis_value[58]", freq); + registerDref(DREF_JOYSTICK_VALUES_CH6, "sim/joystick/joy_mapped_axis_value[59]", freq); + registerDref(DREF_JOYSTICK_VALUES_CH7, "sim/joystick/joy_mapped_axis_value[60]", freq); + registerDref(DREF_JOYSTICK_VALUES_CH8, "sim/joystick/joy_mapped_axis_value[61]", freq); +} + +static bool receiveSingleDref(dref_t in_dref, float* value) +{ uint8_t buf[1024]; struct sockaddr_storage remoteAddr; socklen_t slen = sizeof(remoteAddr); int recvLen; - while (true) - { + recvLen = recvfrom(sockFd, buf, sizeof(buf), 0, (struct sockaddr*)&remoteAddr, &slen); + if (recvLen < 0 && errno != EWOULDBLOCK) { + return false; + } - float motorValue = 0; - float yokeValues[3] = { 0 }; - int y = 0; - for (int i = 0; i < mappingCount; i++) { - if (y > 2) { - break; - } - if (pwmMapping[i] & 0x80) { // Motor - motorValue = PWM_TO_FLOAT_0_1(motor[pwmMapping[i] & 0x7f]); - } else { - yokeValues[y] = PWM_TO_FLOAT_MINUS_1_1(servo[pwmMapping[i]]); - y++; - } - } + if (strncmp((char*)buf, "RREF", 4) != 0) { + return false; + } - sendDref("sim/operation/override/override_joystick", 1); - sendDref("sim/cockpit2/engine/actuators/throttle_ratio_all", motorValue); - sendDref("sim/joystick/yoke_roll_ratio", yokeValues[0]); - sendDref("sim/joystick/yoke_pitch_ratio", yokeValues[1]); - sendDref("sim/joystick/yoke_heading_ratio", yokeValues[2]); - sendDref("sim/cockpit2/engine/actuators/cowl_flap_ratio[0]", 0); - sendDref("sim/cockpit2/engine/actuators/cowl_flap_ratio[1]", 0); - sendDref("sim/cockpit2/engine/actuators/cowl_flap_ratio[2]", 0); - sendDref("sim/cockpit2/engine/actuators/cowl_flap_ratio[3]", 0); - sendDref("sim/cockpit2/engine/actuators/cowl_flap_ratio[4]", 0); - - recvLen = recvfrom(sockFd, buf, sizeof(buf), 0, (struct sockaddr*)&remoteAddr, &slen); - if (recvLen < 0 && errno != EWOULDBLOCK) { - continue; + for (int i = 5; i < recvLen; i += 8) { + dref_t dref = (dref_t)xint2uint32(&buf[i]); + if (dref == in_dref) { + *value = xflt2float(&(buf[i + 4])); + return true; } + } + + return false; +} + +static void exchangeDataWithXPlane(void) +{ + uint8_t buf[1024]; + struct sockaddr_storage remoteAddr; + socklen_t slen = sizeof(remoteAddr); + int recvLen; - if (strncmp((char*)buf, "RREF", 4) != 0) { - continue; + float motorValue = 0; + float yokeValues[3] = {0}; + int y = 0; + for (int i = 0; i < mappingCount; i++) { + if (y > 2) { + break; + } + if (pwmMapping[i] & 0x80) { // Motor + motorValue = PWM_TO_FLOAT_0_1(motor[pwmMapping[i] & 0x7f]); + } else { + yokeValues[y] = PWM_TO_FLOAT_MINUS_1_1(servo[pwmMapping[i]]); + y++; } + } - for (int i = 5; i < recvLen; i += 8) { - dref_t dref = (dref_t)xint2uint32(&buf[i]); - float value = xflt2float(&(buf[i + 4])); + sendDref("sim/operation/override/override_joystick", 1); + if (inavXitlDrefVersion >= XITL_DREF_VERSION) { + sendDref("inav_xitl/plugin/heartbeat", 1.0f); + // Do not send motor value in Sitl mode, INAV XITL sets throttle itself + } else if (inavXitlDrefVersion < XITL_DREF_VERSION) { + sendDref("sim/cockpit2/engine/actuators/throttle_ratio_all",motorValue); + } - switch (dref) - { - case DREF_LATITUDE: - lattitude = value; - break; + sendDref("sim/joystick/yoke_roll_ratio", yokeValues[0]); + sendDref("sim/joystick/yoke_pitch_ratio", yokeValues[1]); + sendDref("sim/joystick/yoke_heading_ratio", yokeValues[2]); + sendDref("sim/cockpit2/engine/actuators/cowl_flap_ratio[0]", 0); + sendDref("sim/cockpit2/engine/actuators/cowl_flap_ratio[1]", 0); + sendDref("sim/cockpit2/engine/actuators/cowl_flap_ratio[2]", 0); + sendDref("sim/cockpit2/engine/actuators/cowl_flap_ratio[3]", 0); + sendDref("sim/cockpit2/engine/actuators/cowl_flap_ratio[4]", 0); + + recvLen = recvfrom(sockFd, buf, sizeof(buf), 0, + (struct sockaddr*)&remoteAddr, &slen); + if (recvLen < 0 && errno != EWOULDBLOCK) { + return; + } else if (recvLen == 0 || (recvLen == -1 && errno == ECONNRESET)) { + connectionState = DISCONNECTED; + return; + } - case DREF_LONGITUDE: - longitude = value; - break; + if (strncmp((char*)buf, "RREF", 4) != 0) { + return; + } - case DREF_ELEVATION: - elevation = value; - break; + for (int i = 5; i < recvLen; i += 8) { + dref_t dref = (dref_t)xint2uint32(&buf[i]); + float value = xflt2float(&(buf[i + 4])); - case DREF_AGL: - agl = value; - break; + switch (dref) { + case DREF_LATITUDE: + lattitude = value; + break; - case DREF_LOCAL_VX: - local_vx = value; - break; + case DREF_LONGITUDE: + longitude = value; + break; - case DREF_LOCAL_VY: - local_vy = value; - break; + case DREF_ELEVATION: + elevation = value; + break; - case DREF_LOCAL_VZ: - local_vz = value; - break; + case DREF_AGL: + agl = value; + break; - case DREF_GROUNDSPEED: - groundspeed = value; - break; + case DREF_LOCAL_VX: + local_vx = value; + break; - case DREF_TRUE_AIRSPEED: - airspeed = value; - break; + case DREF_LOCAL_VY: + local_vy = value; + break; - case DREF_POS_PHI: - roll = value; - break; + case DREF_LOCAL_VZ: + local_vz = value; + break; - case DREF_POS_THETA: - pitch = value; - break; + case DREF_GROUNDSPEED: + groundspeed = value; + break; - case DREF_POS_PSI: - yaw = value; - break; + case DREF_TRUE_AIRSPEED: + airspeed = value; + break; - case DREF_POS_HPATH: - hpath = value; - break; + case DREF_POS_PHI: + roll = value; + break; - case DREF_FORCE_G_AXI1: - accel_x = value; - break; + case DREF_POS_THETA: + pitch = value; + break; - case DREF_FORCE_G_SIDE: - accel_y = value; - break; + case DREF_POS_PSI: + yaw = value; + break; - case DREF_FORCE_G_NRML: - accel_z = value; - break; + case DREF_POS_HPATH: + hpath = value; + break; - case DREF_POS_P: - gyro_x = value; - break; + case DREF_FORCE_G_AXI1: + accel_x = value; + break; - case DREF_POS_Q: - gyro_y = value; - break; + case DREF_FORCE_G_SIDE: + accel_y = value; + break; - case DREF_POS_R: - gyro_z = value; - break; + case DREF_FORCE_G_NRML: + accel_z = value; + break; - case DREF_POS_BARO_CURRENT_INHG: - barometer = value; - break; + case DREF_POS_P: + gyro_x = value; + break; - case DREF_HAS_JOYSTICK: - hasJoystick = value >= 1 ? true : false; - break; + case DREF_POS_Q: + gyro_y = value; + break; - case DREF_JOYSTICK_VALUES_ROll: - joystickRaw[0] = value; - break; + case DREF_POS_R: + gyro_z = value; + break; - case DREF_JOYSTICK_VALUES_PITCH: - joystickRaw[1] = value; - break; + case DREF_POS_BARO_CURRENT_INHG: + barometer = value; + break; - case DREF_JOYSTICK_VALUES_THROTTLE: - joystickRaw[2] = value; - break; + case DREF_HAS_JOYSTICK: + hasJoystick = value >= 1 ? true : false; + break; - case DREF_JOYSTICK_VALUES_YAW: - joystickRaw[3] = value; - break; + case DREF_JOYSTICK_VALUES_ROll: + joystickRaw[0] = value; + break; - case DREF_JOYSTICK_VALUES_CH5: - joystickRaw[4] = value; - break; + case DREF_JOYSTICK_VALUES_PITCH: + joystickRaw[1] = value; + break; - case DREF_JOYSTICK_VALUES_CH6: - joystickRaw[5] = value; - break; + case DREF_JOYSTICK_VALUES_THROTTLE: + joystickRaw[2] = value; + break; - case DREF_JOYSTICK_VALUES_CH7: - joystickRaw[6] = value; - break; + case DREF_JOYSTICK_VALUES_YAW: + joystickRaw[3] = value; + break; - case DREF_JOYSTICK_VALUES_CH8: - joystickRaw[7] = value; - break; + case DREF_JOYSTICK_VALUES_CH5: + joystickRaw[4] = value; + break; - default: - break; - } - } + case DREF_JOYSTICK_VALUES_CH6: + joystickRaw[5] = value; + break; - if (hpath < 0) { - hpath += 3600; - } + case DREF_JOYSTICK_VALUES_CH7: + joystickRaw[6] = value; + break; - if (yaw < 0){ - yaw += 3600; - } + case DREF_JOYSTICK_VALUES_CH8: + joystickRaw[7] = value; + break; + + case DREF_XITL_NUM_SATS: + numSats = (int)value; + break; + + case DREF_XITL_FIX_TYPE: + fixType = (int)value; + break; + + case DREF_XITL_MAG_X: + magX = value; + break; + + case DREF_XITL_MAG_Y: + magY = value; + break; - if (hasJoystick) { - uint16_t channelValues[XPLANE_JOYSTICK_AXIS_COUNT]; - channelValues[0] = FLOAT_MINUS_1_1_TO_PWM(joystickRaw[0]); - channelValues[1] = FLOAT_MINUS_1_1_TO_PWM(joystickRaw[1]); - channelValues[2] = FLOAT_0_1_TO_PWM(joystickRaw[2]); - channelValues[3] = FLOAT_MINUS_1_1_TO_PWM(joystickRaw[3]); - channelValues[4] = FLOAT_0_1_TO_PWM(joystickRaw[4]); - channelValues[5] = FLOAT_0_1_TO_PWM(joystickRaw[5]); - channelValues[6] = FLOAT_0_1_TO_PWM(joystickRaw[6]); - channelValues[7] = FLOAT_0_1_TO_PWM(joystickRaw[7]); - - rxSimSetChannelValue(channelValues, XPLANE_JOYSTICK_AXIS_COUNT); + case DREF_XITL_MAG_Z: + magZ = value; + break; + + case DREF_XITL_RANGEFINDER: + rangefinderAltitude = (int32_t)value; + break; + + case DREF_XITL_BATTERY_VOLTAGE: + batteryVoltage = value; + break; + + case DREF_XITL_BATTERY_CURRENT: + batteryCurrent = value; + break; + + case DREF_XITL_RSSI: + rssi = (uint16_t)value; + break; + + case DREF_XITL_FAILSAFE: + failsafe = value >= 1 ? true : false; + break; + + default: + break; } + } - gpsFakeSet( - GPS_FIX_3D, - 16, - (int32_t)roundf(lattitude * 10000000), - (int32_t)roundf(longitude * 10000000), - (int32_t)roundf(elevation * 100), - (int16_t)roundf(groundspeed * 100), - (int16_t)roundf(hpath * 10), - 0, //(int16_t)roundf(-local_vz * 100), - 0, //(int16_t)roundf(local_vx * 100), - 0, //(int16_t)roundf(-local_vy * 100), - 0 - ); + if (hpath < 0) { + hpath += 3600; + } + + if (yaw < 0) { + yaw += 3600; + } + + if (hasJoystick) { + uint16_t channelValues[XPLANE_JOYSTICK_AXIS_COUNT]; + channelValues[0] = FLOAT_MINUS_1_1_TO_PWM(joystickRaw[0]); + channelValues[1] = FLOAT_MINUS_1_1_TO_PWM(joystickRaw[1]); + channelValues[2] = FLOAT_0_1_TO_PWM(joystickRaw[2]); + channelValues[3] = FLOAT_MINUS_1_1_TO_PWM(joystickRaw[3]); + channelValues[4] = FLOAT_0_1_TO_PWM(joystickRaw[4]); + channelValues[5] = FLOAT_0_1_TO_PWM(joystickRaw[5]); + channelValues[6] = FLOAT_0_1_TO_PWM(joystickRaw[6]); + channelValues[7] = FLOAT_0_1_TO_PWM(joystickRaw[7]); + + rxSimSetChannelValue(channelValues, XPLANE_JOYSTICK_AXIS_COUNT); + } + gpsFakeSet( + fixType, + numSats, + (int32_t)roundf(lattitude * 10000000), + (int32_t)roundf(longitude * 10000000), + (int32_t)roundf(elevation * 100), + (int16_t)roundf(groundspeed * 100), (int16_t)roundf(hpath * 10), + 0, //(int16_t)roundf(-local_vz * 100), + 0, //(int16_t)roundf(local_vx * 100), + 0, //(int16_t)roundf(-local_vy * 100), + 0 + ); + + if (inavXitlDrefVersion >= XITL_DREF_VERSION) { + if (rangefinderAltitude == 0xff) { + fakeRangefindersSetData(-1); + } else { + fakeRangefindersSetData(rangefinderAltitude); + } + } else if (inavXitlDrefVersion < XITL_DREF_VERSION) { + // Use AGL from X-Plane as rangefinder input const int32_t altitideOverGround = (int32_t)roundf(agl * 100); - if (altitideOverGround > 0 && altitideOverGround <= RANGEFINDER_VIRTUAL_MAX_RANGE_CM) { + if (altitideOverGround > 0 && + altitideOverGround <= RANGEFINDER_VIRTUAL_MAX_RANGE_CM) { fakeRangefindersSetData(altitideOverGround); } else { fakeRangefindersSetData(-1); } + } - const int16_t roll_inav = roll * 10; - const int16_t pitch_inav = -pitch * 10; - const int16_t yaw_inav = yaw * 10; + const int16_t roll_inav = roll * 10; + const int16_t pitch_inav = -pitch * 10; + const int16_t yaw_inav = yaw * 10; - if (!useImu) { - imuSetAttitudeRPY(roll_inav, pitch_inav, yaw_inav); - imuUpdateAttitude(micros()); - } + if (!useImu) { + imuSetAttitudeRPY(roll_inav, pitch_inav, yaw_inav); + imuUpdateAttitude(micros()); + } - fakeAccSet( - constrainToInt16(-accel_x * GRAVITY_MSS * 1000.0f), - constrainToInt16(accel_y * GRAVITY_MSS * 1000.0f), - constrainToInt16(accel_z * GRAVITY_MSS * 1000.0f) - ); + fakeAccSet( + constrainToInt16(-accel_x * GRAVITY_MSS * 1000.0f), + constrainToInt16(accel_y * GRAVITY_MSS * 1000.0f), + constrainToInt16(accel_z * GRAVITY_MSS * 1000.0f) + ); - fakeGyroSet( - constrainToInt16(gyro_x * 16.0f), - constrainToInt16(-gyro_y * 16.0f), - constrainToInt16(-gyro_z * 16.0f) - ); + fakeGyroSet( + constrainToInt16(gyro_x * 16.0f), + constrainToInt16(-gyro_y * 16.0f), + constrainToInt16(-gyro_z * 16.0f) + ); + + fakePitotSetAirspeed(airspeed * 100.0f); + + fakeBaroSet((int32_t)roundf(barometer * 3386.39f), + DEGREES_TO_CENTIDEGREES(21)); - fakeBaroSet((int32_t)roundf(barometer * 3386.39f), DEGREES_TO_CENTIDEGREES(21)); - fakePitotSetAirspeed(airspeed * 100.0f); + if (inavXitlDrefVersion >= XITL_DREF_VERSION) { + fakeBattSensorSetVbat(batteryVoltage * 100); + fakeBattSensorSetAmperage(batteryCurrent * 100); + rxSimSetRssi(rssi); + rxSimSetFailsafe(failsafe); + fakeMagSet( + constrainToInt16(magX * 1024.0f), + constrainToInt16(magY * 1024.0f), + constrainToInt16(magZ * 1024.0f) + ); + } else if (inavXitlDrefVersion >= XITL_DREF_VERSION) { fakeBattSensorSetVbat(16.8f * 100); fpQuaternion_t quat; @@ -425,32 +640,82 @@ static void* listenWorker(void* arg) constrainToInt16(north.y * 1024.0f), constrainToInt16(north.z * 1024.0f) ); + } - if (!initialized) { - ENABLE_ARMING_FLAG(SIMULATOR_MODE_SITL); - // Aircraft can wobble on the runway and prevents calibration of the accelerometer - ENABLE_STATE(ACCELEROMETER_CALIBRATED); - initialized = true; - } + unlockMainPID(); +} + +static void* listenWorker(void* arg) +{ + UNUSED(arg); - unlockMainPID(); + while (connectionState != DISCONNECTED) { + switch (connectionState) { + case CONNECTING: + + + // First register all DREFs with 0 freq to clear previous ones + registerCommonDrefs(0); + registerInavXitlDrefs(0); + registerXplaneDrefs(0); + + registerCommonDrefs(100); + + float inavXitlDrefVersionF = 0; + // Wait to determine connection type and test if X-PLane is present + if (receiveSingleDref(DREF_XITL_DataRef_Version, &inavXitlDrefVersionF)) { + inavXitlDrefVersion = (int)inavXitlDrefVersionF; + connectionState = INIT_CONNECTION; + break; + } + delay(100); + break; + + case INIT_CONNECTION: { + if (inavXitlDrefVersion >= XITL_DREF_VERSION) { + registerInavXitlDrefs(100); + printf("[SIM] X-Plane INAV-XITL plugin detected. DataRef version %d.\n", inavXitlDrefVersion); + } else { + registerXplaneDrefs(100); + } + // Get valid data to initialize sensors + exchangeDataWithXPlane(); + + ENABLE_ARMING_FLAG(SIMULATOR_MODE_SITL); + // Aircraft can wobble on the runway and prevents + // calibration of the accelerometer + ENABLE_STATE(ACCELEROMETER_CALIBRATED); + connectionState = CONNECTED; + + break; + } + case CONNECTED: + exchangeDataWithXPlane(); + break; + default: + break; + } } + fprintf(stderr, "[SOCKET] X-Plane connection lost.\n"); + exit(0); + return NULL; } - -bool simXPlaneInit(char* ip, int port, uint8_t* mapping, uint8_t mapCount, bool imu) +bool simXPlaneInit(char* ip, int port, uint8_t* mapping, uint8_t mapCount, + bool imu) { memcpy(pwmMapping, mapping, mapCount); mappingCount = mapCount; useImu = imu; + connectionState = CONNECTING; if (port == 0) { - port = XP_PORT; // use default port + port = XP_PORT; // use default port } - if(lookupAddress(ip, port, SOCK_DGRAM, (struct sockaddr*)&serverAddr, &serverAddrLen) != 0) { + if (lookupAddress(ip, port, SOCK_DGRAM, (struct sockaddr*)&serverAddr, &serverAddrLen) != 0) { return false; } @@ -458,21 +723,21 @@ bool simXPlaneInit(char* ip, int port, uint8_t* mapping, uint8_t mapCount, bool if (sockFd < 0) { return false; } else { - char addrbuf[IPADDRESS_PRINT_BUFLEN]; - char *nptr = prettyPrintAddress((struct sockaddr *)&serverAddr, addrbuf, IPADDRESS_PRINT_BUFLEN ); + char addrbuf[IPADDRESS_PRINT_BUFLEN]; + char* nptr = prettyPrintAddress((struct sockaddr*)&serverAddr, addrbuf, IPADDRESS_PRINT_BUFLEN); if (nptr != NULL) { fprintf(stderr, "[SOCKET] xplane address = %s, fd=%d\n", nptr, sockFd); } } struct timeval tv; - tv.tv_sec = 1; - tv.tv_usec = 0; - if (setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *) &tv,sizeof(struct timeval))) { + tv.tv_sec = 1; + tv.tv_usec = 0; + if (setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, (struct timeval*)&tv, sizeof(struct timeval))) { return false; } - if (setsockopt(sockFd, SOL_SOCKET, SO_SNDTIMEO, (struct timeval *) &tv,sizeof(struct timeval))) { + if (setsockopt(sockFd, SOL_SOCKET, SO_SNDTIMEO, (struct timeval*)&tv, sizeof(struct timeval))) { return false; } @@ -480,37 +745,7 @@ bool simXPlaneInit(char* ip, int port, uint8_t* mapping, uint8_t mapCount, bool return false; } - while (!initialized) { - registerDref(DREF_LATITUDE, "sim/flightmodel/position/latitude", 100); - registerDref(DREF_LONGITUDE, "sim/flightmodel/position/longitude", 100); - registerDref(DREF_ELEVATION, "sim/flightmodel/position/elevation", 100); - registerDref(DREF_AGL, "sim/flightmodel/position/y_agl", 100); - registerDref(DREF_LOCAL_VX, "sim/flightmodel/position/local_vx", 100); - registerDref(DREF_LOCAL_VY, "sim/flightmodel/position/local_vy", 100); - registerDref(DREF_LOCAL_VZ, "sim/flightmodel/position/local_vz", 100); - registerDref(DREF_GROUNDSPEED, "sim/flightmodel/position/groundspeed", 100); - registerDref(DREF_TRUE_AIRSPEED, "sim/flightmodel/position/true_airspeed", 100); - registerDref(DREF_POS_PHI, "sim/flightmodel/position/phi", 100); - registerDref(DREF_POS_THETA, "sim/flightmodel/position/theta", 100); - registerDref(DREF_POS_PSI, "sim/flightmodel/position/psi", 100); - registerDref(DREF_POS_HPATH, "sim/flightmodel/position/hpath", 100); - registerDref(DREF_FORCE_G_AXI1, "sim/flightmodel/forces/g_axil", 100); - registerDref(DREF_FORCE_G_SIDE, "sim/flightmodel/forces/g_side", 100); - registerDref(DREF_FORCE_G_NRML, "sim/flightmodel/forces/g_nrml", 100); - registerDref(DREF_POS_P, "sim/flightmodel/position/P", 100); - registerDref(DREF_POS_Q, "sim/flightmodel/position/Q", 100); - registerDref(DREF_POS_R, "sim/flightmodel/position/R", 100); - registerDref(DREF_POS_BARO_CURRENT_INHG, "sim/weather/barometer_current_inhg", 100); - registerDref(DREF_HAS_JOYSTICK, "sim/joystick/has_joystick", 100); - registerDref(DREF_JOYSTICK_VALUES_PITCH, "sim/joystick/joy_mapped_axis_value[1]", 100); - registerDref(DREF_JOYSTICK_VALUES_ROll, "sim/joystick/joy_mapped_axis_value[2]", 100); - registerDref(DREF_JOYSTICK_VALUES_YAW, "sim/joystick/joy_mapped_axis_value[3]", 100); - // Abusing cowl flaps for other channels - registerDref(DREF_JOYSTICK_VALUES_THROTTLE, "sim/joystick/joy_mapped_axis_value[57]", 100); - registerDref(DREF_JOYSTICK_VALUES_CH5, "sim/joystick/joy_mapped_axis_value[58]", 100); - registerDref(DREF_JOYSTICK_VALUES_CH6, "sim/joystick/joy_mapped_axis_value[59]", 100); - registerDref(DREF_JOYSTICK_VALUES_CH7, "sim/joystick/joy_mapped_axis_value[60]", 100); - registerDref(DREF_JOYSTICK_VALUES_CH8, "sim/joystick/joy_mapped_axis_value[61]", 100); + while (connectionState != CONNECTED) { delay(250); } diff --git a/src/main/target/SITL/sim/xplane.h b/src/main/target/SITL/sim/xplane.h index 1777a30af2b..626a9bc1984 100644 --- a/src/main/target/SITL/sim/xplane.h +++ b/src/main/target/SITL/sim/xplane.h @@ -23,6 +23,7 @@ */ #include +#include #define XP_MAX_PWM_OUTS 4 diff --git a/src/main/target/SITL/target.h b/src/main/target/SITL/target.h index f7963dfdd95..46ad6d9dad4 100644 --- a/src/main/target/SITL/target.h +++ b/src/main/target/SITL/target.h @@ -67,8 +67,6 @@ #define USE_FAKE_BARO #define USE_FAKE_MAG #define USE_GPS_FAKE -#define USE_RANGEFINDER_FAKE -#define USE_RX_SIM #undef MAX_MIXER_PROFILE_COUNT #define MAX_MIXER_PROFILE_COUNT 2 diff --git a/src/main/target/common.h b/src/main/target/common.h index 45eb12ac4bc..4eceac2a124 100644 --- a/src/main/target/common.h +++ b/src/main/target/common.h @@ -180,6 +180,8 @@ #define USE_SIMULATOR #define USE_PITOT_VIRTUAL #define USE_FAKE_BATT_SENSOR +#define USE_RANGEFINDER_FAKE +#define USE_RX_SIM #define USE_CMS_FONT_PREVIEW From 73193e892898314b159aed7174472ddce774e61e Mon Sep 17 00:00:00 2001 From: Scavanger Date: Sat, 17 Jan 2026 09:55:23 -0300 Subject: [PATCH 03/67] Last fixes + Suggestions --- src/main/fc/fc_msp.c | 23 ++++++++++++++--------- src/main/target/SITL/sim/xplane.c | 9 ++++----- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/main/fc/fc_msp.c b/src/main/fc/fc_msp.c index dacf047fcf4..3bf7a354058 100644 --- a/src/main/fc/fc_msp.c +++ b/src/main/fc/fc_msp.c @@ -4201,27 +4201,32 @@ static void readMspSimulatorValues(sbuf_t *src, const int dataSize, const uint8_ static mspResult_e mspProcessSimulatorCommand(sbuf_t *dst, sbuf_t *src, const int dataSize) { + if (dataSize < 2) { + return MSP_RESULT_ERROR; + } + const uint8_t simMspVersion = sbufReadU8(src); // Get the Simulator MSP version - // Check the MSP version of simulator if (simMspVersion != SIMULATOR_MSP_VERSION_2 && simMspVersion != SIMULATOR_MSP_VERSION_3) { return MSP_RESULT_ERROR; } - - // Backward compatibility for HITL Plugin 1.X - simulatorData.flags = sbufReadU8(src); if (simMspVersion == SIMULATOR_MSP_VERSION_3) { - simulatorData.flags |= ((uint16_t)sbufReadU8(src)) << 8; + if (dataSize < 3) { + return MSP_RESULT_ERROR; + } + simulatorData.flags = sbufReadU16(src); + } else { + simulatorData.flags = sbufReadU8(src); } - - // In case of SITL mode, we do not read any input from the simulator (done vis DREFs) + + const int remainingPayload = (int)sbufBytesRemaining(src); + if (!SIMULATOR_HAS_OPTION(HITL_SITL_MODE)) { - // Check if simulator is disabled and was previously enabled (flags != 0) if (!SIMULATOR_HAS_OPTION(HITL_ENABLE) && simulatorData.flags) { fcReboot(false); return MSP_RESULT_NO_REPLY; } else { - readMspSimulatorValues(src, dataSize, simMspVersion); + readMspSimulatorValues(src, remainingPayload, simMspVersion); } } diff --git a/src/main/target/SITL/sim/xplane.c b/src/main/target/SITL/sim/xplane.c index 782c022ba5a..fbc1824e35d 100644 --- a/src/main/target/SITL/sim/xplane.c +++ b/src/main/target/SITL/sim/xplane.c @@ -303,7 +303,7 @@ static bool receiveSingleDref(dref_t in_dref, float* value) return false; } - if (strncmp((char*)buf, "RREF", 4) != 0) { + if (recvLen < 5 || strncmp((char*)buf, "RREF", 4) != 0) { return false; } @@ -572,12 +572,12 @@ static void exchangeDataWithXPlane(void) ); if (inavXitlDrefVersion >= XITL_DREF_VERSION) { - if (rangefinderAltitude == 0xff) { + if (rangefinderAltitude == 0xffff) { fakeRangefindersSetData(-1); } else { fakeRangefindersSetData(rangefinderAltitude); } - } else if (inavXitlDrefVersion < XITL_DREF_VERSION) { + } else { // Use AGL from X-Plane as rangefinder input const int32_t altitideOverGround = (int32_t)roundf(agl * 100); if (altitideOverGround > 0 && @@ -625,7 +625,7 @@ static void exchangeDataWithXPlane(void) constrainToInt16(magY * 1024.0f), constrainToInt16(magZ * 1024.0f) ); - } else if (inavXitlDrefVersion >= XITL_DREF_VERSION) { + } else { fakeBattSensorSetVbat(16.8f * 100); fpQuaternion_t quat; @@ -653,7 +653,6 @@ static void* listenWorker(void* arg) switch (connectionState) { case CONNECTING: - // First register all DREFs with 0 freq to clear previous ones registerCommonDrefs(0); registerInavXitlDrefs(0); From 1938d43e75d393bc12fd27cb8d934402f4ec7426 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Wed, 25 Feb 2026 12:32:07 -0600 Subject: [PATCH 04/67] [FIX] Prevent stack smashing via unbounded OSD message array writes Both osdGetSystemMessage() and osdGetMultiFunctionMessage() accumulate message pointers into a fixed-size array with no bounds checking. With recent additions (geozone avoidance, pitot validation, etc.) the number of potential writes exceeds the array size, causing stack corruption. Introduce a scoped ADD_MSG() macro that guards each write: #define ADD_MSG(msg) if (messageCount < ARRAYLEN(messages)) \ messages[messageCount++] = (msg) Excess messages are silently dropped rather than written past the end of the array. The macro is #undef'd at the end of each function. Fixes: https://github.com/iNavFlight/inav/pull/10048 (Yury-MonZon) --- src/main/io/osd.c | 123 ++++++++++++++++++++++++---------------------- 1 file changed, 63 insertions(+), 60 deletions(-) diff --git a/src/main/io/osd.c b/src/main/io/osd.c index 6ad55632c17..722648d10fd 100644 --- a/src/main/io/osd.c +++ b/src/main/io/osd.c @@ -6137,10 +6137,10 @@ textAttributes_t osdGetSystemMessage(char *buff, size_t buff_size, bool isCenter /* WARNING: messageBuf is shared, use accordingly */ char messageBuf[MAX(SETTING_MAX_NAME_LENGTH, OSD_MESSAGE_LENGTH + 1)]; - /* WARNING: ensure number of messages returned does not exceed messages array size - * Messages array set 1 larger than maximum expected message count of 6 */ + /* Messages array - use ADD_MSG() for bounds-safe access. */ const char *messages[7]; unsigned messageCount = 0; + #define ADD_MSG(msg) if (messageCount < ARRAYLEN(messages)) messages[messageCount++] = (msg) const char *failsafeInfoMessage = NULL; const char *invertedInfoMessage = NULL; @@ -6149,16 +6149,16 @@ textAttributes_t osdGetSystemMessage(char *buff, size_t buff_size, bool isCenter if (FLIGHT_MODE(FAILSAFE_MODE) || FLIGHT_MODE(NAV_RTH_MODE) || FLIGHT_MODE(NAV_WP_MODE) || navigationIsExecutingAnEmergencyLanding()) { /* ADDS MAXIMUM OF 3 MESSAGES TO TOTAL NORMALLY, 5 MESSAGES DURING FAILSAFE */ if (navGetCurrentStateFlags() & NAV_AUTO_WP_DONE) { - messages[messageCount++] = STATE(LANDING_DETECTED) ? OSD_MESSAGE_STR(OSD_MSG_WP_LANDED) : OSD_MESSAGE_STR(OSD_MSG_WP_FINISHED); + ADD_MSG(STATE(LANDING_DETECTED) ? OSD_MESSAGE_STR(OSD_MSG_WP_LANDED) : OSD_MESSAGE_STR(OSD_MSG_WP_FINISHED)); } else if (NAV_Status.state == MW_NAV_STATE_WP_ENROUTE) { // Countdown display for remaining Waypoints char buf[6]; osdFormatDistanceSymbol(buf, posControl.wpDistance, 0, 3); tfp_sprintf(messageBuf, "TO WP %u/%u (%s)", getGeoWaypointNumber(posControl.activeWaypointIndex), posControl.geoWaypointCount, buf); - messages[messageCount++] = messageBuf; + ADD_MSG(messageBuf); } else if (NAV_Status.state == MW_NAV_STATE_HOLD_TIMED) { if (navConfig()->general.waypoint_enforce_altitude && !posControl.wpAltitudeReached) { - messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_ADJUSTING_WP_ALT); + ADD_MSG(OSD_MESSAGE_STR(OSD_MSG_ADJUSTING_WP_ALT)); } else { // WP hold time countdown in seconds timeMs_t currentTime = millis(); @@ -6167,25 +6167,25 @@ textAttributes_t osdGetSystemMessage(char *buff, size_t buff_size, bool isCenter tfp_sprintf(messageBuf, "HOLDING WP FOR %2u S", holdTimeRemaining); - messages[messageCount++] = messageBuf; + ADD_MSG(messageBuf); } } else { const char *navStateMessage = navigationStateMessage(); if (navStateMessage) { - messages[messageCount++] = navStateMessage; + ADD_MSG(navStateMessage); } } #if defined(USE_SAFE_HOME) const char *safehomeMessage = divertingToSafehomeMessage(); if (safehomeMessage) { - messages[messageCount++] = safehomeMessage; + ADD_MSG(safehomeMessage); } #endif #ifdef USE_GEOZONE if (geozone.avoidInRTHInProgress) { - messages[messageCount++] = OSD_MSG_AVOID_ZONES_RTH; + ADD_MSG(OSD_MSG_AVOID_ZONES_RTH); } #endif if (FLIGHT_MODE(FAILSAFE_MODE)) { // In FS mode while armed @@ -6193,38 +6193,38 @@ textAttributes_t osdGetSystemMessage(char *buff, size_t buff_size, bool isCenter uint16_t remainingHoldSec = MS2S(posControl.landingDelay - millis()); tfp_sprintf(messageBuf, "LANDING DELAY: %3u SECONDS", remainingHoldSec); - messages[messageCount++] = messageBuf; + ADD_MSG(messageBuf); } const char *failsafePhaseMessage = osdFailsafePhaseMessage(); failsafeInfoMessage = osdFailsafeInfoMessage(); if (failsafePhaseMessage) { - messages[messageCount++] = failsafePhaseMessage; + ADD_MSG(failsafePhaseMessage); } if (failsafeInfoMessage) { - messages[messageCount++] = failsafeInfoMessage; + ADD_MSG(failsafeInfoMessage); } } else if (isWaypointMissionRTHActive()) { // if RTH activated whilst WP mode selected, remind pilot to cancel WP mode to exit RTH - messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_WP_RTH_CANCEL); + ADD_MSG(OSD_MESSAGE_STR(OSD_MSG_WP_RTH_CANCEL)); } } else if (STATE(LANDING_DETECTED)) { - messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_LANDED); + ADD_MSG(OSD_MESSAGE_STR(OSD_MSG_LANDED)); } else { #ifdef USE_GEOZONE char buf[12], buf1[12]; switch (geozone.messageState) { case GEOZONE_MESSAGE_STATE_NFZ: - messages[messageCount++] = OSD_MSG_NFZ; + ADD_MSG(OSD_MSG_NFZ); break; case GEOZONE_MESSAGE_STATE_LEAVING_FZ: osdFormatDistanceSymbol(buf, geozone.distanceToZoneBorder3d, 0, 3); tfp_sprintf(messageBuf, OSD_MSG_LEAVING_FZ, buf); - messages[messageCount++] = messageBuf; + ADD_MSG(messageBuf); break; case GEOZONE_MESSAGE_STATE_OUTSIDE_FZ: - messages[messageCount++] = OSD_MSG_OUTSIDE_FZ; + ADD_MSG(OSD_MSG_OUTSIDE_FZ); break; case GEOZONE_MESSAGE_STATE_ENTERING_NFZ: osdFormatDistanceSymbol(buf, geozone.distanceToZoneBorder3d, 0, 3); @@ -6234,36 +6234,36 @@ textAttributes_t osdGetSystemMessage(char *buff, size_t buff_size, bool isCenter osdFormatAltitudeSymbol(buf1, geozone.zoneInfo); } tfp_sprintf(messageBuf, OSD_MSG_ENTERING_NFZ, buf, buf1); - messages[messageCount++] = messageBuf; + ADD_MSG(messageBuf); break; case GEOZONE_MESSAGE_STATE_AVOIDING_FB: - messages[messageCount++] = OSD_MSG_AVOIDING_FB; + ADD_MSG(OSD_MSG_AVOIDING_FB); if (!posControl.sendTo.lockSticks) { - messages[messageCount++] = OSD_MSG_MOVE_STICKS; + ADD_MSG(OSD_MSG_MOVE_STICKS); } break; case GEOZONE_MESSAGE_STATE_RETURN_TO_ZONE: - messages[messageCount++] = OSD_MSG_RETURN_TO_ZONE; + ADD_MSG(OSD_MSG_RETURN_TO_ZONE); if (!posControl.sendTo.lockSticks) { - messages[messageCount++] = OSD_MSG_MOVE_STICKS; + ADD_MSG(OSD_MSG_MOVE_STICKS); } break; case GEOZONE_MESSAGE_STATE_AVOIDING_ALTITUDE_BREACH: - messages[messageCount++] = OSD_MSG_AVOIDING_ALT_BREACH; + ADD_MSG(OSD_MSG_AVOIDING_ALT_BREACH); if (!posControl.sendTo.lockSticks) { - messages[messageCount++] = OSD_MSG_MOVE_STICKS; + ADD_MSG(OSD_MSG_MOVE_STICKS); } break; case GEOZONE_MESSAGE_STATE_FLYOUT_NFZ: - messages[messageCount++] = OSD_MSG_FLYOUT_NFZ; + ADD_MSG(OSD_MSG_FLYOUT_NFZ); if (!posControl.sendTo.lockSticks) { - messages[messageCount++] = OSD_MSG_MOVE_STICKS; + ADD_MSG(OSD_MSG_MOVE_STICKS); } break; case GEOZONE_MESSAGE_STATE_POS_HOLD: - messages[messageCount++] = OSD_MSG_AVOIDING_FB; + ADD_MSG(OSD_MSG_AVOIDING_FB); if (!geozone.sticksLocked) { - messages[messageCount++] = OSD_MSG_MOVE_STICKS; + ADD_MSG(OSD_MSG_MOVE_STICKS); } break; case GEOZONE_MESSAGE_STATE_NONE: @@ -6275,37 +6275,37 @@ textAttributes_t osdGetSystemMessage(char *buff, size_t buff_size, bool isCenter if (STATE(AIRPLANE)) { /* ADDS MAXIMUM OF 3 MESSAGES TO TOTAL */ #ifdef USE_FW_AUTOLAND if (canFwLandingBeCancelled()) { - messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_MOVE_STICKS); + ADD_MSG(OSD_MESSAGE_STR(OSD_MSG_MOVE_STICKS)); } else #endif if (navGetCurrentStateFlags() & NAV_CTL_LAUNCH) { - messages[messageCount++] = navConfig()->fw.launch_manual_throttle ? OSD_MESSAGE_STR(OSD_MSG_AUTOLAUNCH_MANUAL) : - OSD_MESSAGE_STR(OSD_MSG_AUTOLAUNCH); + ADD_MSG(navConfig()->fw.launch_manual_throttle ? OSD_MESSAGE_STR(OSD_MSG_AUTOLAUNCH_MANUAL) : + OSD_MESSAGE_STR(OSD_MSG_AUTOLAUNCH)); const char *launchStateMessage = fixedWingLaunchStateMessage(); if (launchStateMessage) { - messages[messageCount++] = launchStateMessage; + ADD_MSG(launchStateMessage); } } else if (FLIGHT_MODE(SOARING_MODE)) { - messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_NAV_SOARING); + ADD_MSG(OSD_MESSAGE_STR(OSD_MSG_NAV_SOARING)); } else if (isFwAutoModeActive(BOXAUTOTUNE)) { - messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_AUTOTUNE); + ADD_MSG(OSD_MESSAGE_STR(OSD_MSG_AUTOTUNE)); if (FLIGHT_MODE(MANUAL_MODE)) { - messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_AUTOTUNE_ACRO); + ADD_MSG(OSD_MESSAGE_STR(OSD_MSG_AUTOTUNE_ACRO)); } } else if (isFwAutoModeActive(BOXAUTOTRIM) && !feature(FEATURE_FW_AUTOTRIM)) { - messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_AUTOTRIM); + ADD_MSG(OSD_MESSAGE_STR(OSD_MSG_AUTOTRIM)); } else if (isFixedWingLevelTrimActive()) { - messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_AUTOLEVEL); + ADD_MSG(OSD_MESSAGE_STR(OSD_MSG_AUTOLEVEL)); } if (IS_RC_MODE_ACTIVE(BOXANGLEHOLD)) { int8_t navAngleHoldAxis = navCheckActiveAngleHoldAxis(); if (isAngleHoldLevel()) { - messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_ANGLEHOLD_LEVEL); + ADD_MSG(OSD_MESSAGE_STR(OSD_MSG_ANGLEHOLD_LEVEL)); } else if (navAngleHoldAxis == FD_ROLL) { - messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_ANGLEHOLD_ROLL); + ADD_MSG(OSD_MESSAGE_STR(OSD_MSG_ANGLEHOLD_ROLL)); } else if (navAngleHoldAxis == FD_PITCH) { - messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_ANGLEHOLD_PITCH); + ADD_MSG(OSD_MESSAGE_STR(OSD_MSG_ANGLEHOLD_PITCH)); } } @@ -6318,16 +6318,16 @@ textAttributes_t osdGetSystemMessage(char *buff, size_t buff_size, bool isCenter } else { strcpy(messageBuf, "(HOLD)"); } - messages[messageCount++] = messageBuf; + ADD_MSG(messageBuf); } else if (FLIGHT_MODE(HEADFREE_MODE)) { - messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_HEADFREE); + ADD_MSG(OSD_MESSAGE_STR(OSD_MSG_HEADFREE)); } if (FLIGHT_MODE(NAV_ALTHOLD_MODE) && !navigationRequiresAngleMode()) { /* If ALTHOLD is separately enabled for multirotor together with ANGL/HORIZON/ACRO modes * then ANGL/HORIZON/ACRO are indicated by the OSD_FLYMODE field. * In this case indicate ALTHOLD is active via a system message */ - messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_ALTITUDE_HOLD); + ADD_MSG(OSD_MESSAGE_STR(OSD_MSG_ALTITUDE_HOLD)); } } } @@ -6342,44 +6342,44 @@ textAttributes_t osdGetSystemMessage(char *buff, size_t buff_size, bool isCenter messageBuf[ii] = sl_toupper(messageBuf[ii]); } invertedInfoMessage = messageBuf; - messages[messageCount++] = invertedInfoMessage; + ADD_MSG(invertedInfoMessage); invertedInfoMessage = OSD_MESSAGE_STR(OSD_MSG_INVALID_SETTING); - messages[messageCount++] = invertedInfoMessage; + ADD_MSG(invertedInfoMessage); } else { invertedInfoMessage = OSD_MESSAGE_STR(OSD_MSG_UNABLE_ARM); - messages[messageCount++] = invertedInfoMessage; + ADD_MSG(invertedInfoMessage); // Show the reason for not arming - messages[messageCount++] = osdArmingDisabledReasonMessage(); + ADD_MSG(osdArmingDisabledReasonMessage()); } } else if (!ARMING_FLAG(ARMED)) { if (isWaypointListValid()) { - messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_WP_MISSION_LOADED); + ADD_MSG(OSD_MESSAGE_STR(OSD_MSG_WP_MISSION_LOADED)); } } /* Messages that are shown regardless of Arming state */ /* ADDS MAXIMUM OF 2 MESSAGES TO TOTAL NORMALLY, 1 MESSAGE DURING FAILSAFE */ if (posControl.flags.wpMissionPlannerActive && !FLIGHT_MODE(FAILSAFE_MODE)) { - messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_MISSION_PLANNER); + ADD_MSG(OSD_MESSAGE_STR(OSD_MSG_MISSION_PLANNER)); } // The following has been commented out as it will be added in #9688 // uint16_t rearmMs = (emergInflightRearmEnabled()) ? emergencyInFlightRearmTimeMS() : 0; if (savingSettings == true) { - messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_SAVING_SETTNGS); + ADD_MSG(OSD_MESSAGE_STR(OSD_MSG_SAVING_SETTNGS)); /*} else if (rearmMs > 0) { // Show rearming time if settings not actively being saved. Ignore the settings saved message if rearm available. char emReArmMsg[23]; tfp_sprintf(emReArmMsg, "** REARM PERIOD: "); tfp_sprintf(emReArmMsg + strlen(emReArmMsg), "%02d", (uint8_t)MS2S(rearmMs)); strcat(emReArmMsg, " **\0"); - messages[messageCount++] = OSD_MESSAGE_STR(emReArmMsg);*/ + ADD_MSG(OSD_MESSAGE_STR(emReArmMsg));*/ } else if (notify_settings_saved > 0) { if (millis() > notify_settings_saved) { notify_settings_saved = 0; } else { - messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_SETTINGS_SAVED); + ADD_MSG(OSD_MESSAGE_STR(OSD_MSG_SETTINGS_SAVED)); } } @@ -6402,6 +6402,7 @@ textAttributes_t osdGetSystemMessage(char *buff, size_t buff_size, bool isCenter osdFormatMessage(buff, buff_size, message, isCenteredText); } + #undef ADD_MSG return elemAttr; } @@ -6526,6 +6527,7 @@ static textAttributes_t osdGetMultiFunctionMessage(char *buff) /* --- WARNINGS --- */ const char *messages[7]; uint8_t messageCount = 0; + #define ADD_MSG(msg) if (messageCount < ARRAYLEN(messages)) messages[messageCount++] = (msg) bool warningCondition = false; warningsCount = 0; uint8_t warningFlagID = 1; @@ -6534,7 +6536,7 @@ static textAttributes_t osdGetMultiFunctionMessage(char *buff) const batteryState_e batteryState = getBatteryState(); warningCondition = batteryState == BATTERY_CRITICAL || batteryState == BATTERY_WARNING; if (osdCheckWarning(warningCondition, warningFlagID, &warningsCount)) { - messages[messageCount++] = batteryState == BATTERY_CRITICAL ? "BATT EMPTY" : "BATT LOW !"; + ADD_MSG(batteryState == BATTERY_CRITICAL ? "BATT EMPTY" : "BATT LOW !"); } #if defined(USE_GPS) @@ -6542,7 +6544,7 @@ static textAttributes_t osdGetMultiFunctionMessage(char *buff) if (feature(FEATURE_GPS)) { if (osdCheckWarning(!STATE(GPS_FIX), warningFlagID <<= 1, &warningsCount)) { bool gpsFailed = getHwGPSStatus() == HW_SENSOR_UNAVAILABLE; - messages[messageCount++] = gpsFailed ? "GPS FAILED" : "NO GPS FIX"; + ADD_MSG(gpsFailed ? "GPS FAILED" : "NO GPS FIX"); } } @@ -6550,12 +6552,12 @@ static textAttributes_t osdGetMultiFunctionMessage(char *buff) warningCondition = NAV_Status.state == MW_NAV_STATE_RTH_ENROUTE && !posControl.flags.rthTrackbackActive && (posControl.homeDistance - posControl.rthSanityChecker.minimalDistanceToHome) > 20000; if (osdCheckWarning(warningCondition, warningFlagID <<= 1, &warningsCount)) { - messages[messageCount++] = "RTH SANITY"; + ADD_MSG("RTH SANITY"); } // Altitude sanity (warning if significant mismatch between estimated and GPS altitude) if (osdCheckWarning(posControl.flags.gpsCfEstimatedAltitudeMismatch, warningFlagID <<= 1, &warningsCount)) { - messages[messageCount++] = "ALT SANITY"; + ADD_MSG("ALT SANITY"); } #endif @@ -6564,7 +6566,7 @@ static textAttributes_t osdGetMultiFunctionMessage(char *buff) if (requestedSensors[SENSOR_INDEX_MAG] != MAG_NONE) { hardwareSensorStatus_e magStatus = getHwCompassStatus(); if (osdCheckWarning(magStatus == HW_SENSOR_UNAVAILABLE || magStatus == HW_SENSOR_UNHEALTHY, warningFlagID <<= 1, &warningsCount)) { - messages[messageCount++] = "MAG FAILED"; + ADD_MSG("MAG FAILED"); } } #endif @@ -6573,7 +6575,7 @@ static textAttributes_t osdGetMultiFunctionMessage(char *buff) // Pitot sensor validation failure (blocked/failed pitot tube) if (sensors(SENSOR_PITOT) && detectedSensors[SENSOR_INDEX_PITOT] != PITOT_VIRTUAL) { if (osdCheckWarning(pitotHasFailed(), warningFlagID <<= 1, &warningsCount)) { - messages[messageCount++] = "PITOT FAIL"; + ADD_MSG("PITOT FAIL"); } } #endif @@ -6582,12 +6584,12 @@ static textAttributes_t osdGetMultiFunctionMessage(char *buff) // const float vibrationLevel = accGetVibrationLevel(); // warningCondition = vibrationLevel > 1.5f; // if (osdCheckWarning(warningCondition, warningFlagID <<= 1, &warningsCount)) { - // messages[messageCount++] = vibrationLevel > 2.5f ? "BAD VIBRTN" : "VIBRATION!"; + // ADD_MSG(vibrationLevel > 2.5f ? "BAD VIBRTN" : "VIBRATION!"); // } #ifdef USE_DEV_TOOLS if (osdCheckWarning(systemConfig()->groundTestMode, warningFlagID <<= 1, &warningsCount)) { - messages[messageCount++] = "GRD TEST !"; + ADD_MSG("GRD TEST !"); } #endif @@ -6600,6 +6602,7 @@ static textAttributes_t osdGetMultiFunctionMessage(char *buff) tfp_sprintf(buff + 1, "%u ", warningsCount); } + #undef ADD_MSG return elemAttr; } From 83079aa64e35b8e08a1fd5bb7e17a80d445ecd9f Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Wed, 25 Feb 2026 14:21:38 -0600 Subject: [PATCH 05/67] Use do...while(0) in ADD_MSG macro to prevent dangling-else bugs --- src/main/io/osd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/io/osd.c b/src/main/io/osd.c index 722648d10fd..01604d12868 100644 --- a/src/main/io/osd.c +++ b/src/main/io/osd.c @@ -6140,7 +6140,7 @@ textAttributes_t osdGetSystemMessage(char *buff, size_t buff_size, bool isCenter /* Messages array - use ADD_MSG() for bounds-safe access. */ const char *messages[7]; unsigned messageCount = 0; - #define ADD_MSG(msg) if (messageCount < ARRAYLEN(messages)) messages[messageCount++] = (msg) + #define ADD_MSG(msg) do { if (messageCount < ARRAYLEN(messages)) messages[messageCount++] = (msg); } while(0) const char *failsafeInfoMessage = NULL; const char *invertedInfoMessage = NULL; @@ -6527,7 +6527,7 @@ static textAttributes_t osdGetMultiFunctionMessage(char *buff) /* --- WARNINGS --- */ const char *messages[7]; uint8_t messageCount = 0; - #define ADD_MSG(msg) if (messageCount < ARRAYLEN(messages)) messages[messageCount++] = (msg) + #define ADD_MSG(msg) do { if (messageCount < ARRAYLEN(messages)) messages[messageCount++] = (msg); } while(0) bool warningCondition = false; warningsCount = 0; uint8_t warningFlagID = 1; From 2f2092709b341a60377bdb15c5f9e00ccaf6bf5f Mon Sep 17 00:00:00 2001 From: breadoven <56191411+breadoven@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:04:14 +0000 Subject: [PATCH 06/67] Issue #10830 fixes --- src/main/io/osd.c | 140 ++++++++++--------- src/main/io/osd.h | 2 + src/main/navigation/navigation_multicopter.c | 13 +- 3 files changed, 85 insertions(+), 70 deletions(-) diff --git a/src/main/io/osd.c b/src/main/io/osd.c index f4fe19a4040..300c3e2ef57 100644 --- a/src/main/io/osd.c +++ b/src/main/io/osd.c @@ -6139,8 +6139,8 @@ textAttributes_t osdGetSystemMessage(char *buff, size_t buff_size, bool isCenter char messageBuf[MAX(SETTING_MAX_NAME_LENGTH, OSD_MESSAGE_LENGTH + 1)]; /* WARNING: ensure number of messages returned does not exceed messages array size - * Messages array set 1 larger than maximum expected message count of 6 */ - const char *messages[7]; + * Messages array set 1 larger than maximum expected message count of 7 */ + const char *messages[8]; unsigned messageCount = 0; const char *failsafeInfoMessage = NULL; @@ -6213,66 +6213,66 @@ textAttributes_t osdGetSystemMessage(char *buff, size_t buff_size, bool isCenter } else if (STATE(LANDING_DETECTED)) { messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_LANDED); } else { + /* Messages shown only when Failsafe, WP, RTH or Emergency Landing not active and landed state inactive */ + /* ADDS MAXIMUM OF 5 MESSAGES TO TOTAL */ #ifdef USE_GEOZONE - char buf[12], buf1[12]; - switch (geozone.messageState) { - case GEOZONE_MESSAGE_STATE_NFZ: - messages[messageCount++] = OSD_MSG_NFZ; - break; - case GEOZONE_MESSAGE_STATE_LEAVING_FZ: - osdFormatDistanceSymbol(buf, geozone.distanceToZoneBorder3d, 0, 3); - tfp_sprintf(messageBuf, OSD_MSG_LEAVING_FZ, buf); - messages[messageCount++] = messageBuf; - break; - case GEOZONE_MESSAGE_STATE_OUTSIDE_FZ: - messages[messageCount++] = OSD_MSG_OUTSIDE_FZ; - break; - case GEOZONE_MESSAGE_STATE_ENTERING_NFZ: - osdFormatDistanceSymbol(buf, geozone.distanceToZoneBorder3d, 0, 3); - if (geozone.zoneInfo == INT32_MAX) { - tfp_sprintf(buf1, "%s%c", "INF", SYM_ALT_M); - } else { - osdFormatAltitudeSymbol(buf1, geozone.zoneInfo); - } - tfp_sprintf(messageBuf, OSD_MSG_ENTERING_NFZ, buf, buf1); - messages[messageCount++] = messageBuf; - break; - case GEOZONE_MESSAGE_STATE_AVOIDING_FB: - messages[messageCount++] = OSD_MSG_AVOIDING_FB; - if (!posControl.sendTo.lockSticks) { - messages[messageCount++] = OSD_MSG_MOVE_STICKS; - } - break; - case GEOZONE_MESSAGE_STATE_RETURN_TO_ZONE: - messages[messageCount++] = OSD_MSG_RETURN_TO_ZONE; - if (!posControl.sendTo.lockSticks) { - messages[messageCount++] = OSD_MSG_MOVE_STICKS; - } - break; - case GEOZONE_MESSAGE_STATE_AVOIDING_ALTITUDE_BREACH: - messages[messageCount++] = OSD_MSG_AVOIDING_ALT_BREACH; - if (!posControl.sendTo.lockSticks) { - messages[messageCount++] = OSD_MSG_MOVE_STICKS; - } - break; - case GEOZONE_MESSAGE_STATE_FLYOUT_NFZ: - messages[messageCount++] = OSD_MSG_FLYOUT_NFZ; - if (!posControl.sendTo.lockSticks) { - messages[messageCount++] = OSD_MSG_MOVE_STICKS; - } - break; - case GEOZONE_MESSAGE_STATE_POS_HOLD: - messages[messageCount++] = OSD_MSG_AVOIDING_FB; - if (!geozone.sticksLocked) { - messages[messageCount++] = OSD_MSG_MOVE_STICKS; - } - break; - case GEOZONE_MESSAGE_STATE_NONE: - break; - } + char buf[12], buf1[12]; + switch (geozone.messageState) { /* ADDS MAXIMUM OF 2 MESSAGES TO TOTAL */ + case GEOZONE_MESSAGE_STATE_NFZ: + messages[messageCount++] = OSD_MSG_NFZ; + break; + case GEOZONE_MESSAGE_STATE_LEAVING_FZ: + osdFormatDistanceSymbol(buf, geozone.distanceToZoneBorder3d, 0, 3); + tfp_sprintf(messageBuf, OSD_MSG_LEAVING_FZ, buf); + messages[messageCount++] = messageBuf; + break; + case GEOZONE_MESSAGE_STATE_OUTSIDE_FZ: + messages[messageCount++] = OSD_MSG_OUTSIDE_FZ; + break; + case GEOZONE_MESSAGE_STATE_ENTERING_NFZ: + osdFormatDistanceSymbol(buf, geozone.distanceToZoneBorder3d, 0, 3); + if (geozone.zoneInfo == INT32_MAX) { + tfp_sprintf(buf1, "%s%c", "INF", SYM_ALT_M); + } else { + osdFormatAltitudeSymbol(buf1, geozone.zoneInfo); + } + tfp_sprintf(messageBuf, OSD_MSG_ENTERING_NFZ, buf, buf1); + messages[messageCount++] = messageBuf; + break; + case GEOZONE_MESSAGE_STATE_AVOIDING_FB: + messages[messageCount++] = OSD_MSG_AVOIDING_FB; + if (!posControl.sendTo.lockSticks) { + messages[messageCount++] = OSD_MSG_MOVE_STICKS; + } + break; + case GEOZONE_MESSAGE_STATE_RETURN_TO_ZONE: + messages[messageCount++] = OSD_MSG_RETURN_TO_ZONE; + if (!posControl.sendTo.lockSticks) { + messages[messageCount++] = OSD_MSG_MOVE_STICKS; + } + break; + case GEOZONE_MESSAGE_STATE_AVOIDING_ALTITUDE_BREACH: + messages[messageCount++] = OSD_MSG_AVOIDING_ALT_BREACH; + if (!posControl.sendTo.lockSticks) { + messages[messageCount++] = OSD_MSG_MOVE_STICKS; + } + break; + case GEOZONE_MESSAGE_STATE_FLYOUT_NFZ: + messages[messageCount++] = OSD_MSG_FLYOUT_NFZ; + if (!posControl.sendTo.lockSticks) { + messages[messageCount++] = OSD_MSG_MOVE_STICKS; + } + break; + case GEOZONE_MESSAGE_STATE_POS_HOLD: + messages[messageCount++] = OSD_MSG_AVOIDING_FB; + if (!geozone.sticksLocked) { + messages[messageCount++] = OSD_MSG_MOVE_STICKS; + } + break; + case GEOZONE_MESSAGE_STATE_NONE: + break; + } #endif - /* Messages shown only when Failsafe, WP, RTH or Emergency Landing not active and landed state inactive */ - /* ADDS MAXIMUM OF 3 MESSAGES TO TOTAL */ if (STATE(AIRPLANE)) { /* ADDS MAXIMUM OF 3 MESSAGES TO TOTAL */ #ifdef USE_FW_AUTOLAND if (canFwLandingBeCancelled()) { @@ -6309,7 +6309,6 @@ textAttributes_t osdGetSystemMessage(char *buff, size_t buff_size, bool isCenter messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_ANGLEHOLD_PITCH); } } - } else if (STATE(MULTIROTOR)) { /* ADDS MAXIMUM OF 2 MESSAGES TO TOTAL */ if (FLIGHT_MODE(NAV_COURSE_HOLD_MODE)) { if (posControl.cruise.multicopterSpeed >= 50.0f) { @@ -6323,12 +6322,21 @@ textAttributes_t osdGetSystemMessage(char *buff, size_t buff_size, bool isCenter } else if (FLIGHT_MODE(HEADFREE_MODE)) { messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_HEADFREE); } - if (FLIGHT_MODE(NAV_ALTHOLD_MODE) && !navigationRequiresAngleMode()) { - /* If ALTHOLD is separately enabled for multirotor together with ANGL/HORIZON/ACRO modes - * then ANGL/HORIZON/ACRO are indicated by the OSD_FLYMODE field. - * In this case indicate ALTHOLD is active via a system message */ - messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_ALTITUDE_HOLD); + if (FLIGHT_MODE(NAV_ALTHOLD_MODE)) { + if (posControl.flags.isTerrainFollowEnabled) { + if (posControl.flags.estAglStatus == EST_TRUSTED) { + messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_SURFACE_OK); + } else { + messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_SURFACE_BAD); + } + } else if (!navigationRequiresAngleMode()) { + /* If ALTHOLD is separately enabled for multirotor together with ANGL/HORIZON/ACRO modes + * then ANGL/HORIZON/ACRO are indicated by the OSD_FLYMODE field. + * In this case indicate ALTHOLD is active via a system message */ + + messages[messageCount++] = OSD_MESSAGE_STR(OSD_MSG_ALTITUDE_HOLD); + } } } } diff --git a/src/main/io/osd.h b/src/main/io/osd.h index 88240ff84c0..7a8ec3cb682 100644 --- a/src/main/io/osd.h +++ b/src/main/io/osd.h @@ -109,6 +109,8 @@ #define OSD_MSG_AUTOLAUNCH "AUTOLAUNCH" #define OSD_MSG_AUTOLAUNCH_MANUAL "AUTOLAUNCH (MANUAL)" #define OSD_MSG_ALTITUDE_HOLD "(ALTITUDE HOLD)" +#define OSD_MSG_SURFACE_OK "(SURFACE)" +#define OSD_MSG_SURFACE_BAD "(!SURFACE INOP!)" #define OSD_MSG_AUTOTRIM "(AUTOTRIM)" #define OSD_MSG_AUTOTUNE "(AUTOTUNE)" #define OSD_MSG_AUTOTUNE_ACRO "SWITCH TO ACRO" diff --git a/src/main/navigation/navigation_multicopter.c b/src/main/navigation/navigation_multicopter.c index 7da742829c5..5901f1ad9e1 100644 --- a/src/main/navigation/navigation_multicopter.c +++ b/src/main/navigation/navigation_multicopter.c @@ -837,10 +837,15 @@ bool isMulticopterLandingDetected(void) /* Basic condition to start looking for landing * Detection active during Failsafe only if throttle below mid hover throttle * and WP mission not active (except landing states). - * Also active in non autonomous flight modes but only when thottle low */ - bool startCondition = (navGetCurrentStateFlags() & (NAV_CTL_LAND | NAV_CTL_EMERG)) - || (FLIGHT_MODE(FAILSAFE_MODE) && !FLIGHT_MODE(NAV_WP_MODE) && !isMulticopterThrottleAboveMidHover()) - || (!navigationIsFlyingAutonomousMode() && throttleStickIsLow()); + * Also active in non autonomous flight modes but only when thottle low. + * Throttle low detection only allowed during Surface if AGL trusted and below 10cm */ + bool throttleLowCheckAllowed = !navigationIsFlyingAutonomousMode(); + if (posControl.flags.isTerrainFollowEnabled) { + throttleLowCheckAllowed = throttleLowCheckAllowed && posControl.flags.estAglStatus == EST_TRUSTED && posControl.actualState.agl.pos.z < 10.0f; + } + bool startCondition = (navGetCurrentStateFlags() & (NAV_CTL_LAND | NAV_CTL_EMERG)) || + (FLIGHT_MODE(FAILSAFE_MODE) && !FLIGHT_MODE(NAV_WP_MODE) && !isMulticopterThrottleAboveMidHover()) || + (throttleLowCheckAllowed && throttleStickIsLow()); static timeMs_t landingDetectorStartedAt; From eb9c9ce745eaace2bb438e17c02e73d397908717 Mon Sep 17 00:00:00 2001 From: daijoubu Date: Tue, 28 Apr 2026 17:26:08 -0700 Subject: [PATCH 07/67] fix: disable bootloader variants for targets without storage backend ANYFC, CLRACINGF4AIR (V1/V2/V3), FF_F35_LIGHTNING, WINGFC, FLYINGRCF4WINGMINI_NOT_RECOMMENDED, AIRBOTF7, and OMNIBUSF7NANOV7 all lack USE_FLASHFS/USE_SDCARD in their target config. Building _bl/_for_bl/_with_bl variants for these targets produces binaries that define MSP_FIRMWARE_UPDATE but have no storage backend, making OTA firmware update silently non-functional. Add NO_BOOTLOADER to cmake/stm32.cmake (following the DISABLE_MSC pattern) and set it on each affected target. This suppresses the broken bootloader build variants while leaving open the possibility of adding proper storage support to these targets in the future. Note: bootloader variants are not built by CI (they are not added to VALID_TARGETS), so this bug was never caught by automated builds. Fixes: https://github.com/iNavFlight/inav/issues/11521 --- cmake/stm32.cmake | 4 ++-- src/main/target/AIRBOTF7/CMakeLists.txt | 4 ++-- src/main/target/ANYFC/CMakeLists.txt | 2 +- src/main/target/CLRACINGF4AIR/CMakeLists.txt | 6 +++--- src/main/target/FF_F35_LIGHTNING/CMakeLists.txt | 4 ++-- .../FLYINGRCF4WINGMINI_NOT_RECOMMENDED/CMakeLists.txt | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cmake/stm32.cmake b/cmake/stm32.cmake index 091cf31fd5d..1954093e9b9 100644 --- a/cmake/stm32.cmake +++ b/cmake/stm32.cmake @@ -285,7 +285,7 @@ function(target_stm32) cmake_parse_arguments( args # Boolean arguments - "DISABLE_MSC;BOOTLOADER" + "DISABLE_MSC;BOOTLOADER;NO_BOOTLOADER" # Single value arguments "HSE_MHZ;LINKER_SCRIPT;NAME;OPENOCD_TARGET;OPTIMIZATION;STARTUP;SVD" # Multi-value arguments @@ -377,7 +377,7 @@ function(target_stm32) setup_firmware_target(${main_target_name} ${name} ${ARGN}) - if(args_BOOTLOADER) + if(args_BOOTLOADER AND NOT args_NO_BOOTLOADER) # Bootloader for the target set(bl_suffix _bl) add_stm32_executable( diff --git a/src/main/target/AIRBOTF7/CMakeLists.txt b/src/main/target/AIRBOTF7/CMakeLists.txt index 3d0e5bf1b83..305c0d44811 100644 --- a/src/main/target/AIRBOTF7/CMakeLists.txt +++ b/src/main/target/AIRBOTF7/CMakeLists.txt @@ -1,2 +1,2 @@ -target_stm32f722xe(AIRBOTF7 SKIP_RELEASES) -target_stm32f722xe(OMNIBUSF7NANOV7 SKIP_RELEASES) +target_stm32f722xe(AIRBOTF7 SKIP_RELEASES NO_BOOTLOADER) +target_stm32f722xe(OMNIBUSF7NANOV7 SKIP_RELEASES NO_BOOTLOADER) diff --git a/src/main/target/ANYFC/CMakeLists.txt b/src/main/target/ANYFC/CMakeLists.txt index 82d68535fa8..480531d92e3 100644 --- a/src/main/target/ANYFC/CMakeLists.txt +++ b/src/main/target/ANYFC/CMakeLists.txt @@ -1 +1 @@ -target_stm32f405xg(ANYFC SKIP_RELEASES) \ No newline at end of file +target_stm32f405xg(ANYFC SKIP_RELEASES NO_BOOTLOADER) \ No newline at end of file diff --git a/src/main/target/CLRACINGF4AIR/CMakeLists.txt b/src/main/target/CLRACINGF4AIR/CMakeLists.txt index 26cc0addb43..d53bc5aff04 100644 --- a/src/main/target/CLRACINGF4AIR/CMakeLists.txt +++ b/src/main/target/CLRACINGF4AIR/CMakeLists.txt @@ -1,3 +1,3 @@ -target_stm32f405xg(CLRACINGF4AIR SKIP_RELEASES) -target_stm32f405xg(CLRACINGF4AIRV2 SKIP_RELEASES) -target_stm32f405xg(CLRACINGF4AIRV3 SKIP_RELEASES) +target_stm32f405xg(CLRACINGF4AIR SKIP_RELEASES NO_BOOTLOADER) +target_stm32f405xg(CLRACINGF4AIRV2 SKIP_RELEASES NO_BOOTLOADER) +target_stm32f405xg(CLRACINGF4AIRV3 SKIP_RELEASES NO_BOOTLOADER) diff --git a/src/main/target/FF_F35_LIGHTNING/CMakeLists.txt b/src/main/target/FF_F35_LIGHTNING/CMakeLists.txt index 15380146f56..415a69ebc51 100644 --- a/src/main/target/FF_F35_LIGHTNING/CMakeLists.txt +++ b/src/main/target/FF_F35_LIGHTNING/CMakeLists.txt @@ -1,2 +1,2 @@ -target_stm32f405xg(FF_F35_LIGHTNING) -target_stm32f405xg(WINGFC) +target_stm32f405xg(FF_F35_LIGHTNING NO_BOOTLOADER) +target_stm32f405xg(WINGFC NO_BOOTLOADER) diff --git a/src/main/target/FLYINGRCF4WINGMINI_NOT_RECOMMENDED/CMakeLists.txt b/src/main/target/FLYINGRCF4WINGMINI_NOT_RECOMMENDED/CMakeLists.txt index 6da40b8ae6d..9c9a729ef0a 100644 --- a/src/main/target/FLYINGRCF4WINGMINI_NOT_RECOMMENDED/CMakeLists.txt +++ b/src/main/target/FLYINGRCF4WINGMINI_NOT_RECOMMENDED/CMakeLists.txt @@ -1 +1 @@ -target_stm32f405xg(FLYINGRCF4WINGMINI_NOT_RECOMMENDED) +target_stm32f405xg(FLYINGRCF4WINGMINI_NOT_RECOMMENDED NO_BOOTLOADER) From 1eeb46711faea01383f2a01f8fb16efe8997bdbd Mon Sep 17 00:00:00 2001 From: breadoven <56191411+breadoven@users.noreply.github.com> Date: Thu, 14 May 2026 10:27:47 +0100 Subject: [PATCH 08/67] Update osd.c --- src/main/io/osd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/io/osd.c b/src/main/io/osd.c index ab99cbb5fa5..5e816bdd0f5 100644 --- a/src/main/io/osd.c +++ b/src/main/io/osd.c @@ -3573,7 +3573,7 @@ static bool osdDrawSingleElement(uint8_t item) if (!moreThanAh) { tfp_sprintf(buff + strlen(buff), "%c%c", SYM_MAH_MI_0, SYM_MAH_MI_1); } else { - tfp_sprintf(buff + strlen(buff), "%c", SYM_AH_MI); + tfp_sprintf(buff + strlen(buff), "%c ", SYM_AH_MI); } if (!efficiencyValid) { buff[0] = buff[1] = buff[2] = buff[3] = '-'; @@ -3587,7 +3587,7 @@ static bool osdDrawSingleElement(uint8_t item) if (!moreThanAh) { tfp_sprintf(buff + strlen(buff), "%c%c", SYM_MAH_NM_0, SYM_MAH_NM_1); } else { - tfp_sprintf(buff + strlen(buff), "%c", SYM_AH_NM); + tfp_sprintf(buff + strlen(buff), "%c ", SYM_AH_NM); } if (!efficiencyValid) { buff[0] = buff[1] = buff[2] = buff[3] = '-'; @@ -3603,7 +3603,7 @@ static bool osdDrawSingleElement(uint8_t item) if (!moreThanAh) { tfp_sprintf(buff + strlen(buff), "%c%c", SYM_MAH_KM_0, SYM_MAH_KM_1); } else { - tfp_sprintf(buff + strlen(buff), "%c", SYM_AH_KM); + tfp_sprintf(buff + strlen(buff), "%c ", SYM_AH_KM); } if (!efficiencyValid) { buff[0] = buff[1] = buff[2] = buff[3] = '-'; From 394395ec557196576d4f1c4218ca9a825357b057 Mon Sep 17 00:00:00 2001 From: breadoven <56191411+breadoven@users.noreply.github.com> Date: Sat, 16 May 2026 15:13:31 +0100 Subject: [PATCH 09/67] Update navigation_multicopter.c --- src/main/navigation/navigation_multicopter.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/navigation/navigation_multicopter.c b/src/main/navigation/navigation_multicopter.c index 5901f1ad9e1..690987ecade 100644 --- a/src/main/navigation/navigation_multicopter.c +++ b/src/main/navigation/navigation_multicopter.c @@ -130,7 +130,11 @@ bool adjustMulticopterAltitudeFromRCInput(void) updateClimbRateToAltitudeController(0, altTarget, ROC_TO_ALT_TARGET); } else { - updateClimbRateToAltitudeController(-50.0f, 0, ROC_TO_ALT_CONSTANT); + const int16_t throttleIdle = getThrottleIdleValue(); + const int16_t throttleMid = rcLookupThrottleMid(); + const int16_t descentRate = scaleRange(constrain(rcCommand[THROTTLE], throttleIdle, throttleMid), throttleIdle, throttleMid, -200, -50); + + updateClimbRateToAltitudeController(descentRate, 0, ROC_TO_ALT_CONSTANT); } // In surface tracking we always indicate that we're adjusting altitude @@ -841,7 +845,7 @@ bool isMulticopterLandingDetected(void) * Throttle low detection only allowed during Surface if AGL trusted and below 10cm */ bool throttleLowCheckAllowed = !navigationIsFlyingAutonomousMode(); if (posControl.flags.isTerrainFollowEnabled) { - throttleLowCheckAllowed = throttleLowCheckAllowed && posControl.flags.estAglStatus == EST_TRUSTED && posControl.actualState.agl.pos.z < 10.0f; + throttleLowCheckAllowed = throttleLowCheckAllowed && posControl.flags.estAglStatus == EST_TRUSTED && posControl.actualState.agl.pos.z < 50.0f; } bool startCondition = (navGetCurrentStateFlags() & (NAV_CTL_LAND | NAV_CTL_EMERG)) || (FLIGHT_MODE(FAILSAFE_MODE) && !FLIGHT_MODE(NAV_WP_MODE) && !isMulticopterThrottleAboveMidHover()) || From eee931d27fd60d170bf20e4a88f3622dedd971fb Mon Sep 17 00:00:00 2001 From: breadoven <56191411+breadoven@users.noreply.github.com> Date: Sun, 17 May 2026 11:11:23 +0100 Subject: [PATCH 10/67] fix descent below 10cm --- src/main/navigation/navigation_multicopter.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/navigation/navigation_multicopter.c b/src/main/navigation/navigation_multicopter.c index 690987ecade..37fa3f9f4cd 100644 --- a/src/main/navigation/navigation_multicopter.c +++ b/src/main/navigation/navigation_multicopter.c @@ -122,19 +122,23 @@ static void updateAltitudeThrottleController_MC(timeDelta_t deltaMicros) bool adjustMulticopterAltitudeFromRCInput(void) { if (posControl.flags.isTerrainFollowEnabled) { - const float altTarget = scaleRangef(rcCommand[THROTTLE], getThrottleIdleValue(), getMaxThrottle(), 0, navConfig()->general.max_terrain_follow_altitude); + const int16_t altTarget = scaleRange(rcCommand[THROTTLE], getThrottleIdleValue(), getMaxThrottle(), 0, navConfig()->general.max_terrain_follow_altitude); // In terrain follow mode we apply different logic for terrain control - if (posControl.flags.estAglStatus == EST_TRUSTED && altTarget > 10.0f) { + if (posControl.flags.estAglStatus == EST_TRUSTED && altTarget > 10) { // We have solid terrain sensor signal - directly map throttle to altitude updateClimbRateToAltitudeController(0, altTarget, ROC_TO_ALT_TARGET); } else { - const int16_t throttleIdle = getThrottleIdleValue(); - const int16_t throttleMid = rcLookupThrottleMid(); - const int16_t descentRate = scaleRange(constrain(rcCommand[THROTTLE], throttleIdle, throttleMid), throttleIdle, throttleMid, -200, -50); + int16_t climbRate = -50; - updateClimbRateToAltitudeController(descentRate, 0, ROC_TO_ALT_CONSTANT); + if (posControl.flags.estAglStatus != EST_TRUSTED) { + const int16_t throttleIdle = getThrottleIdleValue(); + const int16_t throttleMid = rcLookupThrottleMid(); + climbRate = scaleRange(constrain(rcCommand[THROTTLE], throttleIdle, throttleMid), throttleIdle, throttleMid, -200, -50); + } + + updateClimbRateToAltitudeController(climbRate, 0, ROC_TO_ALT_CONSTANT); } // In surface tracking we always indicate that we're adjusting altitude From d53f92c9c3b9d2fc6382aa2846d5f9ae76ba4304 Mon Sep 17 00:00:00 2001 From: Sensei Date: Sun, 17 May 2026 20:56:24 -0500 Subject: [PATCH 11/67] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/main/navigation/navigation_multicopter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/navigation/navigation_multicopter.c b/src/main/navigation/navigation_multicopter.c index 37fa3f9f4cd..d0d92531cf2 100644 --- a/src/main/navigation/navigation_multicopter.c +++ b/src/main/navigation/navigation_multicopter.c @@ -845,7 +845,7 @@ bool isMulticopterLandingDetected(void) /* Basic condition to start looking for landing * Detection active during Failsafe only if throttle below mid hover throttle * and WP mission not active (except landing states). - * Also active in non autonomous flight modes but only when thottle low. + * Also active in non autonomous flight modes but only when throttle low. * Throttle low detection only allowed during Surface if AGL trusted and below 10cm */ bool throttleLowCheckAllowed = !navigationIsFlyingAutonomousMode(); if (posControl.flags.isTerrainFollowEnabled) { From e21b50a6c43982d9f25ffedc335a7d29d7f504c1 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Sun, 17 May 2026 22:31:07 -0500 Subject: [PATCH 12/67] New target: SPEDIXH743 STM32H743 based FC with dual ICM42688P gyros on SPI1/SPI4, MAX7456 OSD on SPI2, M25P16 flash on SPI3, BMP280/DPS310 baro on I2C1, 8 motors (TIM1x4, TIM8x2, TIM3x2), 8 UARTs, and PINIO for VTX and camera switching. --- src/main/target/SPEDIXH743/CMakeLists.txt | 1 + src/main/target/SPEDIXH743/config.c | 28 ++++ src/main/target/SPEDIXH743/target.c | 56 +++++++ src/main/target/SPEDIXH743/target.h | 183 ++++++++++++++++++++++ 4 files changed, 268 insertions(+) create mode 100644 src/main/target/SPEDIXH743/CMakeLists.txt create mode 100644 src/main/target/SPEDIXH743/config.c create mode 100644 src/main/target/SPEDIXH743/target.c create mode 100644 src/main/target/SPEDIXH743/target.h diff --git a/src/main/target/SPEDIXH743/CMakeLists.txt b/src/main/target/SPEDIXH743/CMakeLists.txt new file mode 100644 index 00000000000..1cb49214f88 --- /dev/null +++ b/src/main/target/SPEDIXH743/CMakeLists.txt @@ -0,0 +1 @@ +target_stm32h743xi(SPEDIXH743) diff --git a/src/main/target/SPEDIXH743/config.c b/src/main/target/SPEDIXH743/config.c new file mode 100644 index 00000000000..de3f23448bc --- /dev/null +++ b/src/main/target/SPEDIXH743/config.c @@ -0,0 +1,28 @@ +/* + * This file is part of INAV. + * + * INAV is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * INAV is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with INAV. If not, see . + */ + +#include +#include "platform.h" + +#include "fc/fc_msp_box.h" +#include "io/piniobox.h" + +void targetConfiguration(void) +{ + pinioBoxConfigMutable()->permanentId[0] = BOX_PERMANENT_ID_USER1; // PINIO1: VTX power switch + pinioBoxConfigMutable()->permanentId[1] = BOX_PERMANENT_ID_USER2; // PINIO2: Camera switch +} diff --git a/src/main/target/SPEDIXH743/target.c b/src/main/target/SPEDIXH743/target.c new file mode 100644 index 00000000000..210d45cf210 --- /dev/null +++ b/src/main/target/SPEDIXH743/target.c @@ -0,0 +1,56 @@ +/* + * This file is part of INAV. + * + * INAV is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * INAV is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with INAV. If not, see . + */ + +#include + +#include "platform.h" + +#include "drivers/bus.h" +#include "drivers/io.h" +#include "drivers/pwm_mapping.h" +#include "drivers/timer.h" +#include "drivers/pinio.h" +#include "drivers/sensor.h" + +// Gyro 1: ICM42688P on SPI1, tag=0 +BUSDEV_REGISTER_SPI_TAG(busdev_icm42688_1, DEVHW_ICM42605, ICM42605_1_SPI_BUS, ICM42605_1_CS_PIN, NONE, 0, DEVFLAGS_NONE, IMU_ICM42605_ALIGN); +// Gyro 2: ICM42688P on SPI4, tag=1 +BUSDEV_REGISTER_SPI_TAG(busdev_icm42688_2, DEVHW_ICM42605, ICM42605_2_SPI_BUS, ICM42605_2_CS_PIN, NONE, 1, DEVFLAGS_NONE, IMU_ICM42605_2_ALIGN); + +timerHardware_t timerHardware[] = { + // ---- Motors (TIM1 x4) ---- + DEF_TIM(TIM1, CH1, PE9, TIM_USE_OUTPUT_AUTO, 0, 0), // M1 + DEF_TIM(TIM1, CH2, PE11, TIM_USE_OUTPUT_AUTO, 0, 1), // M2 + DEF_TIM(TIM1, CH3, PE13, TIM_USE_OUTPUT_AUTO, 0, 2), // M3 + DEF_TIM(TIM1, CH4, PE14, TIM_USE_OUTPUT_AUTO, 0, 3), // M4 + + // ---- Motors (TIM8 x2) ---- + DEF_TIM(TIM8, CH3, PC8, TIM_USE_OUTPUT_AUTO, 0, 4), // M5 + DEF_TIM(TIM8, CH4, PC9, TIM_USE_OUTPUT_AUTO, 0, 5), // M6 + + // ---- Motors (TIM3 x2) ---- + DEF_TIM(TIM3, CH1, PA6, TIM_USE_OUTPUT_AUTO, 0, 6), // M7 + DEF_TIM(TIM3, CH2, PA7, TIM_USE_OUTPUT_AUTO, 0, 7), // M8 + + // ---- LED Strip (TIM5 CH1) ---- + DEF_TIM(TIM5, CH1, PA0, TIM_USE_LED, 0, 8), // WS2812 LED Strip + + // ---- Camera Control (PA1 / TIM15_CH1N) ---- + DEF_TIM(TIM15, CH1N, PA1, TIM_USE_ANY, 0, 0), +}; + +const int timerHardwareCount = sizeof(timerHardware) / sizeof(timerHardware[0]); diff --git a/src/main/target/SPEDIXH743/target.h b/src/main/target/SPEDIXH743/target.h new file mode 100644 index 00000000000..fe32056d844 --- /dev/null +++ b/src/main/target/SPEDIXH743/target.h @@ -0,0 +1,183 @@ +/* + * This file is part of INAV. + * + * INAV is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * INAV is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with INAV. If not, see . + */ + +#pragma once + +#define TARGET_BOARD_IDENTIFIER "SPX7" +#define USBD_PRODUCT_STRING "SPEDIXH743" + +#define USE_TARGET_CONFIG + +// *************** Indicators *************************** +#define LED0 PC4 +#define LED1 PC5 + +#define BEEPER PD14 +#define BEEPER_INVERTED + +// *************** IMU generic ************************** +#define USE_DUAL_GYRO +#define USE_TARGET_IMU_HARDWARE_DESCRIPTORS + +#define USE_IMU_ICM42605 + +// *************** SPI1 — Gyro 1 (ICM42688P) ************ +#define USE_SPI +#define USE_SPI_DEVICE_1 +#define SPI1_SCK_PIN PB3 +#define SPI1_MISO_PIN PB4 +#define SPI1_MOSI_PIN PB5 + +#define ICM42605_1_SPI_BUS BUS_SPI1 +#define ICM42605_1_CS_PIN PA15 +#define IMU_ICM42605_ALIGN CW0_DEG + +// *************** SPI4 — Gyro 2 (ICM42688P) ************ +#define USE_SPI_DEVICE_4 +#define SPI4_SCK_PIN PE2 +#define SPI4_MISO_PIN PE5 +#define SPI4_MOSI_PIN PE6 + +#define ICM42605_2_SPI_BUS BUS_SPI4 +#define ICM42605_2_CS_PIN PE3 +#define IMU_ICM42605_2_ALIGN CW0_DEG + +// *************** SPI2 — OSD (MAX7456) ***************** +#define USE_SPI_DEVICE_2 +#define SPI2_SCK_PIN PB13 +#define SPI2_MISO_PIN PB14 +#define SPI2_MOSI_PIN PB15 + +#define USE_MAX7456 +#define MAX7456_SPI_BUS BUS_SPI2 +#define MAX7456_CS_PIN PB12 + +// *************** SPI3 — Flash (M25P16 blackbox) ******* +#define USE_SPI_DEVICE_3 +#define SPI3_SCK_PIN PC10 +#define SPI3_MISO_PIN PC11 +#define SPI3_MOSI_PIN PC12 + +#define USE_FLASHFS +#define USE_FLASH_M25P16 +#define M25P16_SPI_BUS BUS_SPI3 +#define M25P16_CS_PIN PA8 + +#define ENABLE_BLACKBOX_LOGGING_ON_SPIFLASH_BY_DEFAULT + +// *************** I2C — Baro (BMP280/DPS310) *********** +#define USE_I2C +#define USE_I2C_DEVICE_1 +#define I2C1_SCL PB8 +#define I2C1_SDA PB9 + +#define USE_BARO +#define BARO_I2C_BUS BUS_I2C1 +#define USE_BARO_BMP280 +#define USE_BARO_DPS310 + +#define USE_MAG +#define MAG_I2C_BUS BUS_I2C1 +#define USE_MAG_ALL + +#define TEMPERATURE_I2C_BUS BUS_I2C1 +#define PITOT_I2C_BUS BUS_I2C1 +#define RANGEFINDER_I2C_BUS BUS_I2C1 +#define USE_RANGEFINDER + +// *************** UART ********************************* +#define USE_VCP + +#define USE_UART1 +#define UART1_TX_PIN PA9 +#define UART1_RX_PIN PB7 + +#define USE_UART2 +#define UART2_TX_PIN PD5 +#define UART2_RX_PIN PD6 + +#define USE_UART3 +#define UART3_TX_PIN PD8 +#define UART3_RX_PIN PD9 + +#define USE_UART4 +#define UART4_TX_PIN PD1 +#define UART4_RX_PIN PD0 + +#define USE_UART5 +#define UART5_TX_PIN PB6 +#define UART5_RX_PIN PD2 + +#define USE_UART6 +#define UART6_TX_PIN PC6 +#define UART6_RX_PIN PC7 + +#define USE_UART7 +#define UART7_TX_PIN PE8 +#define UART7_RX_PIN PE7 + +#define USE_UART8 +#define UART8_TX_PIN PE1 +#define UART8_RX_PIN PE0 + +// VCP + 8 UARTs +#define SERIAL_PORT_COUNT 9 + +#define DEFAULT_RX_TYPE RX_TYPE_SERIAL +#define SERIALRX_PROVIDER SERIALRX_CRSF +#define SERIALRX_UART SERIAL_PORT_USART6 + +// *************** ADC ********************************** +#define USE_ADC +#define ADC_INSTANCE ADC1 + +#define ADC_CHANNEL_1_PIN PC1 // VBAT +#define ADC_CHANNEL_2_PIN PC2 // Current + +#define VBAT_ADC_CHANNEL ADC_CHN_1 +#define CURRENT_METER_ADC_CHANNEL ADC_CHN_2 + +#define VBAT_SCALE_DEFAULT 110 + +// *************** PINIO ******************************** +#define USE_PINIO +#define USE_PINIOBOX +#define PINIO1_PIN PA2 // VTX power switch +#define PINIO2_PIN PA3 // Camera switch + +// *************** LED STRIP **************************** +#define USE_LED_STRIP +#define WS2811_PIN PA0 + +// *************** Features / defaults ****************** +#define DEFAULT_FEATURES (FEATURE_OSD | FEATURE_TELEMETRY | FEATURE_CURRENT_METER | FEATURE_VBAT | FEATURE_TX_PROF_SEL | FEATURE_BLACKBOX) +#define DEFAULT_VOLTAGE_METER_SOURCE VOLTAGE_METER_ADC +#define DEFAULT_CURRENT_METER_SOURCE CURRENT_METER_ADC + +#define USE_SERIAL_4WAY_BLHELI_INTERFACE +#define USE_DSHOT +#define USE_DSHOT_DMAR +#define USE_ESC_SENSOR + +#define MAX_PWM_OUTPUT_PORTS 8 + +// *************** Pin availability ********************* +#define TARGET_IO_PORTA 0xffff +#define TARGET_IO_PORTB 0xffff +#define TARGET_IO_PORTC 0xffff +#define TARGET_IO_PORTD 0xffff +#define TARGET_IO_PORTE 0xffff From 3533d16af7a6b5ec2c94cdebae1642a2b7cfdb52 Mon Sep 17 00:00:00 2001 From: breadoven <56191411+breadoven@users.noreply.github.com> Date: Mon, 18 May 2026 10:24:12 +0100 Subject: [PATCH 13/67] correct typo --- src/main/navigation/navigation_multicopter.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/navigation/navigation_multicopter.c b/src/main/navigation/navigation_multicopter.c index d0d92531cf2..a83a3541681 100644 --- a/src/main/navigation/navigation_multicopter.c +++ b/src/main/navigation/navigation_multicopter.c @@ -846,11 +846,14 @@ bool isMulticopterLandingDetected(void) * Detection active during Failsafe only if throttle below mid hover throttle * and WP mission not active (except landing states). * Also active in non autonomous flight modes but only when throttle low. - * Throttle low detection only allowed during Surface if AGL trusted and below 10cm */ + * Throttle low detection only allowed during Surface if AGL trusted and below 50cm */ + bool throttleLowCheckAllowed = !navigationIsFlyingAutonomousMode(); + if (posControl.flags.isTerrainFollowEnabled) { throttleLowCheckAllowed = throttleLowCheckAllowed && posControl.flags.estAglStatus == EST_TRUSTED && posControl.actualState.agl.pos.z < 50.0f; } + bool startCondition = (navGetCurrentStateFlags() & (NAV_CTL_LAND | NAV_CTL_EMERG)) || (FLIGHT_MODE(FAILSAFE_MODE) && !FLIGHT_MODE(NAV_WP_MODE) && !isMulticopterThrottleAboveMidHover()) || (throttleLowCheckAllowed && throttleStickIsLow()); From 4ad4a8c1f270d1287062005ddbf66fc9b774dca6 Mon Sep 17 00:00:00 2001 From: Sensei Date: Tue, 19 May 2026 21:42:24 -0500 Subject: [PATCH 14/67] Fix typo in bulding / building Corrected the spelling of 'Bulding' to 'Building' in the documentation. --- docs/development/Building in Linux.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/development/Building in Linux.md b/docs/development/Building in Linux.md index 944872d530c..b26a66f6ac5 100644 --- a/docs/development/Building in Linux.md +++ b/docs/development/Building in Linux.md @@ -92,7 +92,7 @@ cmake -DCOMPILER_VERSION_CHECK=OFF .. `cmake` will generate a number of files in your `build` directory, including a cache of generated build settings `CMakeCache.txt` and a `Makefile`. -## Bulding the firmware +## Building the firmware Once `cmake` has generated the `build/Makefile`, this `Makfile` (with `make`) is used to build the firmware, again from the `build` directory. It is not necessary to re-run `cmake` unless the INAV cmake configuration is changed (i.e. a new release) or you wish to swap between the ARM SDK compiler and a distro or other external compiler. From 4a937f477d4ec171e5b479d713ea48e406e4445a Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Wed, 20 May 2026 15:12:07 -0500 Subject: [PATCH 15/67] fix: prevent LTO from breaking settingGet/settingGetIndex round-trip Under INTERPROCEDURAL_OPTIMIZATION, the compiler inlines settingGet and settingGetIndex with divergent static settingsTable base addresses, causing MSPV2_SETTING to return wrong data for some settings while MSP2_COMMON_SETTING_INFO (name lookup) is correct. --- src/main/fc/settings.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/fc/settings.c b/src/main/fc/settings.c index 912c38362bf..dda81b61491 100644 --- a/src/main/fc/settings.c +++ b/src/main/fc/settings.c @@ -124,12 +124,14 @@ const setting_t *settingFind(const char *name) return NULL; } -const setting_t *settingGet(unsigned index) +// noinline: LTO inlines these with divergent settingsTable base addresses, +// breaking the settingGetIndex -> settingGet round-trip. +__attribute__((noinline)) const setting_t *settingGet(unsigned index) { return index < SETTINGS_TABLE_COUNT ? &settingsTable[index] : NULL; } -unsigned settingGetIndex(const setting_t *val) +__attribute__((noinline)) unsigned settingGetIndex(const setting_t *val) { return val - settingsTable; } From ffcb7c8afbce75f5a209ebc245ef13a3c3f0957e Mon Sep 17 00:00:00 2001 From: breadoven <56191411+breadoven@users.noreply.github.com> Date: Thu, 21 May 2026 11:01:11 +0100 Subject: [PATCH 16/67] fixes --- src/main/io/osd.h | 2 +- src/main/navigation/navigation_multicopter.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/io/osd.h b/src/main/io/osd.h index 7a8ec3cb682..e55aeb22461 100644 --- a/src/main/io/osd.h +++ b/src/main/io/osd.h @@ -110,7 +110,7 @@ #define OSD_MSG_AUTOLAUNCH_MANUAL "AUTOLAUNCH (MANUAL)" #define OSD_MSG_ALTITUDE_HOLD "(ALTITUDE HOLD)" #define OSD_MSG_SURFACE_OK "(SURFACE)" -#define OSD_MSG_SURFACE_BAD "(!SURFACE INOP!)" +#define OSD_MSG_SURFACE_BAD "(!SURFACE UNRELIABLE!)" #define OSD_MSG_AUTOTRIM "(AUTOTRIM)" #define OSD_MSG_AUTOTUNE "(AUTOTUNE)" #define OSD_MSG_AUTOTUNE_ACRO "SWITCH TO ACRO" diff --git a/src/main/navigation/navigation_multicopter.c b/src/main/navigation/navigation_multicopter.c index a83a3541681..5d4b7a5b425 100644 --- a/src/main/navigation/navigation_multicopter.c +++ b/src/main/navigation/navigation_multicopter.c @@ -132,6 +132,7 @@ bool adjustMulticopterAltitudeFromRCInput(void) else { int16_t climbRate = -50; + // Increase descent rate when throttle stick below mid from min rate of 0.5m/s up to max 2 m/s if (posControl.flags.estAglStatus != EST_TRUSTED) { const int16_t throttleIdle = getThrottleIdleValue(); const int16_t throttleMid = rcLookupThrottleMid(); From 4bec42cbc2e4867352b627d5aa7d2718f65c55d8 Mon Sep 17 00:00:00 2001 From: breadoven <56191411+breadoven@users.noreply.github.com> Date: Thu, 21 May 2026 11:14:48 +0100 Subject: [PATCH 17/67] Update osd.c --- src/main/io/osd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/io/osd.c b/src/main/io/osd.c index 55bca838f34..ecaaf536554 100644 --- a/src/main/io/osd.c +++ b/src/main/io/osd.c @@ -3609,7 +3609,7 @@ static bool osdDrawSingleElement(uint8_t item) if (!moreThanAh) { osdWriteChar2(buff + strlen(buff), SYM_MAH_MI_0, SYM_MAH_MI_1); } else { - osdWriteChar(buff + strlen(buff), SYM_AH_MI); + osdWriteChar2(buff + strlen(buff), SYM_AH_MI, SYM_BLANK); } if (!efficiencyValid) { buff[0] = buff[1] = buff[2] = buff[3] = '-'; @@ -3623,7 +3623,7 @@ static bool osdDrawSingleElement(uint8_t item) if (!moreThanAh) { osdWriteChar2(buff + strlen(buff), SYM_MAH_NM_0, SYM_MAH_NM_1); } else { - osdWriteChar(buff + strlen(buff), SYM_AH_NM); + osdWriteChar2(buff + strlen(buff), SYM_AH_NM, SYM_BLANK); } if (!efficiencyValid) { buff[0] = buff[1] = buff[2] = buff[3] = '-'; @@ -3639,7 +3639,7 @@ static bool osdDrawSingleElement(uint8_t item) if (!moreThanAh) { osdWriteChar2(buff + strlen(buff), SYM_MAH_KM_0, SYM_MAH_KM_1); } else { - osdWriteChar(buff + strlen(buff), SYM_AH_KM); + osdWriteChar2(buff + strlen(buff), SYM_AH_KM, SYM_BLANK); } if (!efficiencyValid) { buff[0] = buff[1] = buff[2] = buff[3] = '-'; From 44c5934ffef7013ad7b1fd42c4b004cfb614d38e Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Thu, 21 May 2026 16:15:00 -0500 Subject: [PATCH 18/67] fix: address code review issues in LTO noinline fix - Improve comment accuracy: describe observed behavior rather than the inferred mechanism; note why raw attribute is used instead of NOINLINE macro (NOINLINE is empty on non-F7/H7 targets but bug affects all LTO release builds) - Add noinline to settingGetPgn which has identical pointer arithmetic against settingsTable and is called from settingGetValuePointer - Add brief comments above settingGetIndex and settingGetPgn pointing back to the main explanation on settingGet --- src/main/fc/settings.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/fc/settings.c b/src/main/fc/settings.c index dda81b61491..e3be177fb16 100644 --- a/src/main/fc/settings.c +++ b/src/main/fc/settings.c @@ -124,13 +124,19 @@ const setting_t *settingFind(const char *name) return NULL; } -// noinline: LTO inlines these with divergent settingsTable base addresses, -// breaking the settingGetIndex -> settingGet round-trip. +// Under release builds (LTO enabled), inlining settingGet and settingGetIndex +// at their call sites in fc_msp.c produces wrong results for the +// settingGetIndex(ptr)->settingGet(index) round-trip, causing MSPV2_SETTING +// to return incorrect byte counts for some uint16_t settings. +// Use the raw attribute rather than NOINLINE because NOINLINE expands to +// nothing on non-F7/H7 targets (common.h:29), but LTO is enabled for all +// release targets and the bug affects all of them. __attribute__((noinline)) const setting_t *settingGet(unsigned index) { return index < SETTINGS_TABLE_COUNT ? &settingsTable[index] : NULL; } +// noinline: same reason as settingGet above __attribute__((noinline)) unsigned settingGetIndex(const setting_t *val) { return val - settingsTable; @@ -217,7 +223,9 @@ size_t settingGetValueSize(const setting_t *val) return 0; // Unreachable } -pgn_t settingGetPgn(const setting_t *val) +// noinline: same reason as settingGet above; also does pointer arithmetic +// against settingsTable and is called from settingGetValuePointer. +__attribute__((noinline)) pgn_t settingGetPgn(const setting_t *val) { uint16_t pos = val - (const setting_t *)settingsTable; uint16_t acc = 0; From c629c692e8e127b30e4fb0d21ebb748edd09a0e1 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Fri, 22 May 2026 10:04:00 -0500 Subject: [PATCH 19/67] =?UTF-8?q?SPEDIXH743:=20fix=20ADC=20=E2=80=94=20use?= =?UTF-8?q?=20ADC3=20for=20all=20PC-port=20pins?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PC-port pins on STM32H743 require ADC3. PC1 (VBat, ADC123_INP11) and PC2 (Current, ADC3_INP0) both work on ADC3; ADC1 init silently fails for these pins. Same fix applied to MAMBAH743, FOXEERH743. --- src/main/target/SPEDIXH743/target.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/target/SPEDIXH743/target.h b/src/main/target/SPEDIXH743/target.h index fe32056d844..26ddc73b1e2 100644 --- a/src/main/target/SPEDIXH743/target.h +++ b/src/main/target/SPEDIXH743/target.h @@ -143,10 +143,10 @@ // *************** ADC ********************************** #define USE_ADC -#define ADC_INSTANCE ADC1 +#define ADC_INSTANCE ADC3 // PC-port pins require ADC3 on H743 -#define ADC_CHANNEL_1_PIN PC1 // VBAT -#define ADC_CHANNEL_2_PIN PC2 // Current +#define ADC_CHANNEL_1_PIN PC1 // VBat — ADC123_INP11 (works on ADC3) +#define ADC_CHANNEL_2_PIN PC2 // Current — ADC3_INP0 #define VBAT_ADC_CHANNEL ADC_CHN_1 #define CURRENT_METER_ADC_CHANNEL ADC_CHN_2 From 5023417e547b9b167199d07e7fb9682018382db1 Mon Sep 17 00:00:00 2001 From: meng9633 <3330224355@qq.com> Date: Fri, 22 May 2026 23:13:26 +0800 Subject: [PATCH 20/67] change VBAT_SCALE_DEFAULT = 2100 --- src/main/target/BLUEBERRYH743/target.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/target/BLUEBERRYH743/target.h b/src/main/target/BLUEBERRYH743/target.h index b69400d0674..fe0e7925c9f 100644 --- a/src/main/target/BLUEBERRYH743/target.h +++ b/src/main/target/BLUEBERRYH743/target.h @@ -193,6 +193,7 @@ #define WS2811_PIN PA8 #define DEFAULT_FEATURES (FEATURE_OSD | FEATURE_TELEMETRY | FEATURE_CURRENT_METER | FEATURE_VBAT | FEATURE_TX_PROF_SEL | FEATURE_BLACKBOX) +#define VBAT_SCALE_DEFAULT 2100 #define CURRENT_METER_SCALE 250 #define CURRENT_METER_OFFSET 400 From 0f061d371d00e783f8a27897ff1e2b49f083ad73 Mon Sep 17 00:00:00 2001 From: breadoven <56191411+breadoven@users.noreply.github.com> Date: Fri, 22 May 2026 18:31:34 +0100 Subject: [PATCH 21/67] Update navigation_pos_estimator_agl.c --- .../navigation/navigation_pos_estimator_agl.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/main/navigation/navigation_pos_estimator_agl.c b/src/main/navigation/navigation_pos_estimator_agl.c index 38788e20a77..3e9021abaf6 100644 --- a/src/main/navigation/navigation_pos_estimator_agl.c +++ b/src/main/navigation/navigation_pos_estimator_agl.c @@ -49,24 +49,15 @@ extern navigationPosEstimator_t posEstimator; void updatePositionEstimator_SurfaceTopic(timeUs_t currentTimeUs, float newSurfaceAlt) { const float surfaceDtUs = currentTimeUs - posEstimator.surface.lastUpdateTime; - float newReliabilityMeasurement = 0; + uint8_t newReliabilityMeasurement = 0; // default to 0 for negative values, out of range or failed hardware bool surfaceMeasurementWithinRange = false; posEstimator.surface.lastUpdateTime = currentTimeUs; - if (newSurfaceAlt >= 0) { - if (newSurfaceAlt <= positionEstimationConfig()->max_surface_altitude) { - newReliabilityMeasurement = 1.0f; - surfaceMeasurementWithinRange = true; - posEstimator.surface.alt = newSurfaceAlt; - } - else { - newReliabilityMeasurement = 0.0f; - } - } - else { - // Negative values - out of range or failed hardware - newReliabilityMeasurement = 0.0f; + if (newSurfaceAlt >= 0 && newSurfaceAlt <= positionEstimationConfig()->max_surface_altitude) { + newReliabilityMeasurement = 1; + surfaceMeasurementWithinRange = true; + posEstimator.surface.alt = newSurfaceAlt; } /* Reliability is a measure of confidence of rangefinder measurement. It's increased with each valid sample and decreased with each invalid sample */ From 8471798245051ee26ca063e24fc0eea9c0cf8fe1 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Sat, 23 May 2026 01:16:09 -0500 Subject: [PATCH 22/67] SPEDIXH743: add I2C2 for magnetometer, fix VBAT_SCALE - Add USE_I2C_DEVICE_2 (PB10/PB11) for external magnetometer - Route MAG_I2C_BUS to BUS_I2C2 to match board layout - Fix VBAT_SCALE_DEFAULT (was Betaflight value, not INAV scale) Both ADC and magnetometer verified working on hardware. --- src/main/target/SPEDIXH743/target.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/target/SPEDIXH743/target.h b/src/main/target/SPEDIXH743/target.h index 26ddc73b1e2..c80abf4b79b 100644 --- a/src/main/target/SPEDIXH743/target.h +++ b/src/main/target/SPEDIXH743/target.h @@ -85,13 +85,17 @@ #define I2C1_SCL PB8 #define I2C1_SDA PB9 +#define USE_I2C_DEVICE_2 +#define I2C2_SCL PB10 +#define I2C2_SDA PB11 + #define USE_BARO #define BARO_I2C_BUS BUS_I2C1 #define USE_BARO_BMP280 #define USE_BARO_DPS310 #define USE_MAG -#define MAG_I2C_BUS BUS_I2C1 +#define MAG_I2C_BUS BUS_I2C2 #define USE_MAG_ALL #define TEMPERATURE_I2C_BUS BUS_I2C1 @@ -151,7 +155,7 @@ #define VBAT_ADC_CHANNEL ADC_CHN_1 #define CURRENT_METER_ADC_CHANNEL ADC_CHN_2 -#define VBAT_SCALE_DEFAULT 110 +#define VBAT_SCALE_DEFAULT 1100 // *************** PINIO ******************************** #define USE_PINIO From 53e2ef8e6c7caa41c8e0073ae2b5689b2ff5152a Mon Sep 17 00:00:00 2001 From: Sensei Date: Sun, 24 May 2026 11:27:41 -0500 Subject: [PATCH 23/67] Rename MAX to FIXED_VALUE and update description --- docs/Mixer.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Mixer.md b/docs/Mixer.md index 1e3cc1369e2..6d5fa2528f0 100644 --- a/docs/Mixer.md +++ b/docs/Mixer.md @@ -82,7 +82,7 @@ Each servo mixing rule has the following parameters: | 26 | Stabilized PITCH- | Clipped between -1000 and 0 | | 27 | Stabilized YAW+ | Clipped between 0 and 1000 | | 28 | Stabilized YAW- | Clipped between -1000 and 0 | -| 29 | MAX | Constant value of 500 | +| 29 | FIXED_VALUE (MAX) | Fixed percentage of total range | The `smix reset` command removes all the existing motor mixing rules. @@ -103,4 +103,4 @@ Other usages can be: * automatic parachute deployment * VTOL and especially tail-sitters that require change in mixings during flight mode transition * crowbar airbrakes -* any kind of servo mixings that should be changed during flight \ No newline at end of file +* any kind of servo mixings that should be changed during flight From 0203a1ac1243e6d0d047140d3a03330f9f0d874a Mon Sep 17 00:00:00 2001 From: Sensei Date: Sun, 24 May 2026 11:28:34 -0500 Subject: [PATCH 24/67] Correct FIXED_VALUE to FIXED-VALUE in Mixer.md --- docs/Mixer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Mixer.md b/docs/Mixer.md index 6d5fa2528f0..eb2901295a3 100644 --- a/docs/Mixer.md +++ b/docs/Mixer.md @@ -82,7 +82,7 @@ Each servo mixing rule has the following parameters: | 26 | Stabilized PITCH- | Clipped between -1000 and 0 | | 27 | Stabilized YAW+ | Clipped between 0 and 1000 | | 28 | Stabilized YAW- | Clipped between -1000 and 0 | -| 29 | FIXED_VALUE (MAX) | Fixed percentage of total range | +| 29 | FIXED-VALUE (MAX) | Fixed percentage of total range | The `smix reset` command removes all the existing motor mixing rules. From c27e3820432af4332ebf29fcba731386db0e79fe Mon Sep 17 00:00:00 2001 From: hakrc1 Date: Sun, 7 Apr 2024 17:35:33 +0800 Subject: [PATCH 25/67] add a new target BLADE_F4 --- src/main/target/BLADE_F4/CMakeLists.txt | 1 + src/main/target/BLADE_F4/config.c | 28 ++++ src/main/target/BLADE_F4/target.c | 37 +++++ src/main/target/BLADE_F4/target.h | 179 ++++++++++++++++++++++++ 4 files changed, 245 insertions(+) create mode 100644 src/main/target/BLADE_F4/CMakeLists.txt create mode 100644 src/main/target/BLADE_F4/config.c create mode 100644 src/main/target/BLADE_F4/target.c create mode 100644 src/main/target/BLADE_F4/target.h diff --git a/src/main/target/BLADE_F4/CMakeLists.txt b/src/main/target/BLADE_F4/CMakeLists.txt new file mode 100644 index 00000000000..4a1557112b5 --- /dev/null +++ b/src/main/target/BLADE_F4/CMakeLists.txt @@ -0,0 +1 @@ +target_stm32f405xg(BLADE_F4) diff --git a/src/main/target/BLADE_F4/config.c b/src/main/target/BLADE_F4/config.c new file mode 100644 index 00000000000..b963bfbfa59 --- /dev/null +++ b/src/main/target/BLADE_F4/config.c @@ -0,0 +1,28 @@ +/* + * This file is part of INAV. + * + * INAV is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * INAV is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with INAV. If not, see . + */ + +#include +#include + +#include "fc/fc_msp_box.h" +#include "io/piniobox.h" + +void targetConfiguration(void) +{ + pinioBoxConfigMutable()->permanentId[0] = BOX_PERMANENT_ID_USER1; + pinioBoxConfigMutable()->permanentId[1] = BOX_PERMANENT_ID_USER2; +} diff --git a/src/main/target/BLADE_F4/target.c b/src/main/target/BLADE_F4/target.c new file mode 100644 index 00000000000..a44aabc0142 --- /dev/null +++ b/src/main/target/BLADE_F4/target.c @@ -0,0 +1,37 @@ +/* + * This file is part of INAV. + * + * INAV is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * INAV is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with INAV. If not, see . + */ + +#include +#include + +#include "drivers/io.h" +#include "drivers/timer.h" + +timerHardware_t timerHardware[] = { + DEF_TIM(TIM8, CH3, PC8, TIM_USE_OUTPUT_AUTO, 0, 1), // S1_OUT D2_ST4 + DEF_TIM(TIM8, CH4, PC9, TIM_USE_OUTPUT_AUTO, 0, 0), // S2_OUT D2_ST7 + DEF_TIM(TIM1, CH1, PA8, TIM_USE_OUTPUT_AUTO, 0, 1), // S3_OUT D2_ST1 + DEF_TIM(TIM1, CH2, PA9, TIM_USE_OUTPUT_AUTO, 0, 1), // S4_OUT D2_ST2 + DEF_TIM(TIM1, CH3, PA10, TIM_USE_OUTPUT_AUTO, 0, 0), // S5_OUT D2_ST6 + DEF_TIM(TIM3, CH1, PB4, TIM_USE_OUTPUT_AUTO, 0, 0), // S6_OUT D1_ST4 + DEF_TIM(TIM3, CH3, PB0, TIM_USE_OUTPUT_AUTO, 0, 0), // S7_OUT D1_ST7 + DEF_TIM(TIM3, CH4, PB1, TIM_USE_OUTPUT_AUTO, 0, 0), // S8_OUT D1_ST2 + + DEF_TIM(TIM2, CH2, PB3, TIM_USE_LED, 0, 0), // D1_ST6 +}; + +const int timerHardwareCount = sizeof(timerHardware) / sizeof(timerHardware[0]); diff --git a/src/main/target/BLADE_F4/target.h b/src/main/target/BLADE_F4/target.h new file mode 100644 index 00000000000..91e1b4e5305 --- /dev/null +++ b/src/main/target/BLADE_F4/target.h @@ -0,0 +1,179 @@ +/* + * This file is part of INAV. + * + * INAV is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * INAV is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with INAV. If not, see . + */ + +#pragma once + +#define TARGET_BOARD_IDENTIFIER "RUSH" +#define USBD_PRODUCT_STRING "BLADE_F4" + +#define USE_TARGET_CONFIG + +/*** Indicators ***/ +#define LED0 PC15 +#define LED1 PC14 +#define BEEPER PC13 +#define BEEPER_INVERTED + +/*** SPI/I2C bus ***/ +#define USE_SPI +#define USE_SPI_DEVICE_1 +#define SPI1_NSS_PIN PA4 +#define SPI1_SCK_PIN PA5 +#define SPI1_MISO_PIN PA6 +#define SPI1_MOSI_PIN PA7 + +#define USE_SPI_DEVICE_2 +#define SPI2_NSS_PIN PB12 +#define SPI2_SCK_PIN PB13 +#define SPI2_MISO_PIN PB14 +#define SPI2_MOSI_PIN PB15 +#define SPI2_FLASH_PIN PA15 + + +#define USE_SPI_DEVICE_3 +#define SPI3_SCK_PIN PC10 +#define SPI3_MISO_PIN PC11 +#define SPI3_MOSI_PIN PB5 + +#define USE_I2C +#define USE_I2C_DEVICE_1 +#define I2C1_SCL PB8 +#define I2C1_SDA PB9 + + +/*** IMU sensors ***/ + +// MPU6000 +#define USE_IMU_MPU6000 +#define IMU_MPU6000_ALIGN CW270_DEG +#define MPU6000_SPI_BUS BUS_SPI1 +#define MPU6000_CS_PIN SPI1_NSS_PIN + + +// ICM42605/ICM42688P +#define USE_IMU_ICM42605 +#define IMU_ICM42605_ALIGN CW270_DEG +#define ICM42605_SPI_BUS BUS_SPI1 +#define ICM42605_CS_PIN SPI1_NSS_PIN + +//BMI270 +#define USE_IMU_BMI270 +#define IMU_BMI270_ALIGN CW270_DEG +#define BMI270_SPI_BUS BUS_SPI1 +#define BMI270_CS_PIN SPI1_NSS_PIN + + +/*** OSD ***/ +#define USE_MAX7456 +#define MAX7456_SPI_BUS BUS_SPI2 +#define MAX7456_CS_PIN SPI2_NSS_PIN + +/*** Onboard flash ***/ +#define USE_FLASHFS +#define USE_FLASH_M25P16 +#define M25P16_CS_PIN SPI2_FLASH_PIN +#define M25P16_SPI_BUS BUS_SPI2 + +// *** PINIO *** + +#define USE_PINIO +#define USE_PINIOBOX +#define PINIO1_PIN PC2 +#define PINIO2_PIN PC5 + +/*** Serial ports ***/ +#define USE_VCP +#define USE_UART_INVERTER + +#define USE_UART1 +#define UART1_RX_PIN PB7 +#define UART1_TX_PIN PB6 + +#define USE_UART2 +#define UART2_RX_PIN PA3 +#define UART2_TX_PIN PA2 +#define INVERTER_PIN_UART2_RX PC0 + +#define USE_UART3 +#define UART3_RX_PIN PB11 +#define UART3_TX_PIN PB10 + +#define USE_UART4 +#define UART4_RX_PIN PA1 +#define UART4_TX_PIN PA0 + +#define USE_UART5 +#define UART5_RX_PIN PD2 +#define UART5_TX_PIN PC12 + +#define USE_UART6 +#define UART6_RX_PIN PC7 +#define UART6_TX_PIN PC6 + +#define SERIAL_PORT_COUNT 7 + +/*** BARO & MAG ***/ +#define USE_BARO +#define BARO_I2C_BUS BUS_I2C1 +#define USE_BARO_BMP280 +#define USE_BARO_SPL06 +#define USE_BARO_DPS310 + +#define USE_MAG +#define MAG_I2C_BUS BUS_I2C1 +#define USE_MAG_ALL + +/*** ADC ***/ +#define USE_ADC +#define ADC_CHANNEL_1_PIN PC1 +#define ADC_CHANNEL_2_PIN PC3 + + +#define VBAT_ADC_CHANNEL ADC_CHN_1 +#define CURRENT_METER_ADC_CHANNEL ADC_CHN_2 + + +/*** LED STRIP ***/ +#define USE_LED_STRIP +#define WS2811_PIN PB3 + +/*** Default settings ***/ +#define ENABLE_BLACKBOX_LOGGING_ON_SPIFLASH_BY_DEFAULT +#define DEFAULT_RX_TYPE RX_TYPE_SERIAL +#define SERIALRX_PROVIDER SERIALRX_SBUS +#define CURRENT_METER_SCALE 250 + + +#define DEFAULT_FEATURES (FEATURE_TX_PROF_SEL | FEATURE_OSD | FEATURE_CURRENT_METER | FEATURE_VBAT | FEATURE_TELEMETRY | FEATURE_SOFTSERIAL ) + +/*** Timer/PWM output ***/ +#define USE_SERIAL_4WAY_BLHELI_INTERFACE +#define MAX_PWM_OUTPUT_PORTS 8 +#define USE_DSHOT +#define USE_ESC_SENSOR + +// ********** Optical Flow and Lidar ************** +#define USE_RANGEFINDER +#define USE_RANGEFINDER_MSP +#define USE_OPFLOW +#define USE_OPFLOW_MSP + +/*** Used pins ***/ +#define TARGET_IO_PORTA 0xffff +#define TARGET_IO_PORTB 0xffff +#define TARGET_IO_PORTC 0xffff +#define TARGET_IO_PORTD (BIT(2)) From c9aafb65f51acb671f663f839a0fda7d739490e8 Mon Sep 17 00:00:00 2001 From: hakrc1 Date: Sun, 7 Apr 2024 17:47:22 +0800 Subject: [PATCH 26/67] add new target BLADE_PRO_H7 --- src/main/target/BLADE_PRO_H7/CMakeLists.txt | 1 + src/main/target/BLADE_PRO_H7/config.c | 32 +++ src/main/target/BLADE_PRO_H7/target.c | 70 ++++++ src/main/target/BLADE_PRO_H7/target.h | 233 ++++++++++++++++++++ 4 files changed, 336 insertions(+) create mode 100644 src/main/target/BLADE_PRO_H7/CMakeLists.txt create mode 100644 src/main/target/BLADE_PRO_H7/config.c create mode 100644 src/main/target/BLADE_PRO_H7/target.c create mode 100644 src/main/target/BLADE_PRO_H7/target.h diff --git a/src/main/target/BLADE_PRO_H7/CMakeLists.txt b/src/main/target/BLADE_PRO_H7/CMakeLists.txt new file mode 100644 index 00000000000..37321c43054 --- /dev/null +++ b/src/main/target/BLADE_PRO_H7/CMakeLists.txt @@ -0,0 +1 @@ +target_stm32h743xi(BLADE_PRO_H7) diff --git a/src/main/target/BLADE_PRO_H7/config.c b/src/main/target/BLADE_PRO_H7/config.c new file mode 100644 index 00000000000..1065971614a --- /dev/null +++ b/src/main/target/BLADE_PRO_H7/config.c @@ -0,0 +1,32 @@ +/* + * This file is part of Cleanflight. + * + * Cleanflight is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Cleanflight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Cleanflight. If not, see . + */ + +#include + +#include "platform.h" + +#include "fc/fc_msp_box.h" +#include "fc/config.h" + +#include "io/piniobox.h" + +void targetConfiguration(void) +{ + pinioBoxConfigMutable()->permanentId[0] = BOX_PERMANENT_ID_USER1; + pinioBoxConfigMutable()->permanentId[1] = BOX_PERMANENT_ID_USER2; + beeperConfigMutable()->pwmMode = true; +} diff --git a/src/main/target/BLADE_PRO_H7/target.c b/src/main/target/BLADE_PRO_H7/target.c new file mode 100644 index 00000000000..1aeb2ef3a9d --- /dev/null +++ b/src/main/target/BLADE_PRO_H7/target.c @@ -0,0 +1,70 @@ +/* + * This file is part of INAV. + * + * INAV is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * INAV is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with INAV. If not, see . + */ + +#include + +#include "platform.h" + +#include "drivers/bus.h" +#include "drivers/io.h" +#include "drivers/pwm_mapping.h" +#include "drivers/timer.h" +#include "drivers/pinio.h" +#include "drivers/sensor.h" + +BUSDEV_REGISTER_SPI_TAG(busdev_mpu6000_1, DEVHW_MPU6000, MPU6000_SPI_BUS_1, MPU6000_CS_PIN_1, MPU6000_EXTI_PIN_1, DEVFLAGS_NONE, 0, IMU_MPU6000_ALIGN_1); +BUSDEV_REGISTER_SPI_TAG(busdev_mpu6000_2, DEVHW_MPU6000, MPU6000_SPI_BUS_2, MPU6000_CS_PIN_2, MPU6000_EXTI_PIN_2, DEVFLAGS_NONE, 1, IMU_MPU6000_ALIGN_2); + +BUSDEV_REGISTER_SPI_TAG(busdev_icm42688_1, DEVHW_ICM42605, ICM42605_SPI_BUS_1, ICM42605_CS_PIN_1, ICM42605_EXTI_PIN_1, DEVFLAGS_NONE, 0, IMU_ICM42605_ALIGN_1); +BUSDEV_REGISTER_SPI_TAG(busdev_icm42688_2, DEVHW_ICM42605, ICM42605_SPI_BUS_2, ICM42605_CS_PIN_2, ICM42605_EXTI_PIN_2, DEVFLAGS_NONE, 1, IMU_ICM42605_ALIGN_2); + +BUSDEV_REGISTER_SPI_TAG(busdev_bmi270_1, DEVHW_BMI270, BMI270_SPI_BUS_1, BMI270_CS_PIN_1, BMI270_EXTI_PIN_1, DEVFLAGS_NONE, 0, IMU_BMI270_ALIGN_1); +BUSDEV_REGISTER_SPI_TAG(busdev_bmi270_2, DEVHW_BMI270, BMI270_SPI_BUS_2, BMI270_CS_PIN_2, BMI270_EXTI_PIN_2, DEVFLAGS_NONE, 1, IMU_BMI270_ALIGN_2); + +timerHardware_t timerHardware[] = { +// DEF_TIM(TIM3, CH3, PB0, TIM_USE_OUTPUT_AUTO, 0, 0), // S1 +// DEF_TIM(TIM3, CH4, PB1, TIM_USE_OUTPUT_AUTO, 0, 1), // S2 +// DEF_TIM(TIM5, CH1, PA0, TIM_USE_OUTPUT_AUTO, 0, 2), // S3 +// DEF_TIM(TIM5, CH2, PA1, TIM_USE_OUTPUT_AUTO, 0, 3), // S4 + + DEF_TIM(TIM3, CH4, PB1, TIM_USE_OUTPUT_AUTO, 0, 0), // S2 + DEF_TIM(TIM3, CH3, PB0, TIM_USE_OUTPUT_AUTO, 0, 1), // S1 + DEF_TIM(TIM5, CH2, PA1, TIM_USE_OUTPUT_AUTO, 0, 2), // S4 + DEF_TIM(TIM5, CH1, PA0, TIM_USE_OUTPUT_AUTO, 0, 3), // S3 + + DEF_TIM(TIM5, CH3, PA2, TIM_USE_OUTPUT_AUTO, 0, 4), // S5 + DEF_TIM(TIM5, CH4, PA3, TIM_USE_OUTPUT_AUTO, 0, 5), // S6 + DEF_TIM(TIM4, CH1, PD12, TIM_USE_OUTPUT_AUTO, 0, 6), // S7 + DEF_TIM(TIM4, CH2, PD13, TIM_USE_OUTPUT_AUTO, 0, 7), // S8 + + DEF_TIM(TIM15, CH1, PE5, TIM_USE_OUTPUT_AUTO, 0, 0), // S11 + DEF_TIM(TIM15, CH2, PE6, TIM_USE_OUTPUT_AUTO, 0, 0), // S12 DMA_NONE + DEF_TIM(TIM4, CH4, PD15, TIM_USE_OUTPUT_AUTO, 0, 0), // S10 DMA_NONE + DEF_TIM(TIM4, CH3, PD14, TIM_USE_OUTPUT_AUTO, 0, 0), // S9 + + +// DEF_TIM(TIM4, CH3, PD14, TIM_USE_OUTPUT_AUTO, 0, 0), // S9 +// DEF_TIM(TIM4, CH4, PD15, TIM_USE_OUTPUT_AUTO, 0, 0), // S10 DMA_NONE +// DEF_TIM(TIM15, CH1, PE5, TIM_USE_OUTPUT_AUTO, 0, 0), // S11 +// DEF_TIM(TIM15, CH2, PE6, TIM_USE_OUTPUT_AUTO, 0, 0), // S12 DMA_NONE + + DEF_TIM(TIM1, CH1, PA8, TIM_USE_LED, 0, 9), // LED_2812 + DEF_TIM(TIM2, CH1, PA15, TIM_USE_BEEPER, 0, 0), // BEEPER PWM + +}; + +const int timerHardwareCount = sizeof(timerHardware) / sizeof(timerHardware[0]); diff --git a/src/main/target/BLADE_PRO_H7/target.h b/src/main/target/BLADE_PRO_H7/target.h new file mode 100644 index 00000000000..5c95457d6f3 --- /dev/null +++ b/src/main/target/BLADE_PRO_H7/target.h @@ -0,0 +1,233 @@ +/* + * This file is part of INAV. + * + * INAV is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * INAV is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with INAV. If not, see . + */ + + +#pragma once + +#define TARGET_BOARD_IDENTIFIER "RUSH" +#define USBD_PRODUCT_STRING "BLADE_PRO_H7" + +#define USE_TARGET_CONFIG + +#define LED0 PE3 +#define LED1 PE4 + +#define BEEPER PA15 +#define BEEPER_INVERTED +#define BEEPER_PWM_FREQUENCY 2500 + +// *************** IMU generic *********************** +#define USE_DUAL_GYRO +#define USE_TARGET_IMU_HARDWARE_DESCRIPTORS +// *************** SPI1 IMU1 MPU6000 && BMI270 && ICM42688P **************** +#define USE_SPI +#define USE_SPI_DEVICE_1 +#define SPI1_SCK_PIN PA5 +#define SPI1_MISO_PIN PA6 +#define SPI1_MOSI_PIN PD7 + +#define SPI1_NSS_PIN PC15 +#define SPI1_EXTI_PIN PB2 + +// MPU6000 +#define USE_IMU_MPU6000 +#define IMU_MPU6000_ALIGN_1 CW180_DEG +#define MPU6000_SPI_BUS_1 BUS_SPI1 +#define MPU6000_CS_PIN_1 SPI1_NSS_PIN +#define MPU6000_EXTI_PIN_1 SPI1_EXTI_PIN + +// ICM42605/ICM42688P +#define USE_IMU_ICM42605 +#define IMU_ICM42605_ALIGN_1 CW180_DEG +#define ICM42605_SPI_BUS_1 BUS_SPI1 +#define ICM42605_CS_PIN_1 SPI1_NSS_PIN +#define ICM42605_EXTI_PIN_1 SPI1_EXTI_PIN + +//BMI270 +#define USE_IMU_BMI270 +#define IMU_BMI270_ALIGN_1 CW180_DEG +#define BMI270_SPI_BUS_1 BUS_SPI1 +#define BMI270_CS_PIN_1 SPI1_NSS_PIN +#define BMI270_EXTI_PIN_1 SPI1_EXTI_PIN + +// *************** SPI4 IMU2 BMI270 ************** +#define USE_SPI_DEVICE_4 +#define SPI4_SCK_PIN PE12 +#define SPI4_MISO_PIN PE13 +#define SPI4_MOSI_PIN PE14 + +#define SPI4_NSS_PIN PE11 +#define SPI4_EXTI_PIN PE15 + +// MPU6000 +#define USE_IMU_MPU6000 +#define IMU_MPU6000_ALIGN_2 CW270_DEG +#define MPU6000_SPI_BUS_2 BUS_SPI1 +#define MPU6000_CS_PIN_2 SPI4_NSS_PIN +#define MPU6000_EXTI_PIN_2 SPI4_EXTI_PIN + +// ICM42605/ICM42688P +#define USE_IMU_ICM42605 +#define IMU_ICM42605_ALIGN_2 CW270_DEG +#define ICM42605_SPI_BUS_2 BUS_SPI1 +#define ICM42605_CS_PIN_2 SPI4_NSS_PIN +#define ICM42605_EXTI_PIN_2 SPI4_EXTI_PIN + +//BMI270 +#define USE_IMU_BMI270 +#define IMU_BMI270_ALIGN_2 CW270_DEG +#define BMI270_SPI_BUS_2 BUS_SPI1 +#define BMI270_CS_PIN_2 SPI4_NSS_PIN +#define BMI270_EXTI_PIN_2 SPI4_EXTI_PIN + +// *************** SPI2 OSD *********************** +#define USE_SPI_DEVICE_2 +#define SPI2_SCK_PIN PB13 +#define SPI2_MISO_PIN PB14 +#define SPI2_MOSI_PIN PB15 + +#define USE_MAX7456 +#define MAX7456_SPI_BUS BUS_SPI2 +#define MAX7456_CS_PIN PB12 + +// *************** SPI3 FLASH *********************** +#define USE_SPI_DEVICE_3 + +#define SPI3_NSS_PIN PE2 +#define SPI3_SCK_PIN PB3 +#define SPI3_MISO_PIN PB4 +#define SPI3_MOSI_PIN PB5 + +#define SPI3_SCK_AF GPIO_AF6_SPI3 +#define SPI3_MISO_AF GPIO_AF6_SPI3 +#define SPI3_MOSI_AF GPIO_AF7_SPI3 + +#define W25N01G_SPI_BUS BUS_SPI3 +#define W25N01G_CS_PIN SPI3_NSS_PIN + +#define USE_BLACKBOX +#define USE_FLASHFS +#define USE_FLASH_W25N01G +#define ENABLE_BLACKBOX_LOGGING_ON_SPIFLASH_BY_DEFAULT + +#define USE_FLASHFS +#define USE_FLASH_M25P16 +#define M25P16_CS_PIN SPI3_NSS_PIN +#define M25P16_SPI_BUS BUS_SPI3 + +// *************** I2C /Baro/Mag ********************* +#define USE_I2C +#define USE_I2C_DEVICE_1 +#define I2C1_SCL PB6 +#define I2C1_SDA PB7 + +#define USE_I2C_DEVICE_2 +#define I2C2_SCL PB10 +#define I2C2_SDA PB11 + +#define USE_BARO +#define BARO_I2C_BUS BUS_I2C2 +#define USE_BARO_BMP280 +#define USE_BARO_MS5611 +#define USE_BARO_DPS310 +#define USE_BARO_SPL06 + +#define USE_MAG +#define MAG_I2C_BUS BUS_I2C1 +#define USE_MAG_ALL + +#define TEMPERATURE_I2C_BUS BUS_I2C2 +#define PITOT_I2C_BUS BUS_I2C2 + +#define USE_RANGEFINDER +#define RANGEFINDER_I2C_BUS BUS_I2C2 + + +// *************** UART ***************************** +#define USE_VCP + +#define USE_UART1 +#define UART1_TX_PIN PA9 +#define UART1_RX_PIN PA10 + +#define USE_UART2 +#define UART2_TX_PIN PD5 +#define UART2_RX_PIN PD6 + +#define USE_UART3 +#define UART3_TX_PIN PD8 +#define UART3_RX_PIN PD9 + +#define USE_UART4 +#define UART4_TX_PIN PB9 +#define UART4_RX_PIN PB8 + +#define USE_UART6 +#define UART6_TX_PIN PC6 +#define UART6_RX_PIN PC7 + +#define USE_UART7 +#define UART7_TX_PIN PE8 +#define UART7_RX_PIN PE7 + +#define USE_UART8 +#define UART8_TX_PIN PE1 +#define UART8_RX_PIN PE0 + +#define SERIAL_PORT_COUNT 7 + +#define DEFAULT_RX_TYPE RX_TYPE_SERIAL +#define SERIALRX_PROVIDER SERIALRX_SBUS + +// *************** ADC ***************************** +#define USE_ADC +#define ADC_INSTANCE ADC1 + +#define ADC_CHANNEL_1_PIN PC0 //ADC123 VBAT1 +#define ADC_CHANNEL_2_PIN PC1 //ADC123 CURR1 +#define ADC_CHANNEL_3_PIN PC5 //ADC12 RSSI +#define ADC_CHANNEL_4_PIN PC4 //ADC12 AirS + +#define VBAT_ADC_CHANNEL ADC_CHN_1 +#define CURRENT_METER_ADC_CHANNEL ADC_CHN_2 +#define RSSI_ADC_CHANNEL ADC_CHN_3 +#define AIRSPEED_ADC_CHANNEL ADC_CHN_4 + +// *************** PINIO *************************** +#define USE_PINIO +#define USE_PINIOBOX +#define PINIO1_PIN PD10 // VTX power switcher +#define PINIO2_PIN PD11 // 2xCamera switcher + +// *************** LEDSTRIP ************************ +#define USE_LED_STRIP +#define WS2811_PIN PA8 + +#define DEFAULT_FEATURES (FEATURE_OSD | FEATURE_TELEMETRY | FEATURE_CURRENT_METER | FEATURE_VBAT | FEATURE_TX_PROF_SEL | FEATURE_BLACKBOX) +#define CURRENT_METER_SCALE 250 + +#define USE_SERIAL_4WAY_BLHELI_INTERFACE + +#define TARGET_IO_PORTA 0xffff +#define TARGET_IO_PORTB 0xffff +#define TARGET_IO_PORTC 0xffff +#define TARGET_IO_PORTD 0xffff +#define TARGET_IO_PORTE 0xffff + +#define MAX_PWM_OUTPUT_PORTS 15 +#define USE_DSHOT +#define USE_ESC_SENSOR From 456dcf05e8a056716d12cb182464151c7f4248c8 Mon Sep 17 00:00:00 2001 From: hakrc1 Date: Mon, 8 Apr 2024 09:14:44 +0800 Subject: [PATCH 27/67] deal serial conut --- src/main/target/BLADE_PRO_H7/target.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/target/BLADE_PRO_H7/target.h b/src/main/target/BLADE_PRO_H7/target.h index 5c95457d6f3..3a35511b759 100644 --- a/src/main/target/BLADE_PRO_H7/target.h +++ b/src/main/target/BLADE_PRO_H7/target.h @@ -188,7 +188,7 @@ #define UART8_TX_PIN PE1 #define UART8_RX_PIN PE0 -#define SERIAL_PORT_COUNT 7 +#define SERIAL_PORT_COUNT 8 #define DEFAULT_RX_TYPE RX_TYPE_SERIAL #define SERIALRX_PROVIDER SERIALRX_SBUS From 6af9c7f61881e8b3a9cde394664396015681f155 Mon Sep 17 00:00:00 2001 From: Sensei Date: Mon, 25 May 2026 12:30:11 -0500 Subject: [PATCH 28/67] Change TARGET_BOARD_IDENTIFIER to 'BLF4' and update features --- src/main/target/BLADE_F4/target.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/target/BLADE_F4/target.h b/src/main/target/BLADE_F4/target.h index 91e1b4e5305..71cf76cc0cf 100644 --- a/src/main/target/BLADE_F4/target.h +++ b/src/main/target/BLADE_F4/target.h @@ -17,7 +17,7 @@ #pragma once -#define TARGET_BOARD_IDENTIFIER "RUSH" +#define TARGET_BOARD_IDENTIFIER "BLF4" #define USBD_PRODUCT_STRING "BLADE_F4" #define USE_TARGET_CONFIG @@ -158,7 +158,7 @@ #define CURRENT_METER_SCALE 250 -#define DEFAULT_FEATURES (FEATURE_TX_PROF_SEL | FEATURE_OSD | FEATURE_CURRENT_METER | FEATURE_VBAT | FEATURE_TELEMETRY | FEATURE_SOFTSERIAL ) +#define DEFAULT_FEATURES (FEATURE_TX_PROF_SEL | FEATURE_OSD | FEATURE_CURRENT_METER | FEATURE_VBAT | FEATURE_TELEMETRY ) /*** Timer/PWM output ***/ #define USE_SERIAL_4WAY_BLHELI_INTERFACE From 707f6132a9cfec19d6baa31cd2a52f6f54021db8 Mon Sep 17 00:00:00 2001 From: Sensei Date: Mon, 25 May 2026 12:31:04 -0500 Subject: [PATCH 29/67] Clean up timer definitions in target.c Removed commented-out timer definitions and adjusted the order of active timer definitions. --- src/main/target/BLADE_PRO_H7/target.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/main/target/BLADE_PRO_H7/target.c b/src/main/target/BLADE_PRO_H7/target.c index 1aeb2ef3a9d..28745e92017 100644 --- a/src/main/target/BLADE_PRO_H7/target.c +++ b/src/main/target/BLADE_PRO_H7/target.c @@ -36,10 +36,6 @@ BUSDEV_REGISTER_SPI_TAG(busdev_bmi270_1, DEVHW_BMI270, BMI270_SPI_BUS_1, B BUSDEV_REGISTER_SPI_TAG(busdev_bmi270_2, DEVHW_BMI270, BMI270_SPI_BUS_2, BMI270_CS_PIN_2, BMI270_EXTI_PIN_2, DEVFLAGS_NONE, 1, IMU_BMI270_ALIGN_2); timerHardware_t timerHardware[] = { -// DEF_TIM(TIM3, CH3, PB0, TIM_USE_OUTPUT_AUTO, 0, 0), // S1 -// DEF_TIM(TIM3, CH4, PB1, TIM_USE_OUTPUT_AUTO, 0, 1), // S2 -// DEF_TIM(TIM5, CH1, PA0, TIM_USE_OUTPUT_AUTO, 0, 2), // S3 -// DEF_TIM(TIM5, CH2, PA1, TIM_USE_OUTPUT_AUTO, 0, 3), // S4 DEF_TIM(TIM3, CH4, PB1, TIM_USE_OUTPUT_AUTO, 0, 0), // S2 DEF_TIM(TIM3, CH3, PB0, TIM_USE_OUTPUT_AUTO, 0, 1), // S1 @@ -56,12 +52,6 @@ timerHardware_t timerHardware[] = { DEF_TIM(TIM4, CH4, PD15, TIM_USE_OUTPUT_AUTO, 0, 0), // S10 DMA_NONE DEF_TIM(TIM4, CH3, PD14, TIM_USE_OUTPUT_AUTO, 0, 0), // S9 - -// DEF_TIM(TIM4, CH3, PD14, TIM_USE_OUTPUT_AUTO, 0, 0), // S9 -// DEF_TIM(TIM4, CH4, PD15, TIM_USE_OUTPUT_AUTO, 0, 0), // S10 DMA_NONE -// DEF_TIM(TIM15, CH1, PE5, TIM_USE_OUTPUT_AUTO, 0, 0), // S11 -// DEF_TIM(TIM15, CH2, PE6, TIM_USE_OUTPUT_AUTO, 0, 0), // S12 DMA_NONE - DEF_TIM(TIM1, CH1, PA8, TIM_USE_LED, 0, 9), // LED_2812 DEF_TIM(TIM2, CH1, PA15, TIM_USE_BEEPER, 0, 0), // BEEPER PWM From 562de0859adb8d7039b9b29b309396cfc7c49432 Mon Sep 17 00:00:00 2001 From: Sensei Date: Mon, 25 May 2026 12:32:18 -0500 Subject: [PATCH 30/67] Change TARGET_BOARD_IDENTIFIER and SPI bus settings --- src/main/target/BLADE_PRO_H7/target.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/target/BLADE_PRO_H7/target.h b/src/main/target/BLADE_PRO_H7/target.h index 3a35511b759..a341a504863 100644 --- a/src/main/target/BLADE_PRO_H7/target.h +++ b/src/main/target/BLADE_PRO_H7/target.h @@ -18,7 +18,7 @@ #pragma once -#define TARGET_BOARD_IDENTIFIER "RUSH" +#define TARGET_BOARD_IDENTIFIER "BLPH" #define USBD_PRODUCT_STRING "BLADE_PRO_H7" #define USE_TARGET_CONFIG @@ -46,21 +46,21 @@ // MPU6000 #define USE_IMU_MPU6000 #define IMU_MPU6000_ALIGN_1 CW180_DEG -#define MPU6000_SPI_BUS_1 BUS_SPI1 +#define MPU6000_SPI_BUS_1 BUS_SPI4 #define MPU6000_CS_PIN_1 SPI1_NSS_PIN #define MPU6000_EXTI_PIN_1 SPI1_EXTI_PIN // ICM42605/ICM42688P #define USE_IMU_ICM42605 #define IMU_ICM42605_ALIGN_1 CW180_DEG -#define ICM42605_SPI_BUS_1 BUS_SPI1 +#define ICM42605_SPI_BUS_1 BUS_SPI4 #define ICM42605_CS_PIN_1 SPI1_NSS_PIN #define ICM42605_EXTI_PIN_1 SPI1_EXTI_PIN //BMI270 #define USE_IMU_BMI270 #define IMU_BMI270_ALIGN_1 CW180_DEG -#define BMI270_SPI_BUS_1 BUS_SPI1 +#define BMI270_SPI_BUS_1 BUS_SPI4 #define BMI270_CS_PIN_1 SPI1_NSS_PIN #define BMI270_EXTI_PIN_1 SPI1_EXTI_PIN From c8ff2e495f5fab4d82d80bb167da51aea7778f36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=B5nu=20Samuel?= Date: Sat, 12 Jul 2025 18:56:50 +0300 Subject: [PATCH 31/67] SSD1315 OLEDs also seem to work I just tested random SSD1315 from aliexpress and it worked without any modifications. --- docs/Display.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/Display.md b/docs/Display.md index 952f9b08661..771b701588f 100755 --- a/docs/Display.md +++ b/docs/Display.md @@ -44,6 +44,10 @@ Links to displays: The banggood.com display is the cheapest at the time of writing and will correctly send I2C ACK signals. +### SSD1315 OLED displays + +SSD1315 is newer generation drop in replacement for SSD1306. Tested and working unit is bought from https://www.aliexpress.com/item/1005006901360788.html + #### Crius CO-16 This display is best avoided but will work if you modify it. From d9263672a414cdfc7a58df33c093710fea466291 Mon Sep 17 00:00:00 2001 From: wimalopaan Date: Sat, 14 Mar 2026 17:59:07 +0100 Subject: [PATCH 32/67] reset free PIDs if coefficients are changed via CLI or MSP --- src/main/fc/cli.c | 2 ++ src/main/fc/fc_msp.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/main/fc/cli.c b/src/main/fc/cli.c index 0d6e75627a1..9bb2c776883 100644 --- a/src/main/fc/cli.c +++ b/src/main/fc/cli.c @@ -2700,6 +2700,8 @@ static void cliPid(char *cmdline) { programmingPidsMutable(i)->gains.D = args[D_GAIN]; programmingPidsMutable(i)->gains.FF = args[FF_GAIN]; + programmingPidInit(); + cliPid(""); } else { cliShowParseError(); diff --git a/src/main/fc/fc_msp.c b/src/main/fc/fc_msp.c index 75372ff8825..3ec3f89f3f0 100644 --- a/src/main/fc/fc_msp.c +++ b/src/main/fc/fc_msp.c @@ -2399,6 +2399,9 @@ static mspResult_e mspFcProcessInCommand(uint16_t cmdMSP, sbuf_t *src) programmingPidsMutable(tmp_u8)->gains.I = sbufReadU16(src); programmingPidsMutable(tmp_u8)->gains.D = sbufReadU16(src); programmingPidsMutable(tmp_u8)->gains.FF = sbufReadU16(src); + + programmingPidInit(); + } else return MSP_RESULT_ERROR; break; From ce7aa0fd1d5da3af9a50bed089087036c0cc85d8 Mon Sep 17 00:00:00 2001 From: breadoven <56191411+breadoven@users.noreply.github.com> Date: Fri, 29 May 2026 09:35:55 +0100 Subject: [PATCH 33/67] Update displayport_msp_osd.c --- src/main/io/displayport_msp_osd.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/io/displayport_msp_osd.c b/src/main/io/displayport_msp_osd.c index 4963a09b97b..6b674a300ec 100644 --- a/src/main/io/displayport_msp_osd.c +++ b/src/main/io/displayport_msp_osd.c @@ -119,7 +119,7 @@ static void checkVtxPresent(void) vtxActive = false; } - if (ARMING_FLAG(SIMULATOR_MODE_HITL)) { + if (ARMING_FLAG(SIMULATOR_MODE_HITL)) { vtxActive = true; } } @@ -243,10 +243,11 @@ static int writeString(displayPort_t *displayPort, uint8_t col, uint8_t row, con */ static int drawScreen(displayPort_t *displayPort) // 250Hz { +#ifdef USE_SIMULATOR if (SIMULATOR_HAS_OPTION(HITL_SITL_MODE)) { vtxActive = true; } - +#endif static uint8_t counter = 0; if ((!cmsInMenu && IS_RC_MODE_ACTIVE(BOXOSD)) || (counter++ % DRAW_FREQ_DENOM)) { // 62.5Hz @@ -258,14 +259,14 @@ static int drawScreen(displayPort_t *displayPort) // 250Hz uint8_t refreshSubcmd[1]; refreshSubcmd[0] = MSP_DP_CLEAR_SCREEN; output(displayPort, MSP_DISPLAYPORT, refreshSubcmd, sizeof(refreshSubcmd)); - + // Then dirty the characters that are not blank, to send all data on this draw. for (unsigned int pos = 0; pos < sizeof(screen); pos++) { if (screen[pos] != SYM_BLANK) { bitArraySet(dirty, pos); } } - + sendSubFrameMs = (osdConfig()->msp_displayport_fullframe_interval > 0) ? (millis() + DS2MS(osdConfig()->msp_displayport_fullframe_interval)) : 0; } @@ -570,7 +571,7 @@ static mspResult_e fixDjiBrokenO4ProcessMspCommand(mspPacket_t *cmd, mspPacket_t return processMspCommand(cmd, reply, mspPostProcessFn); } #else -#define fixDjiBrokenO4ProcessMspCommand processMspCommand +#define fixDjiBrokenO4ProcessMspCommand processMspCommand #endif void mspOsdSerialProcess(mspProcessCommandFnPtr mspProcessCommandFn) From 958f4a0af10f3266fb39c90f9cf1f3ee833e29fa Mon Sep 17 00:00:00 2001 From: daijoubu Date: Fri, 29 May 2026 19:02:36 -0700 Subject: [PATCH 34/67] fix: make PLL2M dynamic using VCI=1.6MHz formula for SDMMC clock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PLL2M was hardcoded to 5, which assumes HSE = 8 MHz. On KAKUTEH7WING (HSE = 16 MHz) this gives VCO = 16/5 * 500 = 1600 MHz (out of spec) and SDMMC clock = 400 MHz instead of the required 200 MHz. Fix: compute PLL2M as HSE_VALUE / 1600000, pinning the VCO input to exactly 1.6 MHz for any HSE frequency. With N=500 this gives VCO=800 MHz: HSE=8 MHz: M=5, N=500 → VCO=800 MHz (identical to original) HSE=16 MHz: M=10, N=500 → VCO=800 MHz (correct for KAKUTEH7WING) PLL2R/4 = 200 MHz (SDMMC), PLL2P/2 = 400 MHz. VCIRANGE_0 (1–2 MHz) is correct for the 1.6 MHz VCI input on all targets. Also adds STATIC_ASSERT to catch future targets with non-multiple HSE, and corrects the PLL2P comment (was "500Mhz", should be "400Mhz"). Fixes #11594 --- src/main/target/system_stm32h7xx.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/target/system_stm32h7xx.c b/src/main/target/system_stm32h7xx.c index 6ec0d1c4002..c0ec58a46e2 100644 --- a/src/main/target/system_stm32h7xx.c +++ b/src/main/target/system_stm32h7xx.c @@ -498,10 +498,16 @@ void SystemClock_Config(void) HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphClkInit); #ifdef USE_SDCARD_SDIO + // PLL2M = HSE_VALUE / 1600000 pins the VCO input to exactly 1.6 MHz for any HSE. + // With N=500 this gives VCO=800 MHz: PLL2R/4=200 MHz (SDMMC), PLL2P/2=400 MHz. + // For 8 MHz HSE (M=5, N=500) this is identical to the original hardcoded values — + // only KAKUTEH7WING (16 MHz HSE, M=10) is different from the original broken state. + // HSE_VALUE must be a multiple of 1600000; all current H7 targets (8 MHz, 16 MHz) satisfy this. + STATIC_ASSERT(HSE_VALUE % 1600000 == 0, HSE_VALUE_must_be_a_multiple_of_1_6MHz_for_PLL2M_calculation); RCC_PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SDMMC; - RCC_PeriphClkInit.PLL2.PLL2M = 5; + RCC_PeriphClkInit.PLL2.PLL2M = HSE_VALUE / 1600000; RCC_PeriphClkInit.PLL2.PLL2N = 500; - RCC_PeriphClkInit.PLL2.PLL2P = 2; // 500Mhz + RCC_PeriphClkInit.PLL2.PLL2P = 2; // 400Mhz RCC_PeriphClkInit.PLL2.PLL2Q = 3; // 266Mhz - 133Mhz can be derived from this for for QSPI if flash chip supports the speed. RCC_PeriphClkInit.PLL2.PLL2R = 4; // 200Mhz HAL LIBS REQUIRE 200MHZ SDMMC CLOCK, see HAL_SD_ConfigWideBusOperation, SDMMC_HSpeed_CLK_DIV, SDMMC_NSpeed_CLK_DIV RCC_PeriphClkInit.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_0; From 722aae1b4ed0640d56c421ff2b121bc779f3ff99 Mon Sep 17 00:00:00 2001 From: daijoubu Date: Fri, 29 May 2026 19:23:51 -0700 Subject: [PATCH 35/67] fix: check HAL_RCCEx_PeriphCLKConfig return for SDMMC PLL2 config If PLL2 fails to lock at startup the SDMMC clock is dead and the SD card will fail silently. Call Error_Handler() (infinite loop) on failure to make the fault visible rather than continuing into undefined behaviour. --- src/main/target/system_stm32h7xx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/target/system_stm32h7xx.c b/src/main/target/system_stm32h7xx.c index c0ec58a46e2..f386f180bfd 100644 --- a/src/main/target/system_stm32h7xx.c +++ b/src/main/target/system_stm32h7xx.c @@ -514,7 +514,9 @@ void SystemClock_Config(void) RCC_PeriphClkInit.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE; RCC_PeriphClkInit.PLL2.PLL2FRACN = 0; RCC_PeriphClkInit.SdmmcClockSelection = RCC_SDMMCCLKSOURCE_PLL2; - HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphClkInit); + if (HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphClkInit) != HAL_OK) { + Error_Handler(); + } #endif #ifdef USE_QUADSPI From 7c49b746d7b97cd6cc85b988f2b15156ced75599 Mon Sep 17 00:00:00 2001 From: daijoubu Date: Fri, 29 May 2026 19:39:55 -0700 Subject: [PATCH 36/67] fix: correct PLL1 VCIRANGE from VCIRANGE_2 to VCIRANGE_1 PLL1 VCI input is HSE/M = 2 MHz on all H7 targets, which falls in the 2-4 MHz range (VCIRANGE_1). The previous VCIRANGE_2 (4-8 MHz) was incorrect and affected PLL charge pump calibration. Fixes #11602 --- src/main/target/system_stm32h7xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/target/system_stm32h7xx.c b/src/main/target/system_stm32h7xx.c index f386f180bfd..87fff82719b 100644 --- a/src/main/target/system_stm32h7xx.c +++ b/src/main/target/system_stm32h7xx.c @@ -302,7 +302,7 @@ static void SystemClockHSE_Config(void) RCC_OscInitStruct.PLL.PLLR = pll1Config->r; RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE; - RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2; + RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_1; // VCI = HSE/M = 2 MHz, range is 2-4 MHz HAL_StatusTypeDef status = HAL_RCC_OscConfig(&RCC_OscInitStruct); From 7d24bc20a261c31fcc51559eb0afb53f5ea6e1d2 Mon Sep 17 00:00:00 2001 From: daijoubu Date: Fri, 29 May 2026 19:41:37 -0700 Subject: [PATCH 37/67] cleanup: trim PLL2 comment block and shorten STATIC_ASSERT slug Remove redundant prose that duplicated the assert, remove board-specific name from shared init code, and shorten the assert slug to match codebase conventions. --- src/main/target/system_stm32h7xx.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/target/system_stm32h7xx.c b/src/main/target/system_stm32h7xx.c index 87fff82719b..56a53cefcfb 100644 --- a/src/main/target/system_stm32h7xx.c +++ b/src/main/target/system_stm32h7xx.c @@ -500,10 +500,7 @@ void SystemClock_Config(void) #ifdef USE_SDCARD_SDIO // PLL2M = HSE_VALUE / 1600000 pins the VCO input to exactly 1.6 MHz for any HSE. // With N=500 this gives VCO=800 MHz: PLL2R/4=200 MHz (SDMMC), PLL2P/2=400 MHz. - // For 8 MHz HSE (M=5, N=500) this is identical to the original hardcoded values — - // only KAKUTEH7WING (16 MHz HSE, M=10) is different from the original broken state. - // HSE_VALUE must be a multiple of 1600000; all current H7 targets (8 MHz, 16 MHz) satisfy this. - STATIC_ASSERT(HSE_VALUE % 1600000 == 0, HSE_VALUE_must_be_a_multiple_of_1_6MHz_for_PLL2M_calculation); + STATIC_ASSERT(HSE_VALUE % 1600000 == 0, HSE_not_a_multiple_of_1600000); RCC_PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SDMMC; RCC_PeriphClkInit.PLL2.PLL2M = HSE_VALUE / 1600000; RCC_PeriphClkInit.PLL2.PLL2N = 500; From 5ea966421cd1d51a85348c4c552fa05f1937de38 Mon Sep 17 00:00:00 2001 From: shota hayashi Date: Fri, 6 Feb 2026 07:12:09 +0900 Subject: [PATCH 38/67] Refactor airspeed selection logic in imuCalculateTurnRateacceleration function --- src/main/flight/imu.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/flight/imu.c b/src/main/flight/imu.c index 379a023ffd8..917865d5663 100644 --- a/src/main/flight/imu.c +++ b/src/main/flight/imu.c @@ -729,6 +729,15 @@ static void imuCalculateTurnRateacceleration(fpVector3_t *vEstcentrifugalAccelBF //fixed wing only static float lastspeed = -1.0f; float currentspeed = 0; +#ifdef USE_PITOT + if (pitotValidForAirspeed()) + { + // second choice is pitot + currentspeed = getAirspeedEstimate(); + *acc_ignore_slope_multipiler = 4.0f; + } + else +#endif if (isGPSTrustworthy()) { //first speed choice is gps static bool lastGPSHeartbeat; @@ -742,13 +751,6 @@ static void imuCalculateTurnRateacceleration(fpVector3_t *vEstcentrifugalAccelBF currentspeed = GPS3DspeedFiltered; *acc_ignore_slope_multipiler = 4.0f; } -#ifdef USE_PITOT - else if (sensors(SENSOR_PITOT) && pitotIsHealthy()) { - // second choice is pitot - currentspeed = getAirspeedEstimate(); - *acc_ignore_slope_multipiler = 2.0f; - } -#endif else { //third choice is fixedWingReferenceAirspeed From cf5c6773cfc6ed5c696d69116a8fe4176f47fe2e Mon Sep 17 00:00:00 2001 From: shota hayashi Date: Fri, 6 Feb 2026 22:45:53 +0900 Subject: [PATCH 39/67] Rename pitotValidForAirspeed to pitotGetValidForAirspeed and update references --- src/main/flight/imu.c | 2 +- src/main/flight/pid.c | 2 +- src/main/sensors/pitotmeter.c | 14 +++++++++++--- src/main/sensors/pitotmeter.h | 3 ++- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/main/flight/imu.c b/src/main/flight/imu.c index 917865d5663..093b426ef69 100644 --- a/src/main/flight/imu.c +++ b/src/main/flight/imu.c @@ -730,7 +730,7 @@ static void imuCalculateTurnRateacceleration(fpVector3_t *vEstcentrifugalAccelBF static float lastspeed = -1.0f; float currentspeed = 0; #ifdef USE_PITOT - if (pitotValidForAirspeed()) + if (pitotGetValidForAirspeed()) { // second choice is pitot currentspeed = getAirspeedEstimate(); diff --git a/src/main/flight/pid.c b/src/main/flight/pid.c index 354ffeff371..9beda7ae225 100644 --- a/src/main/flight/pid.c +++ b/src/main/flight/pid.c @@ -553,7 +553,7 @@ void updatePIDCoefficients(void) float tpaFactor=1.0f; float iTermFactor=1.0f; // Separate factor for I-term scaling if(usedPidControllerType == PID_TYPE_PIFF){ // Fixed wing TPA calculation - if(currentControlProfile->throttle.apa_pow>0 && pitotValidForAirspeed()){ + if(currentControlProfile->throttle.apa_pow>0 && pitotGetValidForAirspeed()){ tpaFactor = calculateFixedWingAirspeedTPAFactor(); iTermFactor = calculateFixedWingAirspeedITermFactor(); // Less aggressive I-term scaling }else{ diff --git a/src/main/sensors/pitotmeter.c b/src/main/sensors/pitotmeter.c index 3618923614a..6c6f81aafcc 100755 --- a/src/main/sensors/pitotmeter.c +++ b/src/main/sensors/pitotmeter.c @@ -70,8 +70,9 @@ pitot_t pitot = {.lastMeasurementUs = 0, .lastSeenHealthyMs = 0}; static bool pitotHardwareFailed = false; static uint16_t pitotFailureCounter = 0; static uint16_t pitotRecoveryCounter = 0; -#define PITOT_FAILURE_THRESHOLD 20 // 0.2 seconds at 100Hz - fast detection per LOG00002 analysis -#define PITOT_RECOVERY_THRESHOLD 200 // 2 seconds of consecutive good readings to recover +static bool pitotAirspeedValidCached = false; +#define PITOT_FAILURE_THRESHOLD 10 // 0.2 seconds at 50Hz - fast detection per LOG00002 analysis +#define PITOT_RECOVERY_THRESHOLD 100 // 2 seconds of consecutive good readings to recover // Forward declaration for GPS-based airspeed fallback static float getVirtualAirspeedEstimate(void); @@ -231,6 +232,7 @@ static void performPitotCalibrationCycle(void) } } + STATIC_PROTOTHREAD(pitotThread) { ptBegin(pitotThread); @@ -280,6 +282,7 @@ STATIC_PROTOTHREAD(pitotThread) pitotPressureTmp = sq(fakePitotGetAirspeed()) * SSL_AIR_DENSITY / 20000.0f + SSL_AIR_PRESSURE; } #endif + pitotAirspeedValidCached = pitotValidateAirspeed(); ptYield(); // Calculate IAS @@ -449,7 +452,7 @@ bool pitotHasFailed(void) return pitotHardwareFailed; } -bool pitotValidForAirspeed(void) +bool pitotValidateAirspeed(void) { bool ret = false; ret = pitotIsHealthy() && pitotIsCalibrationComplete(); @@ -508,4 +511,9 @@ bool pitotValidForAirspeed(void) return ret; } + +bool pitotGetValidForAirspeed(void) +{ + return pitotAirspeedValidCached; +} #endif /* PITOT */ diff --git a/src/main/sensors/pitotmeter.h b/src/main/sensors/pitotmeter.h index dc5ac422974..651059f4636 100755 --- a/src/main/sensors/pitotmeter.h +++ b/src/main/sensors/pitotmeter.h @@ -70,7 +70,8 @@ void pitotStartCalibration(void); void pitotUpdate(void); float getAirspeedEstimate(void); bool pitotIsHealthy(void); -bool pitotValidForAirspeed(void); +bool pitotValidateAirspeed(void); +bool pitotGetValidForAirspeed(void); bool pitotHasFailed(void); #endif From b7b4641f3e6c26d27400269988bcc29724939ebd Mon Sep 17 00:00:00 2001 From: shota hayashi Date: Fri, 6 Feb 2026 22:47:04 +0900 Subject: [PATCH 40/67] Update comments for airspeed selection logic in imuCalculateTurnRateacceleration function --- src/main/flight/imu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/flight/imu.c b/src/main/flight/imu.c index 093b426ef69..e5d2e3be75f 100644 --- a/src/main/flight/imu.c +++ b/src/main/flight/imu.c @@ -732,14 +732,14 @@ static void imuCalculateTurnRateacceleration(fpVector3_t *vEstcentrifugalAccelBF #ifdef USE_PITOT if (pitotGetValidForAirspeed()) { - // second choice is pitot + // first choice is airspeed currentspeed = getAirspeedEstimate(); *acc_ignore_slope_multipiler = 4.0f; } else #endif if (isGPSTrustworthy()) { - //first speed choice is gps + // second choice is gps static bool lastGPSHeartbeat; static pt1Filter_t GPS3DspeedFilter; static float GPS3DspeedFiltered = 0.0f; From de0054015ac5215ea63de803eaba52d7ea6d6219 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Sat, 28 Feb 2026 21:45:25 -0600 Subject: [PATCH 41/67] fix: correct ICM42688 bus device hardware type on GEPRC targets The ICM42688 gyro on IMU1 was registered with DEVHW_MPU6000 instead of DEVHW_ICM42605, preventing the ICM42605 driver from detecting it. Boards populated with ICM42688 chips would fail to detect the primary gyro. The IMU2 registration was already correct. Affected targets: GEPRC_TAKER_H743, GEPRCF745_BT_HD Co-Authored-By: Claude Opus 4.6 --- src/main/target/GEPRCF745_BT_HD/target.c | 2 +- src/main/target/GEPRC_TAKER_H743/target.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/target/GEPRCF745_BT_HD/target.c b/src/main/target/GEPRCF745_BT_HD/target.c index 6fe88bde011..91f9957fefc 100644 --- a/src/main/target/GEPRCF745_BT_HD/target.c +++ b/src/main/target/GEPRCF745_BT_HD/target.c @@ -29,7 +29,7 @@ BUSDEV_REGISTER_SPI_TAG(busdev_mpu6000, DEVHW_MPU6000, IMU1_SPI_BUS, IMU1_CS_PIN, NONE, 0, DEVFLAGS_NONE, IMU1_ALIGN); BUSDEV_REGISTER_SPI_TAG(busdev_mpu6000_2, DEVHW_MPU6000, IMU2_SPI_BUS, IMU2_CS_PIN, NONE, 1, DEVFLAGS_NONE, IMU2_ALIGN); -BUSDEV_REGISTER_SPI_TAG(busdev_icm42688, DEVHW_MPU6000, IMU1_SPI_BUS, IMU1_CS_PIN, NONE, 0, DEVFLAGS_NONE, IMU1_ALIGN); +BUSDEV_REGISTER_SPI_TAG(busdev_icm42688, DEVHW_ICM42605, IMU1_SPI_BUS, IMU1_CS_PIN, NONE, 0, DEVFLAGS_NONE, IMU1_ALIGN); BUSDEV_REGISTER_SPI_TAG(busdev_icm42688_2, DEVHW_ICM42605, IMU2_SPI_BUS, IMU2_CS_PIN, NONE, 1, DEVFLAGS_NONE, IMU2_ALIGN); diff --git a/src/main/target/GEPRC_TAKER_H743/target.c b/src/main/target/GEPRC_TAKER_H743/target.c index a04e65b2d58..c0ea036db39 100644 --- a/src/main/target/GEPRC_TAKER_H743/target.c +++ b/src/main/target/GEPRC_TAKER_H743/target.c @@ -29,7 +29,7 @@ BUSDEV_REGISTER_SPI_TAG(busdev_mpu6000, DEVHW_MPU6000, IMU1_SPI_BUS, IMU1_CS_PIN, NONE, 0, DEVFLAGS_NONE, IMU1_ALIGN); BUSDEV_REGISTER_SPI_TAG(busdev_mpu6000_2, DEVHW_MPU6000, IMU2_SPI_BUS, IMU2_CS_PIN, NONE, 1, DEVFLAGS_NONE, IMU2_ALIGN); -BUSDEV_REGISTER_SPI_TAG(busdev_icm42688, DEVHW_MPU6000, IMU1_SPI_BUS, IMU1_CS_PIN, NONE, 0, DEVFLAGS_NONE, IMU1_ALIGN); +BUSDEV_REGISTER_SPI_TAG(busdev_icm42688, DEVHW_ICM42605, IMU1_SPI_BUS, IMU1_CS_PIN, NONE, 0, DEVFLAGS_NONE, IMU1_ALIGN); BUSDEV_REGISTER_SPI_TAG(busdev_icm42688_2, DEVHW_ICM42605, IMU2_SPI_BUS, IMU2_CS_PIN, NONE, 1, DEVFLAGS_NONE, IMU2_ALIGN); From 73df7950dd7896c332469b458cf871ec2f900ab5 Mon Sep 17 00:00:00 2001 From: Error414 Date: Tue, 3 Mar 2026 18:54:58 +0100 Subject: [PATCH 42/67] implement minimal need for softrf --- docs/ADSB.md | 45 +++++++++++ lib/main/MAVLink/common/common.h | 2 + src/main/telemetry/mavlink.c | 130 ++++++++++++++++++------------- 3 files changed, 125 insertions(+), 52 deletions(-) diff --git a/docs/ADSB.md b/docs/ADSB.md index 2e5bef69dfe..6e36bd44b79 100644 --- a/docs/ADSB.md +++ b/docs/ADSB.md @@ -34,6 +34,7 @@ All ADSB receivers which can send Mavlink [ADSB_VEHICLE](https://mavlink.io/en/m * [PINGRX](https://uavionix.com/product/pingrx-pro/) (not tested) * [TT-SC1](https://www.aerobits.pl/product/aero/) (tested) * [ADSBee1090](https://pantsforbirds.com/adsbee-1090/) (tested) +* [SoftRf](https://github.com/lyusupov/SoftRF/wiki/Nano-Edition) (not tested) ## TT-SC1 settings * download software for ADSB TT-SC1 from https://www.aerobits.pl/product/aero/ , file Micro_ADSB_App-vX.XX.X_win_setup.zip and install it @@ -63,3 +64,47 @@ AT+SETTINGS=SAVE * in INAV configurator ports TAB set telemetry MAVLINK, and baudrate 115200 * https://pantsforbirds.com/adsbee-1090/quick-start/ +## SoftRF settings +SoftRF needs more mavlink messages than other receivers, in INAV cli set correct mavlink output. +``` +set mavlink_version = 1 +set mavlink_pos_rate = 2 +save +``` +Baud rate for softRF is 57600. INAV supports only mandatory messages for softRF. +Messages MAVLINK_MSG_ID_SYS_STATUS, MAVLINK_MSG_ID_VFR_HUD, MAVLINK_MSG_ID_ATTITUDE are not supported. + +## Alert and Warning +The ADS-B warning/alert system supports two operating modes, controlled by the parameter osd_adsb_calculation_use_cpa (ON or OFF). + +--- + +### ADS-B Warning and Alert Messages (CPA Mode OFF) +The ADS-B warning/alert system supports two operating modes, controlled by the parameter **osd_adsb_calculation_use_cpa** (ON or OFF). + +When **osd_adsb_calculation_use_cpa = OFF**, the system evaluates only the **current distance between the aircraft and the UAV**. The aircraft with the **shortest distance** is always selected for monitoring. + +- If the aircraft enters the **warning zone** (`adsb_distance_warning`), the corresponding **OSD element is displayed**. +- If the aircraft enters the **alert zone** (`adsb_distance_alert`), the **OSD element starts blinking**, indicating a higher-priority alert. + +This mode therefore provides a simple proximity-based warning determined purely by real-time distance. + +--- + +### ADS-B Warning and Alert Messages (CPA Mode ON) + +When **osd_adsb_calculation_use_cpa = ON**, the system evaluates aircraft using the **Closest Point of Approach (CPA)** and predicted trajectories, not only the current distance. + +1. **Aircraft already inside the alert zone** + If one or more aircraft are currently inside the **alert zone** (`adsb_distance_alert`), the **closest aircraft** to the UAV is selected and the **OSD element blinks**. + +2. **Aircraft in the warning zone, none predicted to enter the alert zone** + If aircraft are present in the **warning zone** (`adsb_distance_warning`), but none of them are predicted to enter the **alert zone** (their CPA distance is greater than `adsb_distance_alert`), the **closest aircraft to the UAV** is selected and the **OSD element remains steady** (no blinking). + +3. **Aircraft in the warning zone, one predicted to enter the alert zone** + If at least one aircraft in the **warning zone** is predicted to enter the **alert zone**, that aircraft is selected and the **OSD element blinks**. + +4. **Aircraft in the warning zone, multiple predicted to enter the alert zone** + If multiple aircraft are predicted to enter the **alert zone**, the system selects the aircraft that will **reach the alert zone first**, and the **OSD element blinks**. + +![ADSB CPA_ON](assets/images/adsb-CPA-on.png) diff --git a/lib/main/MAVLink/common/common.h b/lib/main/MAVLink/common/common.h index 4e47afb03ee..8518d2425d7 100755 --- a/lib/main/MAVLink/common/common.h +++ b/lib/main/MAVLink/common/common.h @@ -987,6 +987,8 @@ typedef enum MAV_DATA_STREAM MAV_DATA_STREAM_RC_CHANNELS=3, /* Enable RC_CHANNELS_SCALED, RC_CHANNELS_RAW, SERVO_OUTPUT_RAW | */ MAV_DATA_STREAM_RAW_CONTROLLER=4, /* Enable ATTITUDE_CONTROLLER_OUTPUT, POSITION_CONTROLLER_OUTPUT, NAV_CONTROLLER_OUTPUT. | */ MAV_DATA_STREAM_POSITION=6, /* Enable LOCAL_POSITION, GLOBAL_POSITION_INT messages. | */ + MAV_DATA_STREAM_SYSTEM_TIME=7, /* Enable SYSTEM_TIME messages. | */ + MAV_DATA_STREAM_HEARTBEAT=8, /* Enable HEARTBEAT messages. | */ MAV_DATA_STREAM_EXTRA1=10, /* Dependent on the autopilot | */ MAV_DATA_STREAM_EXTRA2=11, /* Dependent on the autopilot | */ MAV_DATA_STREAM_EXTRA3=12, /* Dependent on the autopilot | */ diff --git a/src/main/telemetry/mavlink.c b/src/main/telemetry/mavlink.c index d5084b32a62..99ffef9df09 100644 --- a/src/main/telemetry/mavlink.c +++ b/src/main/telemetry/mavlink.c @@ -172,7 +172,9 @@ static uint8_t mavRates[] = { [MAV_DATA_STREAM_POSITION] = 2, // 2Hz [MAV_DATA_STREAM_EXTRA1] = 3, // 3Hz [MAV_DATA_STREAM_EXTRA2] = 2, // 2Hz, HEARTBEATs are important - [MAV_DATA_STREAM_EXTRA3] = 1 // 1Hz + [MAV_DATA_STREAM_EXTRA3] = 1, // 1Hz + [MAV_DATA_STREAM_SYSTEM_TIME] = 1, // 1Hz + [MAV_DATA_STREAM_HEARTBEAT] = 1, // 1Hz }; #define MAXSTREAMS (sizeof(mavRates) / sizeof(mavRates[0])) @@ -704,52 +706,22 @@ void mavlinkSendAttitude(void) mavlinkSendMessage(); } -void mavlinkSendHUDAndHeartbeat(void) +void mavlinkSendSystemTime(void) { - float mavAltitude = 0; - float mavGroundSpeed = 0; - float mavAirSpeed = 0; - float mavClimbRate = 0; + uint64_t timeUnixUsec = 0; + rtcTime_t rtcTime; -#if defined(USE_GPS) - // use ground speed if source available - if (sensors(SENSOR_GPS) -#ifdef USE_GPS_FIX_ESTIMATION - || STATE(GPS_ESTIMATED_FIX) -#endif - ) { - mavGroundSpeed = gpsSol.groundSpeed / 100.0f; + if (rtcGet(&rtcTime)) { + timeUnixUsec = (uint64_t)rtcTime * 1000ULL + (uint64_t)(micros() % 1000); // extrapolation to uS + //timeUnixUsec = (uint64_t)rtcTime * 1000ULL; // mS resolution } -#endif - -#if defined(USE_PITOT) - if (sensors(SENSOR_PITOT) && pitotIsHealthy()) { - mavAirSpeed = getAirspeedEstimate() / 100.0f; - } -#endif - - // select best source for altitude - mavAltitude = getEstimatedActualPosition(Z) / 100.0f; - mavClimbRate = getEstimatedActualVelocity(Z) / 100.0f; - - int16_t thr = getThrottlePercent(osdUsingScaledThrottle()); - mavlink_msg_vfr_hud_pack(mavSystemId, mavComponentId, &mavSendMsg, - // airspeed Current airspeed in m/s - mavAirSpeed, - // groundspeed Current ground speed in m/s - mavGroundSpeed, - // heading Current heading in degrees, in compass units (0..360, 0=north) - DECIDEGREES_TO_DEGREES(attitude.values.yaw), - // throttle Current throttle setting in integer percent, 0 to 100 - thr, - // alt Current altitude (MSL), in meters, if we have surface or baro use them, otherwise use GPS (less accurate) - mavAltitude, - // climb Current climb rate in meters/second - mavClimbRate); + mavlink_msg_system_time_pack(mavSystemId, mavComponentId, &mavSendMsg, timeUnixUsec, millis()); mavlinkSendMessage(); +} - +void mavlinkSendHeartbeat(void) +{ uint8_t mavModes = MAV_MODE_FLAG_MANUAL_INPUT_ENABLED | MAV_MODE_FLAG_CUSTOM_MODE_ENABLED; if (ARMING_FLAG(ARMED)) mavModes |= MAV_MODE_FLAG_SAFETY_ARMED; @@ -821,16 +793,62 @@ void mavlinkSendHUDAndHeartbeat(void) } mavlink_msg_heartbeat_pack(mavSystemId, mavComponentId, &mavSendMsg, - // type Type of the MAV (quadrotor, helicopter, etc., up to 15 types, defined in MAV_TYPE ENUM) - mavSystemType, - // autopilot Autopilot type / class. defined in MAV_AUTOPILOT ENUM - mavType, - // base_mode System mode bitfield, see MAV_MODE_FLAGS ENUM in mavlink/include/mavlink_types.h - mavModes, - // custom_mode A bitfield for use for autopilot-specific flags. - mavCustomMode, - // system_status System status flag, see MAV_STATE ENUM - mavSystemState); + // type Type of the MAV (quadrotor, helicopter, etc., up to 15 types, defined in MAV_TYPE ENUM) + mavSystemType, + // autopilot Autopilot type / class. defined in MAV_AUTOPILOT ENUM + mavType, + // base_mode System mode bitfield, see MAV_MODE_FLAGS ENUM in mavlink/include/mavlink_types.h + mavModes, + // custom_mode A bitfield for use for autopilot-specific flags. + mavCustomMode, + // system_status System status flag, see MAV_STATE ENUM + mavSystemState); + + mavlinkSendMessage(); +} + +void mavlinkSendHUD(void) +{ + float mavAltitude = 0; + float mavGroundSpeed = 0; + float mavAirSpeed = 0; + float mavClimbRate = 0; + +#if defined(USE_GPS) + // use ground speed if source available + if (sensors(SENSOR_GPS) +#ifdef USE_GPS_FIX_ESTIMATION + || STATE(GPS_ESTIMATED_FIX) +#endif + ) { + mavGroundSpeed = gpsSol.groundSpeed / 100.0f; + } +#endif + +#if defined(USE_PITOT) + if (sensors(SENSOR_PITOT) && pitotIsHealthy()) { + mavAirSpeed = getAirspeedEstimate() / 100.0f; + } +#endif + + // select best source for altitude + mavAltitude = getEstimatedActualPosition(Z) / 100.0f; + mavClimbRate = getEstimatedActualVelocity(Z) / 100.0f; + + int16_t thr = getThrottlePercent(osdUsingScaledThrottle()); + mavlink_msg_vfr_hud_pack(mavSystemId, mavComponentId, &mavSendMsg, + // airspeed Current airspeed in m/s + mavAirSpeed, + // groundspeed Current ground speed in m/s + mavGroundSpeed, + // heading Current heading in degrees, in compass units (0..360, 0=north) + DECIDEGREES_TO_DEGREES(attitude.values.yaw), + // throttle Current throttle setting in integer percent, 0 to 100 + thr, + // alt Current altitude (MSL), in meters, if we have surface or baro use them, otherwise use GPS (less accurate) + mavAltitude, + // climb Current climb rate in meters/second + mavClimbRate); mavlinkSendMessage(); } @@ -952,13 +970,21 @@ void processMAVLinkTelemetry(timeUs_t currentTimeUs) } if (mavlinkStreamTrigger(MAV_DATA_STREAM_EXTRA2)) { - mavlinkSendHUDAndHeartbeat(); + mavlinkSendHUD(); } if (mavlinkStreamTrigger(MAV_DATA_STREAM_EXTRA3)) { mavlinkSendBatteryTemperatureStatusText(); } + if (mavlinkStreamTrigger(MAV_DATA_STREAM_HEARTBEAT)) { + mavlinkSendHeartbeat(); + } + + if (mavlinkStreamTrigger(MAV_DATA_STREAM_SYSTEM_TIME)) { + mavlinkSendSystemTime(); + } + } static bool handleIncoming_MISSION_CLEAR_ALL(void) From abfa70302e73adba02a82bbe4b6304c23d913c98 Mon Sep 17 00:00:00 2001 From: Error414 Date: Sun, 3 May 2026 12:12:16 +0200 Subject: [PATCH 43/67] feat: * send GPS in GPS RAW mavlink message * don't yse microsecond extrapolation --- src/main/telemetry/mavlink.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/main/telemetry/mavlink.c b/src/main/telemetry/mavlink.c index 99ffef9df09..bbf37896203 100644 --- a/src/main/telemetry/mavlink.c +++ b/src/main/telemetry/mavlink.c @@ -595,6 +595,8 @@ void mavlinkSendRCChannelsAndRSSI(void) void mavlinkSendPosition(timeUs_t currentTimeUs) { uint8_t gpsFixType = 0; + rtcTime_t rtcTime; + timeUs_t timeUnixUsec = currentTimeUs; if (!(sensors(SENSOR_GPS) #ifdef USE_GPS_FIX_ESTIMATION @@ -603,16 +605,21 @@ void mavlinkSendPosition(timeUs_t currentTimeUs) )) return; - if (gpsSol.fixType == GPS_NO_FIX) + if (gpsSol.fixType == GPS_NO_FIX){ gpsFixType = 1; - else if (gpsSol.fixType == GPS_FIX_2D) - gpsFixType = 2; - else if (gpsSol.fixType == GPS_FIX_3D) - gpsFixType = 3; + } else if (gpsSol.fixType == GPS_FIX_2D) { + gpsFixType = 2; + }else if (gpsSol.fixType == GPS_FIX_3D) { + gpsFixType = 3; + } + + if (rtcGet(&rtcTime)) { + timeUnixUsec = (uint64_t)rtcTime * 1000ULL; + } mavlink_msg_gps_raw_int_pack(mavSystemId, mavComponentId, &mavSendMsg, // time_usec Timestamp (microseconds since UNIX epoch or microseconds since system boot) - currentTimeUs, + timeUnixUsec, // fix_type 0-1: no fix, 2: 2D fix, 3: 3D fix. Some applications will not use the value of this field unless it is at least two, so always correctly fill in the fix. gpsFixType, // lat Latitude in 1E7 degrees @@ -712,8 +719,8 @@ void mavlinkSendSystemTime(void) rtcTime_t rtcTime; if (rtcGet(&rtcTime)) { - timeUnixUsec = (uint64_t)rtcTime * 1000ULL + (uint64_t)(micros() % 1000); // extrapolation to uS - //timeUnixUsec = (uint64_t)rtcTime * 1000ULL; // mS resolution + //timeUnixUsec = (uint64_t)rtcTime * 1000ULL + (uint64_t)(micros() % 1000); // extrapolation to uS + timeUnixUsec = (uint64_t)rtcTime * 1000ULL; // mS resolution } mavlink_msg_system_time_pack(mavSystemId, mavComponentId, &mavSendMsg, timeUnixUsec, millis()); From 43316859be99f39429b94c4ddf42e93653080f50 Mon Sep 17 00:00:00 2001 From: Error414 Date: Fri, 8 May 2026 19:33:23 +0200 Subject: [PATCH 44/67] improve softRF documentation --- docs/ADSB.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/ADSB.md b/docs/ADSB.md index 6e36bd44b79..3b3e54f6f19 100644 --- a/docs/ADSB.md +++ b/docs/ADSB.md @@ -65,14 +65,15 @@ AT+SETTINGS=SAVE * https://pantsforbirds.com/adsbee-1090/quick-start/ ## SoftRF settings -SoftRF needs more mavlink messages than other receivers, in INAV cli set correct mavlink output. +SoftRF supports only mavlink version 1. ``` set mavlink_version = 1 -set mavlink_pos_rate = 2 save ``` -Baud rate for softRF is 57600. INAV supports only mandatory messages for softRF. -Messages MAVLINK_MSG_ID_SYS_STATUS, MAVLINK_MSG_ID_VFR_HUD, MAVLINK_MSG_ID_ATTITUDE are not supported. +The baud rate for SoftRF is 57600. INAV provides minimal support for SoftRF and supports only +the mandatory MAVLink messages: `MAVLINK_MSG_ID_HEARTBEAT`, `MAVLINK_MSG_ID_SYSTEM_TIME`, and `MAVLINK_MSG_ID_GPS_RAW_INT`. + +The following messages are not supported: `MAVLINK_MSG_ID_SYS_STATUS`, `MAVLINK_MSG_ID_VFR_HUD`, and `MAVLINK_MSG_ID_ATTITUDE`. ## Alert and Warning The ADS-B warning/alert system supports two operating modes, controlled by the parameter osd_adsb_calculation_use_cpa (ON or OFF). From ec4efa6ba85d22a3c4cfbe798254784b388f7671 Mon Sep 17 00:00:00 2001 From: Error414 Date: Fri, 8 May 2026 19:36:47 +0200 Subject: [PATCH 45/67] change softrf as tested in doc --- docs/ADSB.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ADSB.md b/docs/ADSB.md index 3b3e54f6f19..dac52b47356 100644 --- a/docs/ADSB.md +++ b/docs/ADSB.md @@ -34,7 +34,7 @@ All ADSB receivers which can send Mavlink [ADSB_VEHICLE](https://mavlink.io/en/m * [PINGRX](https://uavionix.com/product/pingrx-pro/) (not tested) * [TT-SC1](https://www.aerobits.pl/product/aero/) (tested) * [ADSBee1090](https://pantsforbirds.com/adsbee-1090/) (tested) -* [SoftRf](https://github.com/lyusupov/SoftRF/wiki/Nano-Edition) (not tested) +* [SoftRf](https://github.com/lyusupov/SoftRF/wiki/Nano-Edition) (tested) ## TT-SC1 settings * download software for ADSB TT-SC1 from https://www.aerobits.pl/product/aero/ , file Micro_ADSB_App-vX.XX.X_win_setup.zip and install it From 89410f4ecae6a4372943a97f6053ccb43862661f Mon Sep 17 00:00:00 2001 From: Error414 Date: Mon, 18 May 2026 17:19:14 +0200 Subject: [PATCH 46/67] fix qodo suggestions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * timeUnixUsec is declared as timeUs_t ... * The new SoftRF section says SYS_STATUS, VFR_HUD, and ATTITUDE are “not supported”, .... * Spelling/casing: the receiver name is “SoftRF” ... * Spelling/casing: use “MAVLink” ... * Formatting deviates from the prevailing style i ... * The comment on the EXTRA2 stream rate still says “HEARTBEATs are important” ... --- docs/ADSB.md | 4 ++-- src/main/telemetry/mavlink.c | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/ADSB.md b/docs/ADSB.md index dac52b47356..5cbbb52516a 100644 --- a/docs/ADSB.md +++ b/docs/ADSB.md @@ -34,7 +34,7 @@ All ADSB receivers which can send Mavlink [ADSB_VEHICLE](https://mavlink.io/en/m * [PINGRX](https://uavionix.com/product/pingrx-pro/) (not tested) * [TT-SC1](https://www.aerobits.pl/product/aero/) (tested) * [ADSBee1090](https://pantsforbirds.com/adsbee-1090/) (tested) -* [SoftRf](https://github.com/lyusupov/SoftRF/wiki/Nano-Edition) (tested) +* [SoftRF](https://github.com/lyusupov/SoftRF/wiki/Nano-Edition) (tested) ## TT-SC1 settings * download software for ADSB TT-SC1 from https://www.aerobits.pl/product/aero/ , file Micro_ADSB_App-vX.XX.X_win_setup.zip and install it @@ -65,7 +65,7 @@ AT+SETTINGS=SAVE * https://pantsforbirds.com/adsbee-1090/quick-start/ ## SoftRF settings -SoftRF supports only mavlink version 1. +SoftRF supports only MAVLink version 1. ``` set mavlink_version = 1 save diff --git a/src/main/telemetry/mavlink.c b/src/main/telemetry/mavlink.c index bbf37896203..e46d5f324d7 100644 --- a/src/main/telemetry/mavlink.c +++ b/src/main/telemetry/mavlink.c @@ -171,7 +171,7 @@ static uint8_t mavRates[] = { [MAV_DATA_STREAM_RC_CHANNELS] = 1, // 1Hz [MAV_DATA_STREAM_POSITION] = 2, // 2Hz [MAV_DATA_STREAM_EXTRA1] = 3, // 3Hz - [MAV_DATA_STREAM_EXTRA2] = 2, // 2Hz, HEARTBEATs are important + [MAV_DATA_STREAM_EXTRA2] = 2, // 2Hz, HEARTBEATs are important, HEARTBEAT is sent independently 1HZ [MAV_DATA_STREAM_EXTRA3] = 1, // 1Hz [MAV_DATA_STREAM_SYSTEM_TIME] = 1, // 1Hz [MAV_DATA_STREAM_HEARTBEAT] = 1, // 1Hz @@ -596,7 +596,7 @@ void mavlinkSendPosition(timeUs_t currentTimeUs) { uint8_t gpsFixType = 0; rtcTime_t rtcTime; - timeUs_t timeUnixUsec = currentTimeUs; + uint64_t timeUnixUsec = currentTimeUs; if (!(sensors(SENSOR_GPS) #ifdef USE_GPS_FIX_ESTIMATION @@ -605,16 +605,16 @@ void mavlinkSendPosition(timeUs_t currentTimeUs) )) return; - if (gpsSol.fixType == GPS_NO_FIX){ + if (gpsSol.fixType == GPS_NO_FIX) { gpsFixType = 1; } else if (gpsSol.fixType == GPS_FIX_2D) { gpsFixType = 2; - }else if (gpsSol.fixType == GPS_FIX_3D) { + } else if (gpsSol.fixType == GPS_FIX_3D) { gpsFixType = 3; } if (rtcGet(&rtcTime)) { - timeUnixUsec = (uint64_t)rtcTime * 1000ULL; + timeUnixUsec = rtcTime * 1000ULL; } mavlink_msg_gps_raw_int_pack(mavSystemId, mavComponentId, &mavSendMsg, From 9efcbe319df6b923bd7bc106d3153c11bb4a9bd0 Mon Sep 17 00:00:00 2001 From: Error414 Date: Mon, 18 May 2026 19:18:51 +0200 Subject: [PATCH 47/67] fix qodo suggestions * fix mavlink data stream, create new schedule for HB and sys time --- lib/main/MAVLink/common/common.h | 2 -- src/main/telemetry/mavlink.c | 36 +++++++++++++++++++++++++------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/lib/main/MAVLink/common/common.h b/lib/main/MAVLink/common/common.h index 8518d2425d7..4e47afb03ee 100755 --- a/lib/main/MAVLink/common/common.h +++ b/lib/main/MAVLink/common/common.h @@ -987,8 +987,6 @@ typedef enum MAV_DATA_STREAM MAV_DATA_STREAM_RC_CHANNELS=3, /* Enable RC_CHANNELS_SCALED, RC_CHANNELS_RAW, SERVO_OUTPUT_RAW | */ MAV_DATA_STREAM_RAW_CONTROLLER=4, /* Enable ATTITUDE_CONTROLLER_OUTPUT, POSITION_CONTROLLER_OUTPUT, NAV_CONTROLLER_OUTPUT. | */ MAV_DATA_STREAM_POSITION=6, /* Enable LOCAL_POSITION, GLOBAL_POSITION_INT messages. | */ - MAV_DATA_STREAM_SYSTEM_TIME=7, /* Enable SYSTEM_TIME messages. | */ - MAV_DATA_STREAM_HEARTBEAT=8, /* Enable HEARTBEAT messages. | */ MAV_DATA_STREAM_EXTRA1=10, /* Dependent on the autopilot | */ MAV_DATA_STREAM_EXTRA2=11, /* Dependent on the autopilot | */ MAV_DATA_STREAM_EXTRA3=12, /* Dependent on the autopilot | */ diff --git a/src/main/telemetry/mavlink.c b/src/main/telemetry/mavlink.c index e46d5f324d7..6c7e130d06a 100644 --- a/src/main/telemetry/mavlink.c +++ b/src/main/telemetry/mavlink.c @@ -171,12 +171,19 @@ static uint8_t mavRates[] = { [MAV_DATA_STREAM_RC_CHANNELS] = 1, // 1Hz [MAV_DATA_STREAM_POSITION] = 2, // 2Hz [MAV_DATA_STREAM_EXTRA1] = 3, // 3Hz - [MAV_DATA_STREAM_EXTRA2] = 2, // 2Hz, HEARTBEATs are important, HEARTBEAT is sent independently 1HZ + [MAV_DATA_STREAM_EXTRA2] = 2, // 2Hz [MAV_DATA_STREAM_EXTRA3] = 1, // 1Hz - [MAV_DATA_STREAM_SYSTEM_TIME] = 1, // 1Hz - [MAV_DATA_STREAM_HEARTBEAT] = 1, // 1Hz }; +/* HEARTBEAT and SYSTEM_TIME are not part of any MAV_DATA_STREAM; scheduled independently. */ +typedef struct mavlinkScheduledMessage_s { + uint8_t rateHz; // desired transmission rate in Hz + uint8_t ticks; // countdown decremented at TELEMETRY_MAVLINK_MAXRATE +} mavlinkScheduledMessage_t; + +static mavlinkScheduledMessage_t mavHeartbeat = { .rateHz = 1, .ticks = 0 }; +static mavlinkScheduledMessage_t mavSystemTime = { .rateHz = 1, .ticks = TELEMETRY_MAVLINK_MAXRATE / 2 }; // Stagger SYSTEM_TIME by half a period so it doesn't ride in the same TX burst as HEARTBEAT. + #define MAXSTREAMS (sizeof(mavRates) / sizeof(mavRates[0])) static timeUs_t lastMavlinkMessage = 0; @@ -288,6 +295,22 @@ static int mavlinkStreamTrigger(enum MAV_DATA_STREAM streamNum) return 0; } +static bool mavlinkScheduledTrigger(mavlinkScheduledMessage_t *msg) +{ + if (msg->rateHz == 0) { + return false; + } + + if (msg->ticks == 0) { + uint8_t rate = msg->rateHz > TELEMETRY_MAVLINK_MAXRATE ? TELEMETRY_MAVLINK_MAXRATE : msg->rateHz; + msg->ticks = TELEMETRY_MAVLINK_MAXRATE / rate; + return true; + } + + msg->ticks--; + return false; +} + void freeMAVLinkTelemetryPort(void) { closeSerialPort(mavlinkPort); @@ -614,7 +637,7 @@ void mavlinkSendPosition(timeUs_t currentTimeUs) } if (rtcGet(&rtcTime)) { - timeUnixUsec = rtcTime * 1000ULL; + timeUnixUsec = (uint64_t)rtcTime * 1000ULL; } mavlink_msg_gps_raw_int_pack(mavSystemId, mavComponentId, &mavSendMsg, @@ -984,14 +1007,13 @@ void processMAVLinkTelemetry(timeUs_t currentTimeUs) mavlinkSendBatteryTemperatureStatusText(); } - if (mavlinkStreamTrigger(MAV_DATA_STREAM_HEARTBEAT)) { + if (mavlinkScheduledTrigger(&mavHeartbeat)) { mavlinkSendHeartbeat(); } - if (mavlinkStreamTrigger(MAV_DATA_STREAM_SYSTEM_TIME)) { + if (mavlinkScheduledTrigger(&mavSystemTime)) { mavlinkSendSystemTime(); } - } static bool handleIncoming_MISSION_CLEAR_ALL(void) From 7c2090aa57eb0e4fe3af6defd05b55f1b2cfa264 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Sun, 24 May 2026 11:59:55 -0500 Subject: [PATCH 48/67] rename servo mixer MAX to Fixed Value --- docs/VTOL.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/VTOL.md b/docs/VTOL.md index 8341c81086d..403e80d21e7 100644 --- a/docs/VTOL.md +++ b/docs/VTOL.md @@ -133,7 +133,7 @@ save ![Alt text](Screenshots/mixerprofile_fw_mixer.png) -You must also assign the tilting servos values using the MAX values. If you don't do this the motors will point in the direction assigned by the transition mode. +You must also assign the tilting servos values using the Fixed Value values (formerly called "MAX"). If you don't do this the motors will point in the direction assigned by the transition mode. # STEP 2: Configuring as a Multi-Copter in Profile 2 @@ -149,7 +149,7 @@ You must also assign the tilting servos values using the MAX values. If you don 2. **Configure the Multicopter/tricopter:** - Set up your multi-copter/fixed-wing as usual, this time for mixer_profile 2 and control_profile 2. - - Utilize the 'MAX' input in the servo mixer to tilt the motors without altering the servo midpoint. + - Utilize the Fixed Value values (formerly called "MAX") input in the servo mixer to tilt the motors without altering the servo midpoint. - At this stage, focus on configuring profile-specific settings. You can streamline this process by copying and pasting the default PID settings. - you can set -1 in motor mixer throttle as a place holder: this will disable that motor but will load following the motor rules - compass is required to enable navigation modes for multi-rotor profile. @@ -199,7 +199,7 @@ The steps below describe how you can fine-tune the tilting servos to obtian the 2. **Switch to Multicopter/Tricopter:** - Assuming that you have set up your mixer similar to STEP1 and STEP2, you can now switch to the tricopter/multicopter mode and your servos should be tilting the motors upwards. If this is not the case, reverse the servo(s) in the Outputs tab such that the servo(s) is/are pointed upwards. - It is OK for the servos not to point exactly 90 degrees upwards, but they should be as close as possible to that position. - - Also, ensure that your MAX values in the Mixer tab are at 100 and -100, so that your servo will move to the maximum position, as shown in the screenshots in STEP1 and STEP2. + - Also, ensure that your Fixed Value values (formerly called "MAX") values in the Mixer tab are at 100 and -100, so that your servo will move to the maximum position, as shown in the screenshots in STEP1 and STEP2. 3. **Adjust the maximum throws for the Multicopter/Tricopter mode:** - While in tricopter mode, navigate to the Outputs tab and adjust the MIN and MAX endpoint values to position the motors slightly backward. @@ -214,7 +214,7 @@ The steps below describe how you can fine-tune the tilting servos to obtian the 5. **Adjsut the vertival position of the tilt servos:** - Switch back to multicopter/tricopter mode and open the Mixer tab. - - Start adjusting the `MAX` mixer lines from STEP2 such that the servos are pointed exactly upwards. In other words, start reducing the values of 100 and -100 to something like 80 and -80 until the motors are are pointed exaxctly upwards. + - Start adjusting the `Fixed Value` mixer lines from STEP2 such that the servos are pointed exactly upwards. In other words, start reducing the values of 100 and -100 to something like 80 and -80 until the motors are are pointed exaxctly upwards. - You will have to `Save & reboot` for adjustement for the changes to take effect, so be patient, take your time and don't forget to `Save & reboot`. - Move the YAW stick to either extreme position and ensure that the servos are tilting the motors both forwards and backwards. - NOTE: When yawing fully left, the left motor should tilt backwards and the right motor should tilt forwards. From b905af02aa040b8488c0b6b5d4f46f11417ea561 Mon Sep 17 00:00:00 2001 From: Sensei Date: Sat, 30 May 2026 15:18:12 -0500 Subject: [PATCH 49/67] Add new target: AEDROXH7 (Airbot Systems AEDROX H7) * Add new target: AEDROXH7 (Airbot Systems AEDROX H7) STM32H743 flight controller with ICM42688P gyro, W25N01G NAND flash, DPS310 baro, MAX7456 analog OSD, HD OSD via MSP DisplayPort, and 8 motor outputs split across TIM1 and TIM8. SPI3 MOSI on PB2 requires explicit GPIO_AF7_SPI3 override. Magnetometer enabled on I2C1 (hardware present, not in BF config). * AEDROXH7: replace UART4 with DroneCAN on PD0/PD1 PD0/PD1 connect to the CAN transceiver (CANL/CANH pads visible on board), not to a user-accessible UART4 header. Replace UART4 with USE_DRONECAN defines. CAN standby pin TBD pending INAV syntax confirmation. * AEDROXH7: add CAN1_STANDBY PD3 (not yet tested) * CI: re-trigger build to pick up fc_msp.c sign-compare fix in maintenance-9.x --- src/main/target/AEDROXH7/CMakeLists.txt | 1 + src/main/target/AEDROXH7/config.c | 52 +++++++ src/main/target/AEDROXH7/target.c | 52 +++++++ src/main/target/AEDROXH7/target.h | 179 ++++++++++++++++++++++++ 4 files changed, 284 insertions(+) create mode 100644 src/main/target/AEDROXH7/CMakeLists.txt create mode 100644 src/main/target/AEDROXH7/config.c create mode 100644 src/main/target/AEDROXH7/target.c create mode 100644 src/main/target/AEDROXH7/target.h diff --git a/src/main/target/AEDROXH7/CMakeLists.txt b/src/main/target/AEDROXH7/CMakeLists.txt new file mode 100644 index 00000000000..7842a1d933a --- /dev/null +++ b/src/main/target/AEDROXH7/CMakeLists.txt @@ -0,0 +1 @@ +target_stm32h743xi(AEDROXH7) diff --git a/src/main/target/AEDROXH7/config.c b/src/main/target/AEDROXH7/config.c new file mode 100644 index 00000000000..de29bf3d716 --- /dev/null +++ b/src/main/target/AEDROXH7/config.c @@ -0,0 +1,52 @@ +/* + * This file is part of INAV. + * + * INAV is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * INAV is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with INAV. If not, see . + */ + +#include +#include + +#include + +#include "config/config_master.h" + +#include "fc/fc_msp_box.h" +#include "fc/config.h" + +#include "io/serial.h" +#include "io/piniobox.h" + +void targetConfiguration(void) +{ + // GPS on UART2 + serialConfigMutable()->portConfigs[findSerialPortIndexByIdentifier(SERIAL_PORT_USART2)].functionMask = FUNCTION_GPS; + serialConfigMutable()->portConfigs[findSerialPortIndexByIdentifier(SERIAL_PORT_USART2)].gps_baudrateIndex = BAUD_115200; + + // ESC telemetry on UART7 (RX only) + serialConfigMutable()->portConfigs[findSerialPortIndexByIdentifier(SERIAL_PORT_USART7)].functionMask = FUNCTION_ESCSERIAL; + + // HD OSD via MSP DisplayPort on UART8 + serialConfigMutable()->portConfigs[findSerialPortIndexByIdentifier(SERIAL_PORT_USART8)].functionMask = FUNCTION_MSP; + serialConfigMutable()->portConfigs[findSerialPortIndexByIdentifier(SERIAL_PORT_USART8)].msp_baudrateIndex = BAUD_115200; + + // PINIO boxes: USER1-4 mapped to switch boxes + pinioBoxConfigMutable()->permanentId[0] = BOX_PERMANENT_ID_USER1; + pinioBoxConfigMutable()->permanentId[1] = BOX_PERMANENT_ID_USER2; + pinioBoxConfigMutable()->permanentId[2] = BOX_PERMANENT_ID_USER3; + pinioBoxConfigMutable()->permanentId[3] = BOX_PERMANENT_ID_USER4; + + // Enable PWM drive for passive beeper on PA7 / TIM3_CH2 + beeperConfigMutable()->pwmMode = true; +} diff --git a/src/main/target/AEDROXH7/target.c b/src/main/target/AEDROXH7/target.c new file mode 100644 index 00000000000..250f7ba4bba --- /dev/null +++ b/src/main/target/AEDROXH7/target.c @@ -0,0 +1,52 @@ +/* + * This file is part of INAV. + * + * INAV is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * INAV is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with INAV. If not, see . + */ + +#include + +#include "platform.h" + +#include "drivers/bus.h" +#include "drivers/io.h" +#include "drivers/pwm_mapping.h" +#include "drivers/timer.h" +#include "drivers/pinio.h" +#include "drivers/sensor.h" + +// ICM42688P: DEVHW_ICM42605 driver detects both via WHO_AM_I +BUSDEV_REGISTER_SPI_TAG(busdev_icm42688, DEVHW_ICM42605, ICM42605_SPI_BUS, ICM42605_CS_PIN, ICM42605_EXTI_PIN, 0, DEVFLAGS_NONE, IMU_ICM42605_ALIGN); + +timerHardware_t timerHardware[] = { + // Motors M1-M4: TIM8 (CH mapping follows board silkscreen, not channel order) + DEF_TIM(TIM8, CH2, PC7, TIM_USE_OUTPUT_AUTO, 0, 0), // M1 + DEF_TIM(TIM8, CH1, PC6, TIM_USE_OUTPUT_AUTO, 0, 1), // M2 + DEF_TIM(TIM8, CH4, PC9, TIM_USE_OUTPUT_AUTO, 0, 2), // M3 + DEF_TIM(TIM8, CH3, PC8, TIM_USE_OUTPUT_AUTO, 0, 3), // M4 + + // Motors M5-M8: TIM1 on PE9/PE11/PE13/PE14 (AF1) + DEF_TIM(TIM1, CH1, PE9, TIM_USE_OUTPUT_AUTO, 0, 4), // M5 + DEF_TIM(TIM1, CH2, PE11, TIM_USE_OUTPUT_AUTO, 0, 5), // M6 + DEF_TIM(TIM1, CH3, PE13, TIM_USE_OUTPUT_AUTO, 0, 6), // M7 + DEF_TIM(TIM1, CH4, PE14, TIM_USE_OUTPUT_AUTO, 0, 7), // M8 + + // LED strip: TIM2_CH1 on PA5 (AF1) — separate timer from motors + DEF_TIM(TIM2, CH1, PA5, TIM_USE_LED, 0, 0), // LED strip + + // Beeper PWM: TIM3_CH2 on PA7 (AF2) + DEF_TIM(TIM3, CH2, PA7, TIM_USE_BEEPER, 0, 0), // Beeper +}; + +const int timerHardwareCount = sizeof(timerHardware) / sizeof(timerHardware[0]); diff --git a/src/main/target/AEDROXH7/target.h b/src/main/target/AEDROXH7/target.h new file mode 100644 index 00000000000..16fa802d48b --- /dev/null +++ b/src/main/target/AEDROXH7/target.h @@ -0,0 +1,179 @@ +/* + * This file is part of INAV. + * + * INAV is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * INAV is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with INAV. If not, see . + */ + +#pragma once + +#define TARGET_BOARD_IDENTIFIER "AEDH" +#define USBD_PRODUCT_STRING "AEDROXH7" + +#define USE_TARGET_CONFIG + +// *************** LEDs *************************** +#define LED0 PE5 +#define LED1 PE2 + +// *************** Beeper ************************* +#define BEEPER PA7 +#define BEEPER_INVERTED +#define BEEPER_PWM_FREQUENCY 2500 + +// *************** SPI1 - OSD (MAX7456) *********** +#define USE_SPI +#define USE_SPI_DEVICE_1 +#define SPI1_SCK_PIN PB3 +#define SPI1_MISO_PIN PB4 +#define SPI1_MOSI_PIN PB5 + +#define USE_MAX7456 +#define MAX7456_SPI_BUS BUS_SPI1 +#define MAX7456_CS_PIN PE4 + +// *************** SPI2 - Gyro (ICM42688P) ******** +#define USE_SPI_DEVICE_2 +#define SPI2_SCK_PIN PB13 +#define SPI2_MISO_PIN PB14 +#define SPI2_MOSI_PIN PB15 + +#define USE_TARGET_IMU_HARDWARE_DESCRIPTORS + +#define USE_IMU_ICM42605 +#define IMU_ICM42605_ALIGN CW90_DEG +#define ICM42605_SPI_BUS BUS_SPI2 +#define ICM42605_CS_PIN PB12 +#define ICM42605_EXTI_PIN PC4 + +// *************** SPI3 - Flash (W25N01G) ********* +#define USE_SPI_DEVICE_3 +#define SPI3_SCK_PIN PC10 +#define SPI3_MISO_PIN PC11 +#define SPI3_MOSI_PIN PB2 +#define SPI3_NSS_PIN PA15 +// PB2 requires AF7 for SPI3 MOSI — AF6 is the default and incorrect for this pin +#define SPI3_SCK_AF GPIO_AF6_SPI3 +#define SPI3_MISO_AF GPIO_AF6_SPI3 +#define SPI3_MOSI_AF GPIO_AF7_SPI3 + +#define USE_BLACKBOX +#define USE_FLASHFS +#define USE_FLASH_W25N01G +#define W25N01G_SPI_BUS BUS_SPI3 +#define W25N01G_CS_PIN PA15 +#define ENABLE_BLACKBOX_LOGGING_ON_SPIFLASH_BY_DEFAULT + +// *************** I2C1 - Magnetometer ************ +#define USE_I2C +#define USE_I2C_DEVICE_1 +#define I2C1_SCL PB6 +#define I2C1_SDA PB7 + +#define USE_MAG +#define MAG_I2C_BUS BUS_I2C1 +#define USE_MAG_ALL + +// *************** I2C2 - Barometer (DPS310) ****** +#define USE_I2C_DEVICE_2 +#define I2C2_SCL PB10 +#define I2C2_SDA PB11 + +#define USE_BARO +#define BARO_I2C_BUS BUS_I2C2 +#define USE_BARO_ALL + +#define TEMPERATURE_I2C_BUS BUS_I2C2 +#define PITOT_I2C_BUS BUS_I2C1 +#define RANGEFINDER_I2C_BUS BUS_I2C1 +#define USE_RANGEFINDER + +// *************** UARTs ************************** +// UART5 not wired; UART6 pins PC6/PC7 used as motor outputs +#define USE_VCP + +#define USE_UART1 +#define UART1_TX_PIN PA9 +#define UART1_RX_PIN PA10 + +#define USE_UART2 +#define UART2_TX_PIN PD5 +#define UART2_RX_PIN PD6 + +#define USE_UART3 +#define UART3_TX_PIN PD8 +#define UART3_RX_PIN PD9 + +// *************** CAN Bus ************************* +// CAN not yet tested +#define USE_DRONECAN +#define CAN1_RX PD0 +#define CAN1_TX PD1 +#define CAN1_STANDBY PD3 + +#define USE_UART7 +#define UART7_TX_PIN PE8 +#define UART7_RX_PIN PE7 + +#define USE_UART8 +#define UART8_TX_PIN PE1 +#define UART8_RX_PIN PE0 + +#define SERIAL_PORT_COUNT 6 // VCP + UART1-3 + UART7 + UART8 + +#define DEFAULT_RX_TYPE RX_TYPE_SERIAL +#define SERIALRX_PROVIDER SERIALRX_CRSF +#define SERIALRX_UART SERIAL_PORT_USART3 + +// *************** ADC **************************** +#define USE_ADC +#define ADC_INSTANCE ADC1 + +#define ADC_CHANNEL_1_PIN PC0 +#define ADC_CHANNEL_2_PIN PC1 +#define ADC_CHANNEL_3_PIN PC5 + +#define VBAT_ADC_CHANNEL ADC_CHN_1 +#define CURRENT_METER_ADC_CHANNEL ADC_CHN_2 +#define RSSI_ADC_CHANNEL ADC_CHN_3 + +// *************** LED Strip ********************** +#define USE_LED_STRIP +#define WS2811_PIN PA5 + +// *************** PINIO ************************** +#define USE_PINIO +#define USE_PINIOBOX +#define PINIO1_PIN PA2 +#define PINIO2_PIN PA3 +#define PINIO3_PIN PB1 +#define PINIO3_FLAGS PINIO_FLAGS_INVERTED +#define PINIO4_PIN PD15 + +// *************** Features *********************** +#define DEFAULT_FEATURES (FEATURE_OSD | FEATURE_TELEMETRY | FEATURE_CURRENT_METER | FEATURE_VBAT | FEATURE_TX_PROF_SEL | FEATURE_BLACKBOX) +#define CURRENT_METER_SCALE 250 + +#define USE_SERIAL_4WAY_BLHELI_INTERFACE +#define USE_ESC_SENSOR + +// *************** IO Port Masks ****************** +#define TARGET_IO_PORTA (0xffff & ~(BIT(13) | BIT(14))) +#define TARGET_IO_PORTB 0xffff +#define TARGET_IO_PORTC 0xffff +#define TARGET_IO_PORTD 0xffff +#define TARGET_IO_PORTE 0xffff + +// *************** PWM Outputs ******************** +#define MAX_PWM_OUTPUT_PORTS 8 +#define USE_DSHOT From 033388d55c25c7020e065e74cc90559ea75505f8 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Tue, 26 May 2026 02:07:36 -0500 Subject: [PATCH 50/67] drivers: fix DMA request disable ordering in timer IRQ handlers and stop functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit STM32 RM (RM0090 §10.3.17) requires disabling the timer DMA request before disabling the DMA stream. The previous code had this backwards in five places across three driver files, creating a race window where the timer could issue a DMA request to an already-disabled stream. The incorrect ordering: DMA_Cmd(stream, DISABLE); // stream disabled first TIM_DMACmd(tim, source, DISABLE); // timer request disabled second Corrected ordering: TIM_DMACmd(tim, source, DISABLE); // timer request disabled first DMA_Cmd(stream, DISABLE); // stream disabled second Affected locations: - timer_impl_stdperiph.c: impl_timerPWMStopDMA, impl_timerPWMPrepareDMA, DMA TC IRQ handler - timer_impl_stdperiph_at32.c: impl_timerPWMStopDMA - timer_impl_hal.c: DMA TC IRQ handler The correct ordering was already used in impl_timerPWMSetDMACircular (both stdperiph files) and impl_timerPWMStopDMA (HAL file), which serve as the reference implementation. --- src/main/drivers/timer_impl_hal.c | 2 +- src/main/drivers/timer_impl_stdperiph.c | 6 +++--- src/main/drivers/timer_impl_stdperiph_at32.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/drivers/timer_impl_hal.c b/src/main/drivers/timer_impl_hal.c index 9c4093bafa8..a24875eec08 100644 --- a/src/main/drivers/timer_impl_hal.c +++ b/src/main/drivers/timer_impl_hal.c @@ -330,8 +330,8 @@ static void impl_timerDMA_IRQHandler(DMA_t descriptor) tch->dmaState = TCH_DMA_IDLE; } - LL_DMA_DisableStream(tch->dma->dma, lookupDMALLStreamTable[DMATAG_GET_STREAM(tch->timHw->dmaTag)]); LL_TIM_DisableDMAReq_CCx(tch->timHw->tim, lookupDMASourceTable[tch->timHw->channelIndex]); + LL_DMA_DisableStream(tch->dma->dma, lookupDMALLStreamTable[DMATAG_GET_STREAM(tch->timHw->dmaTag)]); DMA_CLEAR_FLAG(descriptor, DMA_IT_TCIF); } diff --git a/src/main/drivers/timer_impl_stdperiph.c b/src/main/drivers/timer_impl_stdperiph.c index 5b52cb862a4..13b38cdd2b3 100644 --- a/src/main/drivers/timer_impl_stdperiph.c +++ b/src/main/drivers/timer_impl_stdperiph.c @@ -279,8 +279,8 @@ static void impl_timerDMA_IRQHandler(DMA_t descriptor) tch->dmaState = TCH_DMA_IDLE; - DMA_Cmd(tch->dma->ref, DISABLE); TIM_DMACmd(tch->timHw->tim, lookupDMASourceTable[tch->timHw->channelIndex], DISABLE); + DMA_Cmd(tch->dma->ref, DISABLE); DMA_CLEAR_FLAG(descriptor, DMA_IT_TCIF); } @@ -518,8 +518,8 @@ void impl_timerPWMPrepareDMA(TCH_t * tch, uint32_t dmaBufferElementCount) // Clear the flag as well, so even if DMA transfer finishes while within ATOMIC_BLOCK // the resulting IRQ won't mess up the DMA state ATOMIC_BLOCK(NVIC_PRIO_MAX) { - DMA_Cmd(tch->dma->ref, DISABLE); TIM_DMACmd(tch->timHw->tim, lookupDMASourceTable[tch->timHw->channelIndex], DISABLE); + DMA_Cmd(tch->dma->ref, DISABLE); DMA_CLEAR_FLAG(tch->dma, DMA_IT_TCIF); } @@ -561,8 +561,8 @@ void impl_timerPWMStartDMA(TCH_t * tch) void impl_timerPWMStopDMA(TCH_t * tch) { - DMA_Cmd(tch->dma->ref, DISABLE); TIM_DMACmd(tch->timHw->tim, lookupDMASourceTable[tch->timHw->channelIndex], DISABLE); + DMA_Cmd(tch->dma->ref, DISABLE); tch->dmaState = TCH_DMA_IDLE; TIM_Cmd(tch->timHw->tim, ENABLE); } diff --git a/src/main/drivers/timer_impl_stdperiph_at32.c b/src/main/drivers/timer_impl_stdperiph_at32.c index 782943ab25d..54c6d257078 100644 --- a/src/main/drivers/timer_impl_stdperiph_at32.c +++ b/src/main/drivers/timer_impl_stdperiph_at32.c @@ -409,8 +409,8 @@ void impl_timerPWMStartDMA(TCH_t * tch) void impl_timerPWMStopDMA(TCH_t * tch) { - dma_channel_enable(tch->dma->ref,FALSE); tmr_dma_request_enable(tch->timHw->tim, lookupDMASourceTable[tch->timHw->channelIndex], FALSE); + dma_channel_enable(tch->dma->ref,FALSE); tch->dmaState = TCH_DMA_IDLE; tmr_counter_enable(tch->timHw->tim, TRUE); } From 633b471682342d141fe9482910da4e92c5f1823b Mon Sep 17 00:00:00 2001 From: XTXTECH Date: Wed, 27 May 2026 10:30:49 +0800 Subject: [PATCH 51/67] Add support for XTX XT25F128F nor flash chip --- src/main/drivers/flash_m25p16.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/drivers/flash_m25p16.c b/src/main/drivers/flash_m25p16.c index 2db61a3a166..c6d5543e448 100644 --- a/src/main/drivers/flash_m25p16.c +++ b/src/main/drivers/flash_m25p16.c @@ -118,6 +118,9 @@ struct { {0x1C3017, 128, 256}, // JEDEC_ID_SPANSION_S25FL116 {0x014015, 32, 256 }, + // JEDEC_ID_XTX_XT25F128F + // Datasheet: https://www.xtxtech.com/Products/info_productModel_XT25F128FSSIGT.html + {0x0B4018, 256, 256 }, // End of list {0x000000, 0, 0}}; From f650829e5b3f5ba10076ebbe71a31c69c4c4be4a Mon Sep 17 00:00:00 2001 From: dtrbinh Date: Thu, 28 May 2026 12:21:03 +0700 Subject: [PATCH 52/67] JHEMCUF435: enable UART2 --- src/main/target/JHEMCUF435/target.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/target/JHEMCUF435/target.h b/src/main/target/JHEMCUF435/target.h index db165c558b8..edbc3d0bbda 100644 --- a/src/main/target/JHEMCUF435/target.h +++ b/src/main/target/JHEMCUF435/target.h @@ -114,15 +114,11 @@ #define UART1_RX_PIN PA10 #define UART1_TX_PIN PA9 -// UART2 TX does not work due currently unknown software issue -// Leaving this info here for future work. -/* #define USE_UART2 #define UART2_RX_AF 6 #define UART2_TX_AF 8 #define UART2_RX_PIN PB0 #define UART2_TX_PIN PA8 -*/ #define USE_UART3 #define UART3_RX_PIN PB11 @@ -149,7 +145,7 @@ #define UART8_RX_PIN PC3 #define UART8_TX_PIN PC2 -#define SERIAL_PORT_COUNT 8 +#define SERIAL_PORT_COUNT 9 #define DEFAULT_RX_TYPE RX_TYPE_SERIAL #define SERIALRX_PROVIDER SERIALRX_CRSF From 7bc73ad6184ea35dab97644e5db027a2a979105a Mon Sep 17 00:00:00 2001 From: FlyingRC-Official <118458039+FlyingRC-Official@users.noreply.github.com> Date: Fri, 10 Apr 2026 13:36:47 +0800 Subject: [PATCH 53/67] feat(target): rename FlyingRC F4Wing Mini target and add BMI270 support --- docs/boards/FlyingRC F4Wing Mini.md | 6 +++--- src/main/target/FLYINGRCF4WINGMINI/CMakeLists.txt | 1 + .../config.c | 0 .../target.c | 0 .../target.h | 9 +++++++-- .../FLYINGRCF4WINGMINI_NOT_RECOMMENDED/CMakeLists.txt | 1 - 6 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 src/main/target/FLYINGRCF4WINGMINI/CMakeLists.txt rename src/main/target/{FLYINGRCF4WINGMINI_NOT_RECOMMENDED => FLYINGRCF4WINGMINI}/config.c (100%) rename src/main/target/{FLYINGRCF4WINGMINI_NOT_RECOMMENDED => FLYINGRCF4WINGMINI}/target.c (100%) rename src/main/target/{FLYINGRCF4WINGMINI_NOT_RECOMMENDED => FLYINGRCF4WINGMINI}/target.h (94%) delete mode 100644 src/main/target/FLYINGRCF4WINGMINI_NOT_RECOMMENDED/CMakeLists.txt diff --git a/docs/boards/FlyingRC F4Wing Mini.md b/docs/boards/FlyingRC F4Wing Mini.md index b51907870dc..d3160537032 100644 --- a/docs/boards/FlyingRC F4Wing Mini.md +++ b/docs/boards/FlyingRC F4Wing Mini.md @@ -1,4 +1,4 @@ -# Board - FLYINGRCF4WINGMINI_NOT_RECOMMENDED +# Board - FLYINGRCF4WINGMINI This is a cheap flight controller (prices range from $16US to $40US) from an unknown company. Many of the components on this FC are likely to have high tollerances due to the low cost. They sold this FC as compatible with INAV without reaching out to the team or having an official target made. The target only exists thanks to a community contributor (dixi83). @@ -13,7 +13,7 @@ Hardware issues have been reported on these flight controllers. They are also mi | | | |-----|-----| | MCU | STM32F405RTG6 | -| Gyro | ICM-42605 | +| Gyro | ICM-42605 / BMI270 | | Baro | SPL06 | | UARTS | 1, 2 (RX only - SBUS), 4 (DJI), 5 | | PWM | Six + One (S12 used for LED control) | @@ -31,7 +31,7 @@ Hardware issues have been reported on these flight controllers. They are also mi > There is no ADC for "voltmeter" input. So potentially this FC can run at 1S to 6S. However there are only 2 LDO regulators on the FC itself. ## Notable missing features -* Current sensor +* Current senso * Blackbox recording * Analogue OSD * PINIO (for VTX power switching etc) diff --git a/src/main/target/FLYINGRCF4WINGMINI/CMakeLists.txt b/src/main/target/FLYINGRCF4WINGMINI/CMakeLists.txt new file mode 100644 index 00000000000..ef771cdd4e4 --- /dev/null +++ b/src/main/target/FLYINGRCF4WINGMINI/CMakeLists.txt @@ -0,0 +1 @@ +target_stm32f405xg(FLYINGRCF4WINGMINI) diff --git a/src/main/target/FLYINGRCF4WINGMINI_NOT_RECOMMENDED/config.c b/src/main/target/FLYINGRCF4WINGMINI/config.c similarity index 100% rename from src/main/target/FLYINGRCF4WINGMINI_NOT_RECOMMENDED/config.c rename to src/main/target/FLYINGRCF4WINGMINI/config.c diff --git a/src/main/target/FLYINGRCF4WINGMINI_NOT_RECOMMENDED/target.c b/src/main/target/FLYINGRCF4WINGMINI/target.c similarity index 100% rename from src/main/target/FLYINGRCF4WINGMINI_NOT_RECOMMENDED/target.c rename to src/main/target/FLYINGRCF4WINGMINI/target.c diff --git a/src/main/target/FLYINGRCF4WINGMINI_NOT_RECOMMENDED/target.h b/src/main/target/FLYINGRCF4WINGMINI/target.h similarity index 94% rename from src/main/target/FLYINGRCF4WINGMINI_NOT_RECOMMENDED/target.h rename to src/main/target/FLYINGRCF4WINGMINI/target.h index 8eecb2c466c..250ccc3d134 100644 --- a/src/main/target/FLYINGRCF4WINGMINI_NOT_RECOMMENDED/target.h +++ b/src/main/target/FLYINGRCF4WINGMINI/target.h @@ -3,7 +3,7 @@ * * INAV is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or + * the Free Software Foundation, either version 3 of the License, o * (at your option) any later version. * * INAV is distributed in the hope that it will be useful, @@ -19,7 +19,7 @@ #define USE_TARGET_CONFIG #define TARGET_BOARD_IDENTIFIER "FRF4WM" -#define USBD_PRODUCT_STRING "FLYINGRCF4WINGMINI_NOT_RECOMMENDED" +#define USBD_PRODUCT_STRING "FLYINGRCF4WINGMINI" #define LED0 PA14 //Blue #define LED1 PA13 //Green @@ -37,6 +37,11 @@ #define ICM42605_SPI_BUS BUS_SPI1 #define ICM42605_CS_PIN PC14 +#define USE_IMU_BMI270 +#define IMU_BMI270_ALIGN CW90_DEG_FLIP +#define BMI270_SPI_BUS BUS_SPI1 +#define BMI270_CS_PIN PC14 + // *************** I2C /Baro/Mag ********************* #define USE_I2C #define USE_I2C_DEVICE_1 diff --git a/src/main/target/FLYINGRCF4WINGMINI_NOT_RECOMMENDED/CMakeLists.txt b/src/main/target/FLYINGRCF4WINGMINI_NOT_RECOMMENDED/CMakeLists.txt deleted file mode 100644 index 9c9a729ef0a..00000000000 --- a/src/main/target/FLYINGRCF4WINGMINI_NOT_RECOMMENDED/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -target_stm32f405xg(FLYINGRCF4WINGMINI_NOT_RECOMMENDED NO_BOOTLOADER) From ad703a69c9d7ae8e19b27f72664dcee7b43c96f0 Mon Sep 17 00:00:00 2001 From: FlyingRC-Official <118458039+FlyingRC-Official@users.noreply.github.com> Date: Fri, 10 Apr 2026 20:16:31 +0800 Subject: [PATCH 54/67] fix(target): correct default gyro orientations --- src/main/target/FLYINGRCF4WINGMINI/target.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/target/FLYINGRCF4WINGMINI/target.h b/src/main/target/FLYINGRCF4WINGMINI/target.h index 250ccc3d134..3d714dd423a 100644 --- a/src/main/target/FLYINGRCF4WINGMINI/target.h +++ b/src/main/target/FLYINGRCF4WINGMINI/target.h @@ -33,12 +33,12 @@ #define SPI1_MOSI_PIN PA7 #define USE_IMU_ICM42605 -#define IMU_ICM42605_ALIGN CW90_DEG_FLIP +#define IMU_ICM42605_ALIGN CW270_DEG_FLIP #define ICM42605_SPI_BUS BUS_SPI1 #define ICM42605_CS_PIN PC14 #define USE_IMU_BMI270 -#define IMU_BMI270_ALIGN CW90_DEG_FLIP +#define IMU_BMI270_ALIGN CW180_DEG_FLIP #define BMI270_SPI_BUS BUS_SPI1 #define BMI270_CS_PIN PC14 From 1b142d9dd4cba93e72379df8b61e4884c97bc4f8 Mon Sep 17 00:00:00 2001 From: FlyingRC-Official <118458039+FlyingRC-Official@users.noreply.github.com> Date: Tue, 26 May 2026 13:32:48 +0800 Subject: [PATCH 55/67] Polish FlyingRC F4Wing Mini documentation --- docs/boards/FlyingRC F4Wing Mini.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/boards/FlyingRC F4Wing Mini.md b/docs/boards/FlyingRC F4Wing Mini.md index d3160537032..15587b90451 100644 --- a/docs/boards/FlyingRC F4Wing Mini.md +++ b/docs/boards/FlyingRC F4Wing Mini.md @@ -1,11 +1,13 @@ # Board - FLYINGRCF4WINGMINI -This is a cheap flight controller (prices range from $16US to $40US) from an unknown company. Many of the components on this FC are likely to have high tollerances due to the low cost. They sold this FC as compatible with INAV without reaching out to the team or having an official target made. The target only exists thanks to a community contributor (dixi83). +This is a budget-friendly flight controller, typically sold in the US$16-40 range. As with many entry-level boards, component tolerances and quality control may vary compared with higher-cost hardware, so users should test critical functions carefully before flying. The board has been advertised as INAV-compatible, but it was not originally submitted through the official INAV target process; current support exists thanks to community work, especially the contribution from dixi83. + +FlyingRC is a small hardware vendor focused on affordable flight-controller and RC electronics products. More information about their work and open-source activity is available on their GitHub page: https://github.com/FlyingRC-Official Hardware issues have been reported on these flight controllers. They are also missing many features. Unlike most other _wing_ flight controllers. This is not an all in one solution. It requires an external power source for servos. So is not as small or light as it first appears. > [!WARNING] -> We recommend you only use this flight controller on very light aircraft that you will keep within line-of-sight distances. Reliability of the hardware is far from guaranteed. So fitting to a larger, heavier aircraft adds unneccesary safety risks. Also, there are essential features missing for other types of flights. Please keep this for small park fliers only, if used at all. +> We recommend you only use this flight controller on very light aircraft that you will keep within line-of-sight distances. Reliability of the hardware is far from guaranteed. So fitting to a larger, heavier aircraft adds unnecessary safety risks. Also, there are essential features missing for other types of flights. Please keep this for small park fliers only, if used at all. > > Also, if you insist on buying one of these. Make sure it's from a somewhere selling it at $16US. Spending $40US on this is a waste of money. You can get better FCs for around that money. @@ -22,7 +24,7 @@ Hardware issues have been reported on these flight controllers. They are also mi | Weight | 2.8g | > [!NOTE] -> There is conflicting information for the power this FC can handle. There are 2 specs providied: +> There is conflicting information for the power this FC can handle. There are 2 specs provided: > | | | > |---|---| > | Voltmeter | 2.5-30V | @@ -31,7 +33,7 @@ Hardware issues have been reported on these flight controllers. They are also mi > There is no ADC for "voltmeter" input. So potentially this FC can run at 1S to 6S. However there are only 2 LDO regulators on the FC itself. ## Notable missing features -* Current senso +* Current sensor * Blackbox recording * Analogue OSD * PINIO (for VTX power switching etc) @@ -39,4 +41,4 @@ Hardware issues have been reported on these flight controllers. They are also mi * On-board power rail for servos * Filtered power for video -All the above can be found on the Matek F405-WMO. Which is a $45US flight controller. Which is 32 x 22 x 12.7 mm and 9g, and has a definite input voltage range of 2S to 6S, and able to handle up to 132A. \ No newline at end of file +All the above can be found on the Matek F405-WMO. Which is a $45US flight controller. Which is 32 x 22 x 12.7 mm and 9g, and has a definite input voltage range of 2S to 6S, and able to handle up to 132A. From 8fc0c73b219ecacb3124710ab669b02b81faf048 Mon Sep 17 00:00:00 2001 From: FlyingRC-Official <118458039+FlyingRC-Official@users.noreply.github.com> Date: Tue, 26 May 2026 13:35:37 +0800 Subject: [PATCH 56/67] Add FlyingRC F4Wing Mini current sensor ADC --- src/main/target/FLYINGRCF4WINGMINI/target.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/target/FLYINGRCF4WINGMINI/target.h b/src/main/target/FLYINGRCF4WINGMINI/target.h index 3d714dd423a..95769052228 100644 --- a/src/main/target/FLYINGRCF4WINGMINI/target.h +++ b/src/main/target/FLYINGRCF4WINGMINI/target.h @@ -98,7 +98,10 @@ #define ADC_INSTANCE ADC1 #define ADC1_DMA_STREAM DMA2_Stream4 #define ADC_CHANNEL_1_PIN PC4 +#define ADC_CHANNEL_2_PIN PC5 #define VBAT_ADC_CHANNEL ADC_CHN_1 +// V4 hardware adds a backside current-sensor pad on the same ADC pin used by Matek F405 TE. +#define CURRENT_METER_ADC_CHANNEL ADC_CHN_2 // *************** LEDSTRIP ************************ #define USE_LED_STRIP From b59974fbeccb81d5515ef05068db61e50e0dd8a5 Mon Sep 17 00:00:00 2001 From: FlyingRC-Official <118458039+FlyingRC-Official@users.noreply.github.com> Date: Tue, 26 May 2026 13:44:08 +0800 Subject: [PATCH 57/67] Fix FlyingRC F4Wing Mini PR cleanup --- docs/boards/FlyingRC F4Wing Mini.md | 2 +- src/main/target/FLYINGRCF4WINGMINI/target.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/boards/FlyingRC F4Wing Mini.md b/docs/boards/FlyingRC F4Wing Mini.md index 15587b90451..26adb948c98 100644 --- a/docs/boards/FlyingRC F4Wing Mini.md +++ b/docs/boards/FlyingRC F4Wing Mini.md @@ -33,7 +33,7 @@ Hardware issues have been reported on these flight controllers. They are also mi > There is no ADC for "voltmeter" input. So potentially this FC can run at 1S to 6S. However there are only 2 LDO regulators on the FC itself. ## Notable missing features -* Current sensor +* Current sensor on earlier hardware revisions (V4 adds a backside ADC current-sensor pad) * Blackbox recording * Analogue OSD * PINIO (for VTX power switching etc) diff --git a/src/main/target/FLYINGRCF4WINGMINI/target.h b/src/main/target/FLYINGRCF4WINGMINI/target.h index 95769052228..21b90a853ee 100644 --- a/src/main/target/FLYINGRCF4WINGMINI/target.h +++ b/src/main/target/FLYINGRCF4WINGMINI/target.h @@ -3,7 +3,7 @@ * * INAV is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, o + * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * INAV is distributed in the hope that it will be useful, From d2ff05b571d57904827de7ba594a65ef6767deac Mon Sep 17 00:00:00 2001 From: FlyingRC-Official <118458039+FlyingRC-Official@users.noreply.github.com> Date: Tue, 26 May 2026 13:44:55 +0800 Subject: [PATCH 58/67] Polish FlyingRC F4Wing Mini review notes --- docs/boards/FlyingRC F4Wing Mini.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/boards/FlyingRC F4Wing Mini.md b/docs/boards/FlyingRC F4Wing Mini.md index 26adb948c98..a1863744215 100644 --- a/docs/boards/FlyingRC F4Wing Mini.md +++ b/docs/boards/FlyingRC F4Wing Mini.md @@ -4,12 +4,12 @@ This is a budget-friendly flight controller, typically sold in the US$16-40 rang FlyingRC is a small hardware vendor focused on affordable flight-controller and RC electronics products. More information about their work and open-source activity is available on their GitHub page: https://github.com/FlyingRC-Official -Hardware issues have been reported on these flight controllers. They are also missing many features. Unlike most other _wing_ flight controllers. This is not an all in one solution. It requires an external power source for servos. So is not as small or light as it first appears. +Hardware issues have been reported on these flight controllers. They are also missing many features. Unlike most other _wing_ flight controllers, this is not an all-in-one solution. It requires an external power source for servos, so is not as small or light as it first appears. > [!WARNING] > We recommend you only use this flight controller on very light aircraft that you will keep within line-of-sight distances. Reliability of the hardware is far from guaranteed. So fitting to a larger, heavier aircraft adds unnecessary safety risks. Also, there are essential features missing for other types of flights. Please keep this for small park fliers only, if used at all. > -> Also, if you insist on buying one of these. Make sure it's from a somewhere selling it at $16US. Spending $40US on this is a waste of money. You can get better FCs for around that money. +> Also, if you choose to buy one of these, compare pricing carefully. At around US$40 there are more capable flight controllers available. ## Specifications | | | @@ -37,8 +37,8 @@ Hardware issues have been reported on these flight controllers. They are also mi * Blackbox recording * Analogue OSD * PINIO (for VTX power switching etc) -* ADCs (external current sensor, airspeed sensor, rssi, etc) +* Additional ADCs (airspeed sensor, RSSI, etc) * On-board power rail for servos * Filtered power for video -All the above can be found on the Matek F405-WMO. Which is a $45US flight controller. Which is 32 x 22 x 12.7 mm and 9g, and has a definite input voltage range of 2S to 6S, and able to handle up to 132A. +Most of the above can be found on the Matek F405-WMO. Which is a $45US flight controller. Which is 32 x 22 x 12.7 mm and 9g, and has a definite input voltage range of 2S to 6S, and able to handle up to 132A. From 2ace7f32ab2df038bfdd2d0560969dae7b7047a1 Mon Sep 17 00:00:00 2001 From: Sensei Date: Sun, 31 May 2026 00:36:01 -0500 Subject: [PATCH 59/67] Refine description and manufacturer details for F4Wing Mini Removed redundant phrases and clarified manufacturer information. --- docs/boards/FlyingRC F4Wing Mini.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/boards/FlyingRC F4Wing Mini.md b/docs/boards/FlyingRC F4Wing Mini.md index a1863744215..237d1c9e33e 100644 --- a/docs/boards/FlyingRC F4Wing Mini.md +++ b/docs/boards/FlyingRC F4Wing Mini.md @@ -1,13 +1,13 @@ # Board - FLYINGRCF4WINGMINI -This is a budget-friendly flight controller, typically sold in the US$16-40 range. As with many entry-level boards, component tolerances and quality control may vary compared with higher-cost hardware, so users should test critical functions carefully before flying. The board has been advertised as INAV-compatible, but it was not originally submitted through the official INAV target process; current support exists thanks to community work, especially the contribution from dixi83. +This is a budget-friendly flight controller. Component tolerances and quality control may vary compared with higher-cost hardware, so users should test critical functions carefully before flying. The board has been advertised as INAV-compatible, but it was not originally submitted through the official INAV target process; current support exists thanks to community work, especially the contribution from dixi83. -FlyingRC is a small hardware vendor focused on affordable flight-controller and RC electronics products. More information about their work and open-source activity is available on their GitHub page: https://github.com/FlyingRC-Official +The manufaturer, FlyingRC, now has a GitHub available: https://github.com/FlyingRC-Official Hardware issues have been reported on these flight controllers. They are also missing many features. Unlike most other _wing_ flight controllers, this is not an all-in-one solution. It requires an external power source for servos, so is not as small or light as it first appears. > [!WARNING] -> We recommend you only use this flight controller on very light aircraft that you will keep within line-of-sight distances. Reliability of the hardware is far from guaranteed. So fitting to a larger, heavier aircraft adds unnecessary safety risks. Also, there are essential features missing for other types of flights. Please keep this for small park fliers only, if used at all. +> We recommend you only use this flight controller on very light aircraft that you will keep within line-of-sight distances. Reliability of the hardware is far from guaranteed. So fitting to a larger, heavier aircraft adds unnecessary safety risks. Also, there are essential features missing for other types of flights. Please keep this for small park fliers only. > > Also, if you choose to buy one of these, compare pricing carefully. At around US$40 there are more capable flight controllers available. @@ -40,5 +40,3 @@ Hardware issues have been reported on these flight controllers. They are also mi * Additional ADCs (airspeed sensor, RSSI, etc) * On-board power rail for servos * Filtered power for video - -Most of the above can be found on the Matek F405-WMO. Which is a $45US flight controller. Which is 32 x 22 x 12.7 mm and 9g, and has a definite input voltage range of 2S to 6S, and able to handle up to 132A. From b3109d16251ba6479238a2bdfc1dac753620e617 Mon Sep 17 00:00:00 2001 From: p-i-engineer Date: Sat, 30 May 2026 06:38:58 -0700 Subject: [PATCH 60/67] dshot ignore fix --- src/main/io/beeper.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/io/beeper.c b/src/main/io/beeper.c index af0f826c88c..c8cc7769921 100644 --- a/src/main/io/beeper.c +++ b/src/main/io/beeper.c @@ -345,7 +345,9 @@ void beeperUpdate(timeUs_t currentTimeUs) if (!beeperIsOn) { #ifdef USE_DSHOT if (isMotorProtocolDshot() && !areMotorsRunning() && beeperConfig()->dshot_beeper_enabled - && currentTimeUs - lastDshotBeeperCommandTimeUs > getDShotBeaconGuardDelayUs()) + && currentTimeUs - lastDshotBeeperCommandTimeUs > getDShotBeaconGuardDelayUs() + && currentBeeperEntry->sequence[beeperPos] != 0 // added beeper timeout so dshot does not beep on "off" + && !(getBeeperOffMask() & (1 << (currentBeeperEntry->mode - 1)))) // added beeper ignore to dshot beacon { lastDshotBeeperCommandTimeUs = currentTimeUs; sendDShotCommand(beeperConfig()->dshot_beeper_tone); From 58dc1070fcf1a4bb0a2e6593442e5afc272fe3c3 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Thu, 4 Jun 2026 10:15:04 -0500 Subject: [PATCH 61/67] FW autotrim: replace servo_autotrim_iterm_rate_limit setting with hardcoded constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The configurable CLI setting added in #11215 was based on a default of 2 with no empirical basis. Blackbox analysis of real flight data shows i-term rates of 4–9 units/s (median) and up to 30 units/s (90th percentile) during settled cruise flight. A hardcoded constant of 30 gives reasonable protection against turbulence-driven i-term spikes without exposing a tuning knob that pilots cannot meaningfully calibrate. --- src/main/fc/settings.yaml | 5 ----- src/main/flight/servos.c | 3 ++- src/main/flight/servos.h | 1 - 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index 6ef471e3bb0..4e8affb0221 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -1361,11 +1361,6 @@ groups: default_value: 15 min: 1 max: 60 - - name: servo_autotrim_iterm_rate_limit - description: "Maximum I-term rate of change (units/sec) for autotrim to be applied. Prevents trim updates during maneuver transitions when I-term is changing rapidly. Only applies when using `feature FW_AUTOTRIM`." - default_value: 2 - min: 0 - max: 50 - name: PG_CONTROL_PROFILES type: controlConfig_t diff --git a/src/main/flight/servos.c b/src/main/flight/servos.c index 9f6eb4851a7..639f5c18a13 100755 --- a/src/main/flight/servos.c +++ b/src/main/flight/servos.c @@ -608,6 +608,7 @@ void processServoAutotrimMode(void) #define SERVO_AUTOTRIM_CENTER_MAX 1700 #define SERVO_AUTOTRIM_UPDATE_SIZE 5 #define SERVO_AUTOTRIM_ATTITUDE_LIMIT 50 // 5 degrees +#define SERVO_AUTOTRIM_ITERM_RATE_LIMIT 30 // ~90th percentile during stable cruise (blackbox-derived) void processContinuousServoAutotrim(const float dT) { @@ -646,7 +647,7 @@ void processContinuousServoAutotrim(const float dT) for (int axis = FD_ROLL; axis <= FD_PITCH; axis++) { // For each stabilized axis, add 5 units of I-term to all associated servo midpoints const float axisIterm = getAxisIterm(axis); - const bool itermIsStable = itermRateOfChange[axis] < servoConfig()->servo_autotrim_iterm_rate_limit; + const bool itermIsStable = itermRateOfChange[axis] < SERVO_AUTOTRIM_ITERM_RATE_LIMIT; if (fabsf(axisIterm) > SERVO_AUTOTRIM_UPDATE_SIZE && itermIsStable) { const int8_t ItermUpdate = axisIterm > 0.0f ? SERVO_AUTOTRIM_UPDATE_SIZE : -SERVO_AUTOTRIM_UPDATE_SIZE; diff --git a/src/main/flight/servos.h b/src/main/flight/servos.h index b16dd7ca915..bc001455a91 100644 --- a/src/main/flight/servos.h +++ b/src/main/flight/servos.h @@ -171,7 +171,6 @@ typedef struct servoConfig_s { uint8_t tri_unarmed_servo; // send tail servo correction pulses even when unarmed uint8_t servo_autotrim_rotation_limit; // Max rotation for servo midpoints to be updated uint8_t servo_autotrim_iterm_threshold; // How much of the Iterm is carried over to the servo midpoints on each update - uint8_t servo_autotrim_iterm_rate_limit; // Max I-term rate of change (units/sec) to apply autotrim } servoConfig_t; PG_DECLARE(servoConfig_t, servoConfig); From 1b167eee5a71979d732d0cc7b7c44879afabfb1f Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Thu, 4 Jun 2026 10:51:41 -0500 Subject: [PATCH 62/67] docs: regenerate Settings.md (remove servo_autotrim_iterm_rate_limit) --- docs/Settings.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/docs/Settings.md b/docs/Settings.md index 39e3de90d52..3d7351dc371 100644 --- a/docs/Settings.md +++ b/docs/Settings.md @@ -6052,16 +6052,6 @@ When feature SERIALRX is enabled, this allows connection to several receivers wh --- -### servo_autotrim_iterm_rate_limit - -Maximum I-term rate of change (units/sec) for autotrim to be applied. Prevents trim updates during maneuver transitions when I-term is changing rapidly. Only applies when using `feature FW_AUTOTRIM`. - -| Default | Min | Max | -| --- | --- | --- | -| 2 | 0 | 50 | - ---- - ### servo_autotrim_rotation_limit Servo midpoints are only updated when total aircraft rotation is less than this threshold [deg/s]. Only applies when using `feature FW_AUTOTRIM`. From f9606987507c2f4b2369419372d0ad0568c87df2 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Thu, 4 Jun 2026 16:27:43 -0500 Subject: [PATCH 63/67] Bump firmware version to 9.1.0 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 14db3259720..24a0fe83ca0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,7 +52,7 @@ else() endif() -project(INAV VERSION 9.0.1) +project(INAV VERSION 9.1.0) enable_language(ASM) From 4e681841d292b4d165a94c489cf3be5af1389950 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Thu, 4 Jun 2026 17:41:00 -0500 Subject: [PATCH 64/67] Add pinioBoxConfig_t to PG struct sizes reference database pinioBoxConfig_t was missing from the reference database despite SPEEDYBEEF745AIO having USE_PINIOBOX defined. The struct is uint8_t permanentId[PINIO_COUNT] (4 bytes), PG version 1. --- cmake/pg_struct_sizes.reference.db | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/pg_struct_sizes.reference.db b/cmake/pg_struct_sizes.reference.db index a98e89bf21c..49fa79ce34d 100644 --- a/cmake/pg_struct_sizes.reference.db +++ b/cmake/pg_struct_sizes.reference.db @@ -47,3 +47,4 @@ telemetryConfig_t 58 8 timeConfig_t 4 1 vtxConfig_t 55 4 vtxSettingsConfig_t 12 2 +pinioBoxConfig_t 4 1 From 5451de6e799554e26abd126665798c45f9886b23 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Fri, 5 Jun 2026 14:11:24 -0500 Subject: [PATCH 65/67] Update release guide with lessons from 9.1.0-RC1 - Add version string format section: lowercase rc + hyphen required by Configurator firmware flasher regex (uppercase RC causes firmware to be invisible in flasher after upload) - Fix rename script: use hyphen separator in output filename to match the required inav_-rc_.hex pattern - Replace directory-flattening find command with platform-organized mkdir/mv; flattening caused a Windows .exe inside macOS DMG in 9.0.0 - Add PR branch verification step in changelog section: gh pr list shows PRs by date regardless of target branch, so PRs on maintenance-10.x can appear falsely - Update gh release create examples: add --prerelease for RC releases, use --target for atomic tagging at a specific commit - Add post-publish verification: confirm firmware appears in Configurator Firmware Flasher before publishing the Configurator release --- docs/development/release-create.md | 105 +++++++++++++++++++++++------ 1 file changed, 84 insertions(+), 21 deletions(-) diff --git a/docs/development/release-create.md b/docs/development/release-create.md index a2cb4994a4c..a75f8f6848a 100644 --- a/docs/development/release-create.md +++ b/docs/development/release-create.md @@ -2,11 +2,11 @@ This document describes the process for creating INAV firmware and configurator releases. -> **Note:** This document is designed to be used with coding assistants (such as Claude Code) that can execute the commands and automate parts of the release process. Update this document with lessons learned after each release. +> **Note:** This document is designed to be used with coding assistants (such as Claude Code) that can execute the commands and automate parts of the release process. Update this document with lessons learned after each release. Sensei has written more detailed guides for his process in the third-party repo https://github.com/sensei-hacker/inav-claude/tree/master/claude/release-manager ## CRITICAL PRINCIPLE: Verify Builds BEFORE Creating Tags -**Never tag a commit that hasn't been proven to build successfully.** +**Never tag a commit that hasn't been fully tested successfully.** Order of operations: 1. Merge all firmware PRs to the release branch @@ -93,6 +93,22 @@ Version numbers are set in: - View: `jq -r .version package.json` (or `node -p "require('./package.json').version"`) - Update: `npm version --no-git-tag-version` +## Version String Format (RC Releases) + +**CRITICAL:** Establish the canonical version string before starting any release work. + +RC version strings must use **lowercase `rc`** joined to the version with a **hyphen**: + +| Correct | Wrong | +|---------|-------| +| `9.1.0-rc1` | `9.1.0-RC1` | +| `9.1.0-rc2` | `9.1.0_RC2` | +| `9.0.0-rc3` | `9.0.0-rc_3` | + +The Configurator firmware flasher uses a case-sensitive regex to parse firmware filenames. Uppercase `RC` or underscore separators cause the target board name to be misread, making the firmware invisible in the flasher even after a successful release upload. + +--- + ## Pre-Release Checklist ### Code Readiness @@ -318,6 +334,20 @@ LAST_TAG=$(git describe --tags --abbrev=0) gh pr list --state merged --search "merged:>=$(git log -1 --format=%ai $LAST_TAG | cut -d' ' -f1)" --limit 100 ``` +### Verify Each PR Is on the Correct Branch + +**Before including a PR in release notes**, confirm it is actually merged into the release branch, not a future branch. `gh pr list` shows PRs by merge date regardless of target branch — a PR merged to `maintenance-10.x` will appear even though it's not in the current release. + +```bash +# Confirm a PR's merge commit exists on the release branch +git log upstream/maintenance-9.x --oneline | grep + +# Or check all recent merge commits on the branch +git log upstream/maintenance-9.x --oneline --merges | head -30 +``` + +If a PR is not in that output, exclude it from the release notes. + ### Using git log ```bash @@ -403,7 +433,7 @@ make MATEKF405 MATEKF722 Remove CI suffix and add RC number for RC releases: ```bash -RC_NUM="RC2" # Empty for final releases +RC_NUM="rc2" # Empty for final releases # Check if any .hex files exist to avoid errors with the glob if compgen -G "*.hex" > /dev/null; then @@ -411,7 +441,7 @@ if compgen -G "*.hex" > /dev/null; then target=$(echo "$f" | sed -E 's/inav_[0-9]+\.[0-9]+\.[0-9]+_(.*)_ci-.*/\1/') version=$(echo "$f" | sed -E 's/inav_([0-9]+\.[0-9]+\.[0-9]+)_.*/\1/') if [ -n "$RC_NUM" ]; then - mv "$f" "inav_${version}_${RC_NUM}_${target}.hex" + mv "$f" "inav_${version}-${RC_NUM}_${target}.hex" else mv "$f" "inav_${version}_${target}.hex" fi @@ -429,29 +459,46 @@ Download from GitHub Actions CI: # List recent workflow runs gh run list --repo iNavFlight/inav-configurator --limit 10 -# Download artifacts +# Download artifacts (creates one subdirectory per platform artifact) gh run download --repo iNavFlight/inav-configurator -# Flatten directory structure -find . -mindepth 2 -type f -exec mv -t . {} + -# Remove the now-empty subdirectories -find . -mindepth 1 -type d -empty -delete +# CRITICAL: Organize by platform — NEVER flatten all files into one directory. +# Flattening can put Windows .exe files inside macOS DMGs (caused a 9.0.0 release incident). +mkdir -p linux/ macos/ windows/ +mv INAV-Configurator_linux_*/* linux/ +mv INAV-Configurator_macOS*/* macos/ +mv INAV-Configurator_win_*/* windows/ +rmdir INAV-Configurator_* ``` ## Creating GitHub Releases ### Create Draft Release -```bash -# Firmware -cd inav -gh release create --draft --title "INAV " --notes-file release-notes.md -gh release upload *.hex +For RC releases, add `--prerelease` so GitHub marks them as pre-release and they don't appear as the latest stable release. Use `--target ` to tag a specific commit (safer than tagging the current HEAD, and works even when the local repo is locked). -# Configurator -cd inav-configurator -gh release create --draft --title "INAV Configurator " --notes-file release-notes.md -gh release upload *.zip *.dmg *.exe *.AppImage *.deb *.rpm *.msi +```bash +# Firmware (RC release) +gh release create 9.1.0-rc1 \ + --repo iNavFlight/inav \ + --target \ + --title "INAV 9.1.0-rc1 release candidate for testing" \ + --notes-file release-notes.md \ + --prerelease \ + --draft +gh release upload 9.1.0-rc1 firmware-dir/*.hex --repo iNavFlight/inav + +# Configurator (RC release) +gh release create 9.1.0-rc1 \ + --repo iNavFlight/inav-configurator \ + --target \ + --title "INAV Configurator 9.1.0-rc1 release candidate for testing" \ + --notes-file release-notes.md \ + --prerelease \ + --draft +gh release upload 9.1.0-rc1 linux/* macos/* windows/* --repo iNavFlight/inav-configurator + +# Final releases: same commands, omit --prerelease ``` ### Managing Release Assets @@ -477,16 +524,32 @@ gh api -X DELETE "repos/iNavFlight/inav/releases/assets/ASSET_ID" ### Publish Release +**Publish firmware first, then verify the Configurator can see it before publishing the Configurator release.** + +```bash +# Step 1: Publish firmware release +gh release edit --repo iNavFlight/inav --draft=false +``` + +**Step 2: Verify firmware appears in Configurator Firmware Flasher (human step)** + +Open INAV Configurator → Firmware Flasher tab → enable "Show unstable releases". The new firmware version must appear in the release list. This confirms the GitHub release is properly formatted and the filename regex parsed correctly. + +Also select a target whose name contains spaces (e.g., `MAMBAH743 2022B GYRO2`) and confirm it displays with spaces, not underscores — this validates that multi-word target names parsed correctly. + +If the firmware does not appear: check that filenames follow `inav_-rc_.hex` exactly (lowercase `rc`, hyphen separator). See [Asset Naming Conventions](#asset-naming-conventions). + ```bash -gh release edit --draft=false +# Step 3: Publish configurator release (only after firmware verified in flasher) +gh release edit --repo iNavFlight/inav-configurator --draft=false ``` ## Asset Naming Conventions -**Firmware (RC releases):** `inav__RC_.hex` +**Firmware (RC releases):** `inav_-rc_.hex` **Firmware (final):** `inav__.hex` -**Configurator (RC releases):** `INAV-Configurator___RC.` +**Configurator (RC releases):** `INAV-Configurator__-rc.` **Configurator (final):** `INAV-Configurator__.` ## Maintenance Branches From 989da80443b30995a051c1e170459a272028c961 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Fri, 5 Jun 2026 23:40:53 -0500 Subject: [PATCH 66/67] fix: remove duplicate autotrim macro definitions from servos.c Commit 58dc1070fc re-added SERVO_AUTOTRIM_FILTER_CUTOFF and related constants to servos.c as local #defines, but they were already moved to servos.h by a prior refactor (dcc404ecc0). The duplicate caused a build error: the .c definition used integer 1 while the header has 1.0f, triggering -Werror,-Wmacro-redefined on all targets. Remove the redundant block from servos.c and add the one genuinely new constant (SERVO_AUTOTRIM_ITERM_RATE_LIMIT) to servos.h alongside the existing autotrim constants. --- src/main/flight/servos.c | 7 ------- src/main/flight/servos.h | 8 ++++++++ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/flight/servos.c b/src/main/flight/servos.c index 639f5c18a13..ec5cf50948a 100755 --- a/src/main/flight/servos.c +++ b/src/main/flight/servos.c @@ -603,13 +603,6 @@ void processServoAutotrimMode(void) } } -#define SERVO_AUTOTRIM_FILTER_CUTOFF 1 // LPF cutoff frequency -#define SERVO_AUTOTRIM_CENTER_MIN 1300 -#define SERVO_AUTOTRIM_CENTER_MAX 1700 -#define SERVO_AUTOTRIM_UPDATE_SIZE 5 -#define SERVO_AUTOTRIM_ATTITUDE_LIMIT 50 // 5 degrees -#define SERVO_AUTOTRIM_ITERM_RATE_LIMIT 30 // ~90th percentile during stable cruise (blackbox-derived) - void processContinuousServoAutotrim(const float dT) { static timeMs_t lastUpdateTimeMs; diff --git a/src/main/flight/servos.h b/src/main/flight/servos.h index bc001455a91..3f8ebf1b4e8 100644 --- a/src/main/flight/servos.h +++ b/src/main/flight/servos.h @@ -161,6 +161,14 @@ typedef struct servoParam_s { PG_DECLARE_ARRAY(servoParam_t, MAX_SUPPORTED_SERVOS, servoParams); + +#define SERVO_AUTOTRIM_FILTER_CUTOFF 1.0f // LPF cutoff frequency +#define SERVO_AUTOTRIM_CENTER_MIN 1300 +#define SERVO_AUTOTRIM_CENTER_MAX 1700 +#define SERVO_AUTOTRIM_UPDATE_SIZE 5 +#define SERVO_AUTOTRIM_ATTITUDE_LIMIT 50 // 5 degrees +#define SERVO_AUTOTRIM_ITERM_RATE_LIMIT 30 // ~90th percentile during stable cruise (blackbox-derived) + typedef struct servoConfig_s { // PWM values, in milliseconds, common range is 1000-2000 (1ms to 2ms) uint16_t servoCenterPulse; // This is the value for servos when they should be in the middle. e.g. 1500. From 3a06d157508146d56b4d4cd6ddd255ddfff9bdc1 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Thu, 4 Jun 2026 10:15:04 -0500 Subject: [PATCH 67/67] FW autotrim: replace servo_autotrim_iterm_rate_limit setting with hardcoded constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The configurable CLI setting added in #11215 was based on a default of 2 with no empirical basis. Blackbox analysis of real flight data shows i-term rates of 4–9 units/s (median) and up to 30 units/s (90th percentile) during settled cruise flight. A hardcoded constant of 30 gives reasonable protection against turbulence-driven i-term spikes without exposing a tuning knob that pilots cannot meaningfully calibrate. --- src/main/flight/servos.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/flight/servos.c b/src/main/flight/servos.c index ec5cf50948a..e72dd66ea7d 100755 --- a/src/main/flight/servos.c +++ b/src/main/flight/servos.c @@ -603,6 +603,7 @@ void processServoAutotrimMode(void) } } + void processContinuousServoAutotrim(const float dT) { static timeMs_t lastUpdateTimeMs;