From c4d4d889417f397225c4a2fc2131bbd69f790cdc Mon Sep 17 00:00:00 2001 From: Marcelo Bezerra <23555060+mmosca@users.noreply.github.com> Date: Mon, 16 Dec 2024 00:21:00 +0100 Subject: [PATCH 01/11] Remove legacy hdzero special case --- src/main/io/displayport_msp_osd.c | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/src/main/io/displayport_msp_osd.c b/src/main/io/displayport_msp_osd.c index 0b9be8c6d86..a39f123cdde 100644 --- a/src/main/io/displayport_msp_osd.c +++ b/src/main/io/displayport_msp_osd.c @@ -133,31 +133,6 @@ static int output(displayPort_t *displayPort, uint8_t cmd, uint8_t *subcmd, int return sent; } -static uint8_t determineHDZeroOsdMode(void) -{ - if (cmsInMenu) { - return HD_5018; - } - - // Check if all visible widgets are in the center 30x16 chars of the canvas. - int activeLayout = osdGetActiveLayout(NULL); - osd_items_e index = 0; - do { - index = osdIncElementIndex(index); - uint16_t pos = osdLayoutsConfig()->item_pos[activeLayout][index]; - if (OSD_VISIBLE(pos)) { - uint8_t elemPosX = OSD_X(pos); - uint8_t elemPosY = OSD_Y(pos); - if (!osdItemIsFixed(index) && (elemPosX < 10 || elemPosX > 39 || elemPosY == 0 || elemPosY == 17)) { - return HD_5018; - } - } - } while (index > 0); - - return HD_3016; -} - - uint8_t setAttrPage(uint8_t origAttr, uint8_t page) { return (origAttr & ~DISPLAYPORT_MSP_ATTR_FONTPAGE_MASK) | (page & DISPLAYPORT_MSP_ATTR_FONTPAGE_MASK); @@ -175,10 +150,6 @@ uint8_t setAttrVersion(uint8_t origAttr, uint8_t version) static int setDisplayMode(displayPort_t *displayPort) { - if (osdVideoSystem == VIDEO_SYSTEM_HDZERO) { - currentOsdMode = determineHDZeroOsdMode(); // Can change between layouts - } - uint8_t subcmd[] = { MSP_DP_OPTIONS, 0, currentOsdMode }; // Font selection, mode (SD/HD) return output(displayPort, MSP_DISPLAYPORT, subcmd, sizeof(subcmd)); } From cb834eb616593cb03487b06eb218118b1a9efd43 Mon Sep 17 00:00:00 2001 From: Sensei Date: Sat, 14 Feb 2026 22:45:45 -0600 Subject: [PATCH 02/11] NEW_HARDWARE_POLICY.md: Use Discord rather than email Updated contact method for hardware feature requests. --- docs/policies/NEW_HARDWARE_POLICY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/policies/NEW_HARDWARE_POLICY.md b/docs/policies/NEW_HARDWARE_POLICY.md index bd4bddedf05..10767524700 100644 --- a/docs/policies/NEW_HARDWARE_POLICY.md +++ b/docs/policies/NEW_HARDWARE_POLICY.md @@ -67,7 +67,7 @@ If one of the core developers has the hardware in possession they may opt in and 1. Requester is advised to open a feature request to add support for certain hardware to INAV by following [this link](https://github.com/iNavFlight/inav/issues/new/choose) -2. After opening a feature request, Requester is advised to contact the core development team by [email](mailto:coredev@inavflight.com) mentioning the open feature request and communicate with developer team via email to arrange hardware and specifications delivery. +2. After opening a feature request, Requester is advised to contact the core development team via [Discord](https://discord.gg/peg2hhbYwN) mentioning the open feature request and communicate with developer team via email to arrange hardware and specifications delivery. ## See also From 1d370a6d68df8e71e3d037081d284f7937f6a50c Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Mon, 16 Feb 2026 23:12:08 -0600 Subject: [PATCH 03/11] Add SDMODELH7V2 target (STM32H743) SDMODEL SDH7 V2 flight controller with MPU6000 on SPI4 (CW270), MAX7456 OSD on SPI2, SD card blackbox on SPI1, BMP280/MS5611 baro and IST8310 mag on I2C1, 8 motor outputs across TIM2/3/5/8, VCP + 6 UARTs (UART7 RX-only for ESC sensor). --- src/main/target/SDMODELH7V2/CMakeLists.txt | 1 + src/main/target/SDMODELH7V2/target.c | 46 ++++++ src/main/target/SDMODELH7V2/target.h | 178 +++++++++++++++++++++ 3 files changed, 225 insertions(+) create mode 100644 src/main/target/SDMODELH7V2/CMakeLists.txt create mode 100644 src/main/target/SDMODELH7V2/target.c create mode 100644 src/main/target/SDMODELH7V2/target.h diff --git a/src/main/target/SDMODELH7V2/CMakeLists.txt b/src/main/target/SDMODELH7V2/CMakeLists.txt new file mode 100644 index 00000000000..ac26094e636 --- /dev/null +++ b/src/main/target/SDMODELH7V2/CMakeLists.txt @@ -0,0 +1 @@ +target_stm32h743xi(SDMODELH7V2) diff --git a/src/main/target/SDMODELH7V2/target.c b/src/main/target/SDMODELH7V2/target.c new file mode 100644 index 00000000000..ee01b45327f --- /dev/null +++ b/src/main/target/SDMODELH7V2/target.c @@ -0,0 +1,46 @@ +/* + * 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" + +timerHardware_t timerHardware[] = { + DEF_TIM(TIM3, CH3, PB0, TIM_USE_OUTPUT_AUTO, 0, 0), // M1 + DEF_TIM(TIM3, CH4, PB1, TIM_USE_OUTPUT_AUTO, 0, 1), // M2 + + DEF_TIM(TIM2, CH2, PB3, TIM_USE_OUTPUT_AUTO, 0, 2), // M3 + DEF_TIM(TIM2, CH3, PB10, TIM_USE_OUTPUT_AUTO, 0, 3), // M4 + + DEF_TIM(TIM5, CH1, PA0, TIM_USE_OUTPUT_AUTO, 0, 4), // M5 + DEF_TIM(TIM5, CH3, PA2, TIM_USE_OUTPUT_AUTO, 0, 5), // M6 + + DEF_TIM(TIM8, CH3, PC8, TIM_USE_OUTPUT_AUTO, 0, 6), // M7 + DEF_TIM(TIM8, CH4, PC9, TIM_USE_OUTPUT_AUTO, 0, 7), // M8 + + DEF_TIM(TIM4, CH1, PD12, TIM_USE_LED, 0, 14), // LED strip + DEF_TIM(TIM1, CH1, PE9, TIM_USE_ANY, 0, 12), // Camera control +}; + +const int timerHardwareCount = sizeof(timerHardware) / sizeof(timerHardware[0]); diff --git a/src/main/target/SDMODELH7V2/target.h b/src/main/target/SDMODELH7V2/target.h new file mode 100644 index 00000000000..b49697cd80f --- /dev/null +++ b/src/main/target/SDMODELH7V2/target.h @@ -0,0 +1,178 @@ +/* + * 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 "SDH7V2" +#define USBD_PRODUCT_STRING "SDMODELH7V2" + +#define USE_TARGET_CONFIG + +// *************** LED / BEEPER ********************** +#define LED0 PC2 + +#define BEEPER PC13 +#define BEEPER_INVERTED + +// *************** SPI1 - SD Card ******************** +#define USE_SPI +#define USE_SPI_DEVICE_1 +#define SPI1_SCK_PIN PA5 +#define SPI1_MISO_PIN PA6 +#define SPI1_MOSI_PIN PA7 + +// *************** 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 + +// *************** SPI4 - IMU (MPU6000) ************** +#define USE_SPI_DEVICE_4 +#define SPI4_SCK_PIN PE2 +#define SPI4_MISO_PIN PE5 +#define SPI4_MOSI_PIN PE6 + +#define USE_IMU_MPU6000 +#define IMU_MPU6000_ALIGN CW270_DEG +#define MPU6000_SPI_BUS BUS_SPI4 +#define MPU6000_CS_PIN PE4 +#define MPU6000_EXTI_PIN PE1 + +// *************** I2C1 - Baro / Mag ***************** +#define USE_I2C +#define USE_I2C_DEVICE_1 +#define I2C1_SCL PB6 +#define I2C1_SDA PB7 + +#define USE_BARO +#define BARO_I2C_BUS BUS_I2C1 +#define USE_BARO_BMP280 +#define USE_BARO_MS5611 + +#define USE_MAG +#define MAG_I2C_BUS BUS_I2C1 +#define USE_MAG_IST8310 +#define USE_MAG_ALL + +#define TEMPERATURE_I2C_BUS BUS_I2C1 +#define PITOT_I2C_BUS BUS_I2C1 + +#define USE_RANGEFINDER +#define RANGEFINDER_I2C_BUS BUS_I2C1 + +// *************** SD Card (SPI) ********************* +#define USE_SDCARD +#define USE_SDCARD_SPI +#define SDCARD_SPI_BUS BUS_SPI1 +#define SDCARD_CS_PIN PA4 +#define SDCARD_DETECT_PIN PA3 +#define SDCARD_DETECT_INVERTED + +#define ENABLE_BLACKBOX_LOGGING_ON_SDCARD_BY_DEFAULT + +// *************** 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 PD1 +#define UART4_RX_PIN PD0 + +#define USE_UART6 +#define UART6_TX_PIN PC6 +#define UART6_RX_PIN PC7 + +// UART7 RX-only (ESC sensor); TX pin not routed but AF mapping requires a valid pin +#define USE_UART7 +#define UART7_TX_PIN PE8 +#define UART7_RX_PIN PE7 + +#define SERIAL_PORT_COUNT 7 // VCP, UART1-4, UART6, UART7 + +#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 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 + +// BF DEFAULT_VOLTAGE_METER_SCALE=109, BF DEFAULT_CURRENT_METER_SCALE=168 +// INAV uses a different scaling formula; these values approximate the same +// physical measurement. Users should calibrate via the configurator. +#define VBAT_SCALE_DEFAULT 1090 +#define CURRENT_METER_SCALE 168 + +// *************** USB detect ************************ +#define USB_DETECT_PIN PA8 + +// *************** PINIO ***************************** +#define USE_PINIO +#define USE_PINIOBOX +#define PINIO1_PIN PE13 +#define PINIO1_FLAGS PINIO_FLAGS_INVERTED +#define PINIO2_PIN PB11 +#define PINIO2_FLAGS PINIO_FLAGS_INVERTED + +// *************** LED STRIP ************************* +#define USE_LED_STRIP +#define WS2811_PIN PD12 + +// *************** DEFAULT FEATURES ****************** +#define DEFAULT_FEATURES (FEATURE_OSD | FEATURE_TELEMETRY | FEATURE_CURRENT_METER | FEATURE_VBAT | FEATURE_TX_PROF_SEL | FEATURE_BLACKBOX) + +// *************** SERIAL 4WAY *********************** +#define USE_SERIAL_4WAY_BLHELI_INTERFACE + +// *************** ESC SENSOR ************************ +#define USE_ESC_SENSOR + +// *************** DSHOT ***************************** +#define USE_DSHOT + +// *************** IO PORT MASK ********************** +#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 8 From 3a93f2402b6148c7e55c2de4b5a1a50bf068b5d8 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Tue, 17 Feb 2026 00:15:50 -0600 Subject: [PATCH 04/11] SDMODELH7V2: replace camera control with USER1/USER2 PINIO, preset UART2 for onboard Bluetooth MSP Remove the camera control timer output on PE9. Add config.c to wire PINIO1/PINIO2 to USER1/USER2 mode boxes and preset UART2 for MSP at 115200 baud since it is connected to an onboard Bluetooth module. --- src/main/target/SDMODELH7V2/config.c | 37 ++++++++++++++++++++++++++++ src/main/target/SDMODELH7V2/target.c | 1 - 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 src/main/target/SDMODELH7V2/config.c diff --git a/src/main/target/SDMODELH7V2/config.c b/src/main/target/SDMODELH7V2/config.c new file mode 100644 index 00000000000..54ecc6f7627 --- /dev/null +++ b/src/main/target/SDMODELH7V2/config.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 "platform.h" + +#include "fc/fc_msp_box.h" +#include "fc/config.h" + +#include "io/piniobox.h" +#include "drivers/serial.h" +#include "io/serial.h" + +void targetConfiguration(void) +{ + pinioBoxConfigMutable()->permanentId[0] = BOX_PERMANENT_ID_USER1; + pinioBoxConfigMutable()->permanentId[1] = BOX_PERMANENT_ID_USER2; + + // UART2 is connected to an onboard Bluetooth module (not user-accessible) + serialConfigMutable()->portConfigs[findSerialPortIndexByIdentifier(SERIAL_PORT_USART2)].functionMask = FUNCTION_MSP; + serialConfigMutable()->portConfigs[findSerialPortIndexByIdentifier(SERIAL_PORT_USART2)].msp_baudrateIndex = BAUD_115200; +} diff --git a/src/main/target/SDMODELH7V2/target.c b/src/main/target/SDMODELH7V2/target.c index ee01b45327f..b3da3c075b1 100644 --- a/src/main/target/SDMODELH7V2/target.c +++ b/src/main/target/SDMODELH7V2/target.c @@ -40,7 +40,6 @@ timerHardware_t timerHardware[] = { DEF_TIM(TIM8, CH4, PC9, TIM_USE_OUTPUT_AUTO, 0, 7), // M8 DEF_TIM(TIM4, CH1, PD12, TIM_USE_LED, 0, 14), // LED strip - DEF_TIM(TIM1, CH1, PE9, TIM_USE_ANY, 0, 12), // Camera control }; const int timerHardwareCount = sizeof(timerHardware) / sizeof(timerHardware[0]); From e17b50b3dfbb73d72baed7e6114e2158a8b4ccee Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Sun, 22 Feb 2026 15:08:25 -0600 Subject: [PATCH 05/11] SDMODELH7V2: COnfigure PINIO and Bluetooth arming control --- src/main/target/SDMODELH7V2/config.c | 1 + src/main/target/SDMODELH7V2/target.h | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/target/SDMODELH7V2/config.c b/src/main/target/SDMODELH7V2/config.c index 54ecc6f7627..47f9da30bc5 100644 --- a/src/main/target/SDMODELH7V2/config.c +++ b/src/main/target/SDMODELH7V2/config.c @@ -30,6 +30,7 @@ void targetConfiguration(void) { pinioBoxConfigMutable()->permanentId[0] = BOX_PERMANENT_ID_USER1; pinioBoxConfigMutable()->permanentId[1] = BOX_PERMANENT_ID_USER2; + pinioBoxConfigMutable()->permanentId[2] = findBoxByActiveBoxId(BOXARM)->permanentId; // UART2 is connected to an onboard Bluetooth module (not user-accessible) serialConfigMutable()->portConfigs[findSerialPortIndexByIdentifier(SERIAL_PORT_USART2)].functionMask = FUNCTION_MSP; diff --git a/src/main/target/SDMODELH7V2/target.h b/src/main/target/SDMODELH7V2/target.h index b49697cd80f..fcdc0898f18 100644 --- a/src/main/target/SDMODELH7V2/target.h +++ b/src/main/target/SDMODELH7V2/target.h @@ -135,9 +135,6 @@ #define CURRENT_METER_ADC_CHANNEL ADC_CHN_2 #define RSSI_ADC_CHANNEL ADC_CHN_3 -// BF DEFAULT_VOLTAGE_METER_SCALE=109, BF DEFAULT_CURRENT_METER_SCALE=168 -// INAV uses a different scaling formula; these values approximate the same -// physical measurement. Users should calibrate via the configurator. #define VBAT_SCALE_DEFAULT 1090 #define CURRENT_METER_SCALE 168 @@ -147,11 +144,19 @@ // *************** PINIO ***************************** #define USE_PINIO #define USE_PINIOBOX -#define PINIO1_PIN PE13 + +// VTX power +#define PINIO1_PIN PB11 #define PINIO1_FLAGS PINIO_FLAGS_INVERTED -#define PINIO2_PIN PB11 + +// Cam pin +#define PINIO2_PIN PE9 #define PINIO2_FLAGS PINIO_FLAGS_INVERTED +// Bluetooth (off on arm) +#define PINIO3_PIN PE13 +#define PINIO3_FLAGS PINIO_FLAGS_INVERTED + // *************** LED STRIP ************************* #define USE_LED_STRIP #define WS2811_PIN PD12 From 7ec7f0d190807ddabea5513025143e23516df6ac Mon Sep 17 00:00:00 2001 From: Sensei Date: Sun, 1 Mar 2026 09:01:02 -0600 Subject: [PATCH 06/11] Remove HD_3016 from resolutionType_e enum Removed HD_3016 resolution type from the enum. --- src/main/io/displayport_msp_osd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/io/displayport_msp_osd.c b/src/main/io/displayport_msp_osd.c index a39f123cdde..f193913916f 100644 --- a/src/main/io/displayport_msp_osd.c +++ b/src/main/io/displayport_msp_osd.c @@ -58,7 +58,6 @@ typedef enum { // defines are from hdzero code SD_3016, HD_5018, - HD_3016, // Special HDZERO mode that just sends the centre 30x16 of the 50x18 canvas to the VRX HD_6022, // added to support DJI wtfos 60x22 grid HD_5320 // added to support Avatar and BetaflightHD } resolutionType_e; From b52c6f783473a157b652a026b66d75bc1646651a Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Sun, 1 Mar 2026 12:31:43 -0600 Subject: [PATCH 07/11] CI: publish PR test builds to iNavFlight/pr-test-builds Add a workflow_run-triggered job that uploads each PR's firmware as individually named release assets on iNavFlight/pr-test-builds, then posts (or updates) a PR comment with a direct download link. This avoids two limitations of GitHub Actions artifacts: the opaque single-zip bundle, and the requirement for a GitHub login to download. Requires a PR_BUILDS_TOKEN secret with Contents:write access to iNavFlight/pr-test-builds. --- .github/workflows/ci.yml | 10 ++ .github/workflows/pr-test-builds.yml | 151 +++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 .github/workflows/pr-test-builds.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf911321f14..f3a856bc7f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,6 +107,16 @@ jobs: with: name: targets path: targets.txt + - name: Save PR number + if: github.event_name == 'pull_request' + run: echo "${{ github.event.pull_request.number }}" > pr_number.txt + - name: Upload PR number + if: github.event_name == 'pull_request' + uses: actions/upload-artifact@v4 + with: + name: pr-number + path: pr_number.txt + retention-days: 1 build-SITL-Linux-arm64: runs-on: ubuntu-22.04-arm diff --git a/.github/workflows/pr-test-builds.yml b/.github/workflows/pr-test-builds.yml new file mode 100644 index 00000000000..8a217d7fe32 --- /dev/null +++ b/.github/workflows/pr-test-builds.yml @@ -0,0 +1,151 @@ +name: PR Test Builds + +# Runs after "Build firmware" completes. Uses workflow_run (rather than +# pull_request directly) so that secrets are available even for PRs from forks. +# +# Requires a repository secret PR_BUILDS_TOKEN with Contents: write access +# to iNavFlight/pr-test-builds (fine-grained PAT or classic PAT with repo scope). +on: + workflow_run: + workflows: ["Build firmware"] + types: [completed] + +jobs: + publish: + runs-on: ubuntu-latest + # Only act on pull_request-triggered runs that succeeded. + if: > + github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.conclusion == 'success' + # Prevent concurrent runs for the same PR branch racing on the + # release delete/create cycle. + concurrency: + group: pr-test-build-${{ github.event.workflow_run.head_branch }} + cancel-in-progress: true + permissions: + actions: read # to download artifacts from the triggering workflow run + pull-requests: write # to post the PR comment + + steps: + - name: Download PR number + uses: actions/download-artifact@v4 + with: + name: pr-number + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Read PR number + id: pr + run: | + PR_NUM=$(tr -dc '0-9' < pr_number.txt) + if [ -z "$PR_NUM" ]; then + echo "::error::Invalid PR number in artifact" + exit 1 + fi + echo "number=${PR_NUM}" >> $GITHUB_OUTPUT + + - name: Download firmware artifacts + uses: actions/download-artifact@v4 + with: + pattern: matrix-inav-* + merge-multiple: true + path: hexes + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Get build info + id: info + run: | + COUNT=$(find hexes -name '*.hex' -type f | wc -l) + if [ "$COUNT" -eq 0 ]; then + echo "::error::No .hex files found in downloaded artifacts" + exit 1 + fi + echo "count=${COUNT}" >> $GITHUB_OUTPUT + echo "short_sha=$(echo '${{ github.event.workflow_run.head_sha }}' | cut -c1-7)" >> $GITHUB_OUTPUT + + # Delete the previous release for this PR (if any) so assets are replaced + # cleanly on each new commit. --cleanup-tag removes the old tag so it is + # recreated fresh pointing to the new commit. + - name: Delete existing PR release + env: + GH_TOKEN: ${{ secrets.PR_BUILDS_TOKEN }} + run: | + gh release delete "pr-${{ steps.pr.outputs.number }}" \ + --repo iNavFlight/pr-test-builds --cleanup-tag --yes 2>/dev/null || true + + - name: Create PR release + env: + GH_TOKEN: ${{ secrets.PR_BUILDS_TOKEN }} + run: | + PR_NUMBER="${{ steps.pr.outputs.number }}" + SHORT_SHA="${{ steps.info.outputs.short_sha }}" + COUNT="${{ steps.info.outputs.count }}" + PR_URL="https://github.com/${{ github.repository }}/pull/${PR_NUMBER}" + + NOTES="Test build for [PR #${PR_NUMBER}](${PR_URL}) — commit \`${SHORT_SHA}\` + +**${COUNT} targets built.** Find your board's \`.hex\` file by name (e.g. \`MATEKF405SE.hex\`). + +> Development build for testing only. Use Full Chip Erase when flashing." + + cd hexes + gh release create "pr-${PR_NUMBER}" *.hex \ + --repo iNavFlight/pr-test-builds \ + --prerelease \ + --title "PR #${PR_NUMBER} (${SHORT_SHA})" \ + --notes "${NOTES}" + + - name: Post or update PR comment + uses: actions/github-script@v7 + env: + PR_NUMBER: ${{ steps.pr.outputs.number }} + SHORT_SHA: ${{ steps.info.outputs.short_sha }} + HEX_COUNT: ${{ steps.info.outputs.count }} + with: + script: | + const prNumber = parseInt(process.env.PR_NUMBER); + const shortSha = process.env.SHORT_SHA; + const count = process.env.HEX_COUNT; + const releaseUrl = `https://github.com/iNavFlight/pr-test-builds/releases/tag/pr-${prNumber}`; + + const body = [ + '', + '**Test firmware build ready** — commit `' + shortSha + '`', + '', + `[Download firmware for PR #${prNumber}](${releaseUrl})`, + '', + `${count} targets built. Find your board's \`.hex\` file by name on that page ` + + '(e.g. `MATEKF405SE.hex`). Files are individually downloadable — no GitHub login required.', + '', + '> Development build for testing only. Use Full Chip Erase when flashing.', + ].join('\n'); + + const comments = await github.paginate( + github.rest.issues.listComments, + { + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + } + ); + + const existing = comments.find(c => + c.user.type === 'Bot' && c.body.includes('') + ); + + if (existing) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body, + }); + } From 37e02bd1c050e7a49c71834f3edd98460b11ae09 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Sun, 1 Mar 2026 12:44:29 -0600 Subject: [PATCH 08/11] CI: address code review findings in pr-test-builds workflow - Add issues: write permission (required for github.rest.issues.* endpoints) - Include fork repo name in concurrency key to prevent cross-fork cancellation - Pin release tag to head_sha via --target flag - Add parseInt radix and NaN validation for PR number --- .github/workflows/pr-test-builds.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-test-builds.yml b/.github/workflows/pr-test-builds.yml index 8a217d7fe32..a297bd3671b 100644 --- a/.github/workflows/pr-test-builds.yml +++ b/.github/workflows/pr-test-builds.yml @@ -20,10 +20,11 @@ jobs: # Prevent concurrent runs for the same PR branch racing on the # release delete/create cycle. concurrency: - group: pr-test-build-${{ github.event.workflow_run.head_branch }} + group: pr-test-build-${{ github.event.workflow_run.head_repository.full_name }}-${{ github.event.workflow_run.head_branch }} cancel-in-progress: true permissions: actions: read # to download artifacts from the triggering workflow run + issues: write # github.rest.issues.* endpoints used to post PR comments pull-requests: write # to post the PR comment steps: @@ -93,6 +94,7 @@ jobs: gh release create "pr-${PR_NUMBER}" *.hex \ --repo iNavFlight/pr-test-builds \ --prerelease \ + --target "${{ github.event.workflow_run.head_sha }}" \ --title "PR #${PR_NUMBER} (${SHORT_SHA})" \ --notes "${NOTES}" @@ -104,7 +106,8 @@ jobs: HEX_COUNT: ${{ steps.info.outputs.count }} with: script: | - const prNumber = parseInt(process.env.PR_NUMBER); + const prNumber = parseInt(process.env.PR_NUMBER, 10); + if (isNaN(prNumber)) throw new Error(`Invalid PR number: ${process.env.PR_NUMBER}`); const shortSha = process.env.SHORT_SHA; const count = process.env.HEX_COUNT; const releaseUrl = `https://github.com/iNavFlight/pr-test-builds/releases/tag/pr-${prNumber}`; From c7edbc33a09da687781f730600041c1e8347eeb1 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Sun, 1 Mar 2026 16:07:02 -0600 Subject: [PATCH 09/11] CI: fix YAML syntax error in pr-test-builds workflow Multi-line shell string in the NOTES variable had unindented continuation lines (**..., >...) that were less indented than the YAML block scalar body, causing a parse error on every run. Replace with printf + --notes-file to avoid multi-line strings in the YAML block scalar entirely. Also moves ${{ }} expressions into env vars rather than inline in the shell script. --- .github/workflows/pr-test-builds.yml | 29 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/.github/workflows/pr-test-builds.yml b/.github/workflows/pr-test-builds.yml index a297bd3671b..cf4a32b7b57 100644 --- a/.github/workflows/pr-test-builds.yml +++ b/.github/workflows/pr-test-builds.yml @@ -78,25 +78,24 @@ jobs: - name: Create PR release env: GH_TOKEN: ${{ secrets.PR_BUILDS_TOKEN }} + PR_NUMBER: ${{ steps.pr.outputs.number }} + SHORT_SHA: ${{ steps.info.outputs.short_sha }} + HEX_COUNT: ${{ steps.info.outputs.count }} + HEAD_SHA: ${{ github.event.workflow_run.head_sha }} + REPO: ${{ github.repository }} run: | - PR_NUMBER="${{ steps.pr.outputs.number }}" - SHORT_SHA="${{ steps.info.outputs.short_sha }}" - COUNT="${{ steps.info.outputs.count }}" - PR_URL="https://github.com/${{ github.repository }}/pull/${PR_NUMBER}" - - NOTES="Test build for [PR #${PR_NUMBER}](${PR_URL}) — commit \`${SHORT_SHA}\` - -**${COUNT} targets built.** Find your board's \`.hex\` file by name (e.g. \`MATEKF405SE.hex\`). - -> Development build for testing only. Use Full Chip Erase when flashing." - - cd hexes - gh release create "pr-${PR_NUMBER}" *.hex \ + PR_URL="https://github.com/${REPO}/pull/${PR_NUMBER}" + printf '%s\n\n%s\n\n%s\n' \ + "Test build for [PR #${PR_NUMBER}](${PR_URL}) — commit \`${SHORT_SHA}\`" \ + "**${HEX_COUNT} targets built.** Find your board's \`.hex\` file by name (e.g. \`MATEKF405SE.hex\`)." \ + "> Development build for testing only. Use Full Chip Erase when flashing." \ + > release-notes.md + gh release create "pr-${PR_NUMBER}" hexes/*.hex \ --repo iNavFlight/pr-test-builds \ --prerelease \ - --target "${{ github.event.workflow_run.head_sha }}" \ + --target "${HEAD_SHA}" \ --title "PR #${PR_NUMBER} (${SHORT_SHA})" \ - --notes "${NOTES}" + --notes-file release-notes.md - name: Post or update PR comment uses: actions/github-script@v7 From 359c2c757ce3f4734d7f1c0db00e7325bd08d304 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Thu, 5 Mar 2026 19:17:53 -0600 Subject: [PATCH 10/11] optimize: reorder PG struct fields to eliminate alignment padding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reorder fields in four PG structs from largest-to-smallest type to eliminate internal padding holes created by misaligned fields. Each modified PG version is incremented to force EEPROM reset on firmware upgrade (required when struct layout changes). Results on MATEKF722 (F722, flash-constrained target): .pg_resetdata: 1303 → 1275 bytes (-28 bytes) text total: 470511 → 470491 bytes (-20 bytes) Structs optimized: pidProfile_s: 292 → 272 bytes (21 holes → 0), PG version 11→12 blackboxConfig_s: 16 → 12 bytes ( 2 holes → 0), PG version 4→5 statsConfig_s: 20 → 16 bytes ( 2 holes → 0), PG version 2→3 geozone_config_s: 20 → 16 bytes ( 2 holes → 0), PG version 0→1 Co-Authored-By: Claude Sonnet 4.6 --- src/main/blackbox/blackbox.c | 2 +- src/main/blackbox/blackbox.h | 2 +- src/main/fc/stats.c | 2 +- src/main/fc/stats.h | 2 +- src/main/flight/pid.c | 2 +- src/main/flight/pid.h | 76 +++++++++++++----------- src/main/navigation/navigation.h | 2 +- src/main/navigation/navigation_geozone.c | 2 +- 8 files changed, 47 insertions(+), 43 deletions(-) diff --git a/src/main/blackbox/blackbox.c b/src/main/blackbox/blackbox.c index e4c2afb3eec..7597f6d581f 100644 --- a/src/main/blackbox/blackbox.c +++ b/src/main/blackbox/blackbox.c @@ -99,7 +99,7 @@ #define BLACKBOX_INVERTED_CARD_DETECTION 0 #endif -PG_REGISTER_WITH_RESET_TEMPLATE(blackboxConfig_t, blackboxConfig, PG_BLACKBOX_CONFIG, 4); +PG_REGISTER_WITH_RESET_TEMPLATE(blackboxConfig_t, blackboxConfig, PG_BLACKBOX_CONFIG, 5); PG_RESET_TEMPLATE(blackboxConfig_t, blackboxConfig, .device = DEFAULT_BLACKBOX_DEVICE, diff --git a/src/main/blackbox/blackbox.h b/src/main/blackbox/blackbox.h index 1901201fa22..296291af0c7 100644 --- a/src/main/blackbox/blackbox.h +++ b/src/main/blackbox/blackbox.h @@ -54,11 +54,11 @@ typedef enum BlackboxState { } BlackboxState; typedef struct blackboxConfig_s { + uint32_t includeFlags; uint16_t rate_num; uint16_t rate_denom; uint8_t device; uint8_t invertedCardDetection; - uint32_t includeFlags; int8_t arm_control; } blackboxConfig_t; diff --git a/src/main/fc/stats.c b/src/main/fc/stats.c index 2fc4c63633d..f48b0862330 100644 --- a/src/main/fc/stats.c +++ b/src/main/fc/stats.c @@ -21,7 +21,7 @@ #define MIN_FLIGHT_DISTANCE_M 30 // minimum distance flown for a flight to be registered [m] -PG_REGISTER_WITH_RESET_TEMPLATE(statsConfig_t, statsConfig, PG_STATS_CONFIG, 2); +PG_REGISTER_WITH_RESET_TEMPLATE(statsConfig_t, statsConfig, PG_STATS_CONFIG, 3); PG_RESET_TEMPLATE(statsConfig_t, statsConfig, .stats_enabled = SETTING_STATS_DEFAULT, diff --git a/src/main/fc/stats.h b/src/main/fc/stats.h index d0b4b162b60..b2195165ffc 100644 --- a/src/main/fc/stats.h +++ b/src/main/fc/stats.h @@ -5,10 +5,10 @@ typedef struct statsConfig_s { uint32_t stats_total_time; // [Seconds] uint32_t stats_total_dist; // [Metres] - uint16_t stats_flight_count; #ifdef USE_ADC uint32_t stats_total_energy; // deciWatt hour (x0.1Wh) #endif + uint16_t stats_flight_count; uint8_t stats_enabled; } statsConfig_t; diff --git a/src/main/flight/pid.c b/src/main/flight/pid.c index 0a2faa648ca..cab56d73618 100644 --- a/src/main/flight/pid.c +++ b/src/main/flight/pid.c @@ -179,7 +179,7 @@ static EXTENDED_FASTRAM bool angleHoldIsLevel = false; static EXTENDED_FASTRAM float fixedWingLevelTrim; static EXTENDED_FASTRAM pidController_t fixedWingLevelTrimController; -PG_REGISTER_PROFILE_WITH_RESET_TEMPLATE(pidProfile_t, pidProfile, PG_PID_PROFILE, 11); +PG_REGISTER_PROFILE_WITH_RESET_TEMPLATE(pidProfile_t, pidProfile, PG_PID_PROFILE, 12); PG_RESET_TEMPLATE(pidProfile_t, pidProfile, .bank_mc = { diff --git a/src/main/flight/pid.h b/src/main/flight/pid.h index ff2e85031b7..4cfc6230019 100644 --- a/src/main/flight/pid.h +++ b/src/main/flight/pid.h @@ -97,71 +97,75 @@ typedef enum { } itermRelax_e; typedef struct pidProfile_s { - uint8_t pidControllerType; + // PID banks first (2-byte aligned, largest group) pidBank_t bank_fw; pidBank_t bank_mc; - uint8_t dterm_lpf_type; // Dterm LPF type: PT1, BIQUAD - uint16_t dterm_lpf_hz; - - uint8_t yaw_lpf_hz; - - uint8_t heading_hold_rate_limit; // Maximum rotation rate HEADING_HOLD mode can feed to yaw rate PID controller - - uint8_t itermWindupPointPercent; // Experimental ITerm windup threshold, percent of motor saturation - - uint32_t axisAccelerationLimitYaw; // Max rate of change of yaw angular rate setpoint (deg/s^2 = dps/s) - uint32_t axisAccelerationLimitRollPitch; // Max rate of change of roll/pitch angular rate setpoint (deg/s^2 = dps/s) - - int16_t max_angle_inclination[ANGLE_INDEX_COUNT]; // Max possible inclination (roll and pitch axis separately - - uint16_t pidItermLimitPercent; - + // 4-byte fields grouped to avoid alignment holes // Airplane-specific parameters float fixedWingReferenceAirspeed; // Reference tuning airspeed for the airplane - the speed for which PID gains are tuned float fixedWingCoordinatedYawGain; // This is the gain of the yaw rate required to keep the yaw rate consistent with the turn rate for a coordinated turn. - float fixedWingCoordinatedPitchGain; // This is the gain of the pitch rate to keep the pitch angle constant during coordinated turns. - uint16_t fixedWingYawItermBankFreeze; // Freeze yaw Iterm when bank angle is more than this many degrees - + float fixedWingCoordinatedPitchGain; // This is the gain of the pitch rate to keep the pitch angle constant during coordinated turns. float navVelXyDTermLpfHz; - uint8_t navVelXyDtermAttenuation; // VEL_XY dynamic Dterm scale: Dterm will be attenuatedby this value (in percent) when UAV is traveling with more than navVelXyDtermAttenuationStart percents of max velocity - uint8_t navVelXyDtermAttenuationStart; // VEL_XY dynamic Dterm scale: Dterm attenuation will begin at this percent of max velocity - uint8_t navVelXyDtermAttenuationEnd; // VEL_XY dynamic Dterm scale: Dterm will be fully attenuated at this percent of max velocity - uint8_t iterm_relax_cutoff; // This cutoff frequency specifies a low pass filter which predicts average response of the quad to setpoint - uint8_t iterm_relax; // Enable iterm suppression during stick input + float fixedWingLevelTrim; + float fixedWingLevelTrimGain; #ifdef USE_D_BOOST float dBoostMin; float dBoostMax; float dBoostMaxAtAlleceleration; - uint8_t dBoostGyroDeltaLpfHz; #endif #ifdef USE_ANTIGRAVITY float antigravityGain; float antigravityAccelerator; - uint8_t antigravityCutoff; #endif - uint16_t navFwPosHdgPidsumLimit; - uint8_t controlDerivativeLpfHz; - - float fixedWingLevelTrim; - float fixedWingLevelTrimGain; - - uint8_t fwAltControlResponseFactor; - bool fwAltControlUsePos; #ifdef USE_SMITH_PREDICTOR float smithPredictorStrength; float smithPredictorDelay; - uint16_t smithPredictorFilterHz; #endif + uint32_t axisAccelerationLimitYaw; // Max rate of change of yaw angular rate setpoint (deg/s^2 = dps/s) + uint32_t axisAccelerationLimitRollPitch; // Max rate of change of roll/pitch angular rate setpoint (deg/s^2 = dps/s) + // 2-byte fields + int16_t max_angle_inclination[ANGLE_INDEX_COUNT]; // Max possible inclination (roll and pitch axis separately + uint16_t pidItermLimitPercent; + uint16_t fixedWingYawItermBankFreeze; // Freeze yaw Iterm when bank angle is more than this many degrees + uint16_t navFwPosHdgPidsumLimit; uint16_t fwItermLockTimeMaxMs; + uint16_t dterm_lpf_hz; + +#ifdef USE_SMITH_PREDICTOR + uint16_t smithPredictorFilterHz; +#endif + + // 1-byte fields grouped at end to avoid padding holes + uint8_t pidControllerType; + uint8_t dterm_lpf_type; // Dterm LPF type: PT1, BIQUAD + uint8_t yaw_lpf_hz; + uint8_t heading_hold_rate_limit; // Maximum rotation rate HEADING_HOLD mode can feed to yaw rate PID controller + uint8_t itermWindupPointPercent; // Experimental ITerm windup threshold, percent of motor saturation + uint8_t navVelXyDtermAttenuation; // VEL_XY dynamic Dterm scale: Dterm will be attenuated by this value (in percent) when UAV is traveling with more than navVelXyDtermAttenuationStart percents of max velocity + uint8_t navVelXyDtermAttenuationStart; // VEL_XY dynamic Dterm scale: Dterm attenuation will begin at this percent of max velocity + uint8_t navVelXyDtermAttenuationEnd; // VEL_XY dynamic Dterm scale: Dterm will be fully attenuated at this percent of max velocity + uint8_t iterm_relax_cutoff; // This cutoff frequency specifies a low pass filter which predicts average response of the quad to setpoint + uint8_t iterm_relax; // Enable iterm suppression during stick input + uint8_t controlDerivativeLpfHz; + uint8_t fwAltControlResponseFactor; + bool fwAltControlUsePos; uint8_t fwItermLockRateLimit; uint8_t fwItermLockEngageThreshold; +#ifdef USE_D_BOOST + uint8_t dBoostGyroDeltaLpfHz; +#endif + +#ifdef USE_ANTIGRAVITY + uint8_t antigravityCutoff; +#endif + } pidProfile_t; typedef struct pidAutotuneConfig_s { diff --git a/src/main/navigation/navigation.h b/src/main/navigation/navigation.h index ddc7e23e76d..f4cb5120fc3 100644 --- a/src/main/navigation/navigation.h +++ b/src/main/navigation/navigation.h @@ -167,11 +167,11 @@ typedef struct geoZoneConfig_s typedef struct geozone_config_s { uint32_t fenceDetectionDistance; + uint32_t copterFenceStopDistance; uint16_t avoidAltitudeRange; uint16_t safeAltitudeDistance; bool nearestSafeHomeAsInclusivZone; uint8_t safeHomeFenceAction; - uint32_t copterFenceStopDistance; uint8_t noWayHomeAction; } geozone_config_t; diff --git a/src/main/navigation/navigation_geozone.c b/src/main/navigation/navigation_geozone.c index dfc7539859b..beb3051e0c5 100755 --- a/src/main/navigation/navigation_geozone.c +++ b/src/main/navigation/navigation_geozone.c @@ -124,7 +124,7 @@ static bool lockRTZ = false; geozone_t geozone; -PG_REGISTER_WITH_RESET_TEMPLATE(geozone_config_t, geoZoneConfig, PG_GEOZONE_CONFIG, 0); +PG_REGISTER_WITH_RESET_TEMPLATE(geozone_config_t, geoZoneConfig, PG_GEOZONE_CONFIG, 1); PG_RESET_TEMPLATE(geozone_config_t, geoZoneConfig, .fenceDetectionDistance = SETTING_GEOZONE_DETECTION_DISTANCE_DEFAULT, From c9c1905543f620c38f2c5df569007149c29ee279 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Thu, 5 Mar 2026 19:29:11 -0600 Subject: [PATCH 11/11] optimize: remove redundant section label comments from pidProfile_s Co-Authored-By: Claude Sonnet 4.6 --- src/main/flight/pid.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/flight/pid.h b/src/main/flight/pid.h index 4cfc6230019..523af380bb0 100644 --- a/src/main/flight/pid.h +++ b/src/main/flight/pid.h @@ -97,11 +97,10 @@ typedef enum { } itermRelax_e; typedef struct pidProfile_s { - // PID banks first (2-byte aligned, largest group) + // Fields ordered largest-to-smallest to eliminate alignment padding holes pidBank_t bank_fw; pidBank_t bank_mc; - // 4-byte fields grouped to avoid alignment holes // Airplane-specific parameters float fixedWingReferenceAirspeed; // Reference tuning airspeed for the airplane - the speed for which PID gains are tuned float fixedWingCoordinatedYawGain; // This is the gain of the yaw rate required to keep the yaw rate consistent with the turn rate for a coordinated turn. @@ -129,7 +128,6 @@ typedef struct pidProfile_s { uint32_t axisAccelerationLimitYaw; // Max rate of change of yaw angular rate setpoint (deg/s^2 = dps/s) uint32_t axisAccelerationLimitRollPitch; // Max rate of change of roll/pitch angular rate setpoint (deg/s^2 = dps/s) - // 2-byte fields int16_t max_angle_inclination[ANGLE_INDEX_COUNT]; // Max possible inclination (roll and pitch axis separately uint16_t pidItermLimitPercent; uint16_t fixedWingYawItermBankFreeze; // Freeze yaw Iterm when bank angle is more than this many degrees @@ -141,7 +139,6 @@ typedef struct pidProfile_s { uint16_t smithPredictorFilterHz; #endif - // 1-byte fields grouped at end to avoid padding holes uint8_t pidControllerType; uint8_t dterm_lpf_type; // Dterm LPF type: PT1, BIQUAD uint8_t yaw_lpf_hz;