Files
HaloKeymind/platformio.ini
mikecarper fa010196b1 Merge PR #7 with reviewed ExpressLRS power and memory fixes
Integrate ExpressLRS TX module support and its stacked ESP32 heap changes.
Use the approved Linkflow calibration (17-30 dBm), preserve the PA drive
and output path after radio recovery, and keep LoRa OTA enabled.

Preserve stored ACLs and filters on allocation failure, release owned
client/filter buffers, service heap OTA contexts on Companions, release
self-serving workspaces after TempRadio, and reset staged-resume state
when a context is released. Keep manual staging and active operations
alive. Account for all moved allocations in the runtime RAM gate.

Validation: 1,393 native cases; radio-power, heap-context, ACL persistence,
shared-queue transfer, display/inbox, radio receive, and memory regressions.
Firmware builds passed for Linkflow, Heltec V2 Companion, T-Beam MQTT
repeater, Heltec V4 R8 MQTT repeater, RAK4631 repeater, and Indicator Full.
Physical verification awaits access to the currently offline lab Pi.
2026-09-10 22:06:16 -07:00

430 lines
16 KiB
INI

; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[platformio]
extra_configs =
variants/*/platformio.ini
platformio.local.ini
[arduino_base]
framework = arduino
monitor_speed = 115200
lib_deps =
SPI
Wire
;jgromes/RadioLib @ ^7.7.1
https://github.com/jgromes/RadioLib.git#187ef24791c3d844939b2be13a68bd890bd04e4c
rweather/Crypto @ ^0.4.0
adafruit/RTClib @ ^2.1.3
melopero/Melopero RV3028 @ ^1.1.0
electroniccats/CayenneLPP @ 1.6.1
build_flags = -w -DNDEBUG -DRADIOLIB_STATIC_ONLY=1 -DRADIOLIB_GODMODE=1
-D LORA_FREQ=869.618
-D LORA_BW=62.5
-D LORA_SF=8
; TCXO startup delay in microseconds, written by CustomSX1262/CustomLR1110
; after begin(). RadioLib defaults to 5000 and nothing used to override it, so
; every board paid 5 ms on every duty-cycle wake. Lowest working value measured
; on five modules: T096 400, Tracker V2 300, Waveshare 150, ThinkNode M3 200,
; T1000-E 200 us - 1600 keeps 4x margin over the worst of them. It also feeds
; the RXPS sleep floor and timer guard (transition = this + 1000), so lowering
; it makes low-SF profiles armable that were not. A board needing its own value
; puts -D MC_TCXO_DELAY_US=... in its own build_flags; the later -D wins.
-D MC_TCXO_DELAY_US=1600
-D ENABLE_ADVERT_ON_BOOT=1
-D ENABLE_PRIVATE_KEY_IMPORT=1 ; NOTE: comment these out for more secure firmware
-D ENABLE_PRIVATE_KEY_EXPORT=1
-D RADIOLIB_EXCLUDE_CC1101=1
-D RADIOLIB_EXCLUDE_RF69=1
-D RADIOLIB_EXCLUDE_SX1231=1
-D RADIOLIB_EXCLUDE_SI443X=1
-D RADIOLIB_EXCLUDE_RFM2X=1
-D RADIOLIB_EXCLUDE_SX128X=1
-D RADIOLIB_EXCLUDE_AFSK=1
-D RADIOLIB_EXCLUDE_AX25=1
-D RADIOLIB_EXCLUDE_HELLSCHREIBER=1
-D RADIOLIB_EXCLUDE_MORSE=1
-D RADIOLIB_EXCLUDE_APRS=1
-D RADIOLIB_EXCLUDE_BELL=1
-D RADIOLIB_EXCLUDE_RTTY=1
-D RADIOLIB_EXCLUDE_SSTV=1
build_src_filter =
+<*.cpp>
+<helpers/*.cpp>
+<helpers/ui/DisplayDriver.cpp>
+<helpers/ui/DisplayPowerSettings.cpp>
+<helpers/ota/OtaTinf.c>
; MQTT-only sources (need Timezone/NTPClient); observer envs add them back.
-<helpers/MQTTMessageBuilder.cpp>
-<helpers/JWTHelper.cpp>
+<helpers/radiolib/*.cpp>
+<helpers/bridges/BridgeBase.cpp>
+<helpers/ui/MomentaryButton.cpp>
; ----------------- ESP32 ---------------------
[esp32_base]
extends = arduino_base
platform = platformio/espressif32@6.11.0
monitor_filters = esp32_exception_decoder
; OTA is available on every ESP32 variant (A/B via esp_ota; the detools-sequential apply decodes into the
; inactive slot). The received .mota is staged in the INACTIVE OTA slot (OtaStoreFlashEsp32), not RAM, so
; deltas/full images of any size fetch over the air (RX-safe, sector-coalesced). The build always includes
; it; for `applydelta` to actually run, the board must use a dual-app/OTA partition table
; (board_build.partitions) with two app slots + otadata (boards without one fetch-refuse cleanly).
; pio_endf appends the EndF self-identity trailer. (nRF52 single-slot OTA is enabled per-board, not here -
; it needs a custom bootloader; RP2040/STM32 have no A/B path yet.)
extra_scripts =
pre:scripts/generate_webconfig_html.py
pre:scripts/meshcore_image_identity.py
pre:scripts/portable_esp32_link.py
pre:scripts/esp32_ota_heap_context.py
merge-bin.py
post:tools/mota/pio_endf.py
post:scripts/check_esp32_dram.py
post:scripts/check_firmware_ram.py
build_flags = ${arduino_base.build_flags}
-D ESP32_PLATFORM
-D ENABLE_OTA=1
-D OTA_FLASH_STORE=1
; Keep the fixed-offset MeshCore image identity even though application code
; does not reference it directly. The flasher reads it before boot.
-Wl,-u,meshcore_image_identity
-D OTA_FOLDER_SERIAL ; `ota folder on` relays a host folder of .mota over the USB console (no extra HW)
; Route esp_transport_ws_init through src/helpers/ESP32WsTransportFix.cpp to
; fix a heap-overflow in the precompiled IDF 4.4 WS transport (see that file).
-Wl,--wrap=esp_transport_ws_init
; -D ESP32_CPU_FREQ=80 ; change it to your need
lib_deps = ${arduino_base.lib_deps}
; Keep these as a matched pair. ESPAsyncWebServer 3.12.0 relies on
; AsyncTCP 3.5.0's synchronous close/abort behavior and request-lifetime fix.
ESP32Async/AsyncTCP @ 3.5.0
ESP32Async/ESPAsyncWebServer @ 3.12.0
bblanchon/ArduinoJson @ 7.4.3
; Generic WiFi-join QR rendering is linked only by ESP32 display builds.
ricmoo/QRCode @ 0.0.1
build_src_filter = ${arduino_base.build_src_filter}
+<helpers/ui/DisplayDriver.cpp>
+<helpers/esp32/WebConfigServer.cpp>
+<helpers/esp32/PortableErrorStrings.cpp>
+<helpers/esp32/WiFiOtaSeeder.cpp>
+<helpers/ota/*.cpp>
+<helpers/ota/detools/detools.c>
[esp32_ota]
lib_deps =
ESP32Async/AsyncTCP @ 3.5.0
ESP32Async/ESPAsyncWebServer @ 3.12.0
file://arch/esp32/AsyncElegantOTA
; esp32c6 uses arduino framework 3.x
; WARNING: experimental. May not work as stable as other platforms.
[esp32c6_base]
extends = esp32_base
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.13-1/platform-espressif32.zip
; ESP32-S3 Full Companion images use the ROM-compatible dual-I/O flash mode.
; This is independent of the board's PSRAM type and of whether the USB
; connector exposes the S3 native USB peripheral. Ordinary non-Full profiles
; keep their existing board-specific flash mode.
[esp32_s3_full]
board_build.flash_mode = dio
; ----------------- NRF52 ---------------------
[nrf52_base]
extends = arduino_base
platform = nordicnrf52
platform_packages =
; use internal fork that includes patch to ble stack to prevent firmware lockup during rapid connect/disconnect
; https://github.com/meshcore-dev/MeshCore/pull/1177
; https://github.com/meshcore-dev/MeshCore/pull/1295
framework-arduinoadafruitnrf52 @ https://github.com/meshcore-dev/Adafruit_nRF52_Arduino#d541301
platformio/toolchain-gccarmnoneeabi@^1.140201.0
extra_scripts = create-uf2.py
pre:scripts/nrf52_usb_power_fix.py
pre:scripts/nrf52_internal_bootloader_link.py
post:scripts/check_firmware_ram.py
build_flags = ${arduino_base.build_flags}
-D NRF52_PLATFORM
-D LFS_NO_ASSERT=1
-D EXTRAFS=1
-D MESH_NRF52_LOOP_STACK_WORDS=2048
-Wl,--wrap=xTaskCreate
-D USE_CC310_HW_CRYPTO=1
lib_deps =
${arduino_base.lib_deps}
https://github.com/oltaco/CustomLFS#0.2.3
; Vendored ESP-IDF MQTT client; the LDF otherwise pulls it into non-ESP builds.
lib_ignore = PsychicMqttClient
; Shared LoRa OTA recipe for nRF52840 variants. nRF52840 has no A/B slot, so the update is
; applied in place by the custom OTAFIX bootloader (in-place detools); the app only stages + verifies +
; approves it. OtaFlashLayout_nrf52.h derives the safe ceiling from the linked app region and secondary
; storage: internal ExtraFS stays protected at 0xD4000; QSPI/no-ExtraFS roles can stage through 0xED000.
; The EndF hook records that resolved geometry so motatool can size each in-place patch without target-name
; heuristics. An older bootloader makes the app stage below 0xD4000; packages sized for the expanded window
; require the updated OTAFIX bootloader (or a legacy motatool override). The device must run the matching
; OTAFIX bootloader for `applydelta` to actually apply. (detools.c is intentionally NOT built into the app -
; only the bootloader decodes.) The [rak4631] base defines this same recipe inline because it carries
; board-specific extras (fix_bsec_lib.py, BSEC lib) and a fixed post-script order.
[nrf52_lora_ota]
extends = nrf52_base
extra_scripts = ${nrf52_base.extra_scripts}
post:tools/mota/pio_endf.py
build_flags = ${nrf52_base.build_flags}
-D ENABLE_OTA=1
-D OTA_FLASH_STORE=1
-D OTA_FOLDER_SERIAL ; `ota folder on` relays a host folder of .mota over the USB console (no extra HW)
build_src_filter = ${nrf52_base.build_src_filter}
+<helpers/ota/*.cpp>
lib_deps = ${nrf52_base.lib_deps}
; Opt-in for repeater environments whose exact board has dedicated QSPI flash
; and a matching QSPI-capable OTAFIX bootloader. The raw OTA store owns that
; chip, so companion builds that mount QSPI as LittleFS must not use this flag.
[nrf52_qspi_ota]
build_flags =
-D OTA_QSPI_STORE=1
; RAK15001 is a single-data-line GD25Q16C connected to the WisBlock sensor
; SPI bus. The nRF QSPI peripheral can issue its FAST_READ and page-program
; commands in single-line mode, but the module's 15 MHz limit requires an
; 8 MHz clock. Exact JEDEC matching makes a missing or different shared-bus
; device fail closed instead of being mistaken for an OTA store.
[nrf52_rak15001_ota]
build_flags =
${nrf52_qspi_ota.build_flags}
-D OTA_QSPI_SHARED_WISBLOCK_SPI=1
-D OTA_QSPI_SCK_ARDUINO_PIN=3
-D OTA_QSPI_CS_ARDUINO_PIN=26
-D OTA_QSPI_IO0_ARDUINO_PIN=30
-D OTA_QSPI_IO1_ARDUINO_PIN=29
-D OTA_QSPI_IO2_NOT_CONNECTED=1
-D OTA_QSPI_IO3_NOT_CONNECTED=1
-D OTA_QSPI_SCK_FREQUENCY=NRF_QSPI_FREQ_32MDIV4
-D OTA_QSPI_EXPECTED_JEDEC_ID=0xC84015UL
-D OTA_QSPI_EXPECTED_SIZE=2097152UL
; Six-pin Winbond W25Q16JV breakout wired permanently to a RAK19007. It shares
; the WisBlock SPI clock/data nets but has a dedicated chip-select on AIN1, so
; one base-board wiring can be used with either RAK4631 or RAK3401+RAK13302.
; The breakout exposes standard SPI only, so use single-data-line nRF QSPI
; mode. Exact JEDEC matching rejects substituted W25Q32/64/128 boards. Keep
; the clock at 8 MHz for short wiring and power it from J12 VDD so the matching
; bootloader can read the staged image after reset.
[nrf52_wisblock_w25q16_ota]
build_flags =
${nrf52_qspi_ota.build_flags}
-D OTA_QSPI_SHARED_WISBLOCK_SPI=1
-D OTA_QSPI_SCK_ARDUINO_PIN=3
-D OTA_QSPI_CS_ARDUINO_PIN=31
-D OTA_QSPI_IO0_ARDUINO_PIN=30
-D OTA_QSPI_IO1_ARDUINO_PIN=29
-D OTA_QSPI_IO2_NOT_CONNECTED=1
-D OTA_QSPI_IO3_NOT_CONNECTED=1
-D OTA_QSPI_SCK_FREQUENCY=NRF_QSPI_FREQ_32MDIV4
-D OTA_QSPI_EXPECTED_JEDEC_ID=0xEF4015UL
-D OTA_QSPI_EXPECTED_SIZE=2097152UL
; Compatibility alias retained for existing nRF52840 board definitions.
[rak4631_hw]
extends = nrf52_lora_ota
extra_scripts = ${nrf52_lora_ota.extra_scripts}
build_flags = ${nrf52_lora_ota.build_flags}
build_src_filter = ${nrf52_lora_ota.build_src_filter}
lib_deps = ${nrf52_lora_ota.lib_deps}
; ----------------- RP2040 ---------------------
[rp2040_base]
extends = arduino_base
extra_scripts = post:scripts/check_firmware_ram.py
upload_protocol = picotool
board_build.core = earlephilhower
platform = https://github.com/maxgerhardt/platform-raspberrypi.git ; framework-arduinopico @ 1.50600.0+sha.6a1d13e9
build_flags = ${arduino_base.build_flags}
-D RP2040_PLATFORM
; Arduino-Pico ships board-specific helper libraries alongside its core.
; Deep dependency discovery can mistake iLabs_Hearth for an application
; dependency even on boards without its required ESP32-C6 coprocessor.
lib_ignore = iLabs Hearth
; ----------------- STM32 ----------------------
[stm32_base]
extends = arduino_base
platform = ststm32
extra_scripts = post:arch/stm32/build_hex.py
post:scripts/check_firmware_ram.py
build_flags = ${arduino_base.build_flags}
-D STM32_PLATFORM
-D ED25519_COMPACT_BASE=1
-I src/helpers/stm32
-I $PROJECT_PACKAGES_DIR/framework-arduinoststm32/libraries/SubGhz/src
build_src_filter = ${arduino_base.build_src_filter}
+<helpers/stm32>
lib_deps = ${arduino_base.lib_deps}
file://arch/stm32/Adafruit_LittleFS_stm32
SubGhz
[i2c_voltage_monitor_base]
build_flags =
-D ENV_INCLUDE_INA3221=1
-D ENV_INCLUDE_INA219=1
-D ENV_INCLUDE_INA226=1
-D ENV_INCLUDE_INA260=1
lib_deps =
adafruit/Adafruit INA3221 Library @ ^1.0.1
adafruit/Adafruit INA219 @ ^1.2.3
robtillaart/INA226 @ ^0.6.4
adafruit/Adafruit INA260 Library @ ^1.5.3
[sensor_base]
build_flags =
-D ENV_INCLUDE_GPS=1
-D ENV_INCLUDE_AHTX0=1
-D ENV_INCLUDE_BME280=1
-D ENV_INCLUDE_BMP280=1
-D ENV_INCLUDE_SHTC3=1
-D ENV_INCLUDE_SHT4X=1
-D ENV_INCLUDE_LPS22HB=1
${i2c_voltage_monitor_base.build_flags}
-D ENV_INCLUDE_MLX90614=1
-D ENV_INCLUDE_VL53L0X=1
-D ENV_INCLUDE_BME680=1
-D ENV_INCLUDE_BMP085=1
lib_deps =
${i2c_voltage_monitor_base.lib_deps}
adafruit/Adafruit AHTX0 @ ^2.0.5
adafruit/Adafruit BME280 Library @ ^2.3.0
adafruit/Adafruit BMP280 Library @ ^2.6.8
adafruit/Adafruit SHTC3 Library @ ^1.0.1
sensirion/Sensirion I2C SHT4x @ ^1.1.2
arduino-libraries/Arduino_LPS22HB @ ^1.0.2
adafruit/Adafruit MLX90614 Library @ ^2.1.5
adafruit/Adafruit_VL53L0X @ ^1.2.4
stevemarple/MicroNMEA @ ^2.0.6
adafruit/Adafruit BME680 Library @ ^2.0.4
adafruit/Adafruit BMP085 Library @ ^1.2.4
; Shared trim for optional environmental/ranging sensors. Board-native radio,
; display, buttons, battery monitoring, and other board telemetry remain.
[nrf52_external_environmental_sensor_trim]
build_flags =
-UENV_INCLUDE_AHTX0
-UENV_INCLUDE_BME280
-UENV_INCLUDE_BMP280
-UENV_INCLUDE_SHTC3
-UENV_INCLUDE_SHT4X
-UENV_INCLUDE_LPS22HB
-UENV_INCLUDE_MLX90614
-UENV_INCLUDE_VL53L0X
-UENV_INCLUDE_BME680
-UENV_INCLUDE_BMP085
-UENV_INCLUDE_RAK12035
-UENV_INCLUDE_BME680_BSEC
; Lean nRF52 LoRa OTA roles with onboard GPS use this full external-sensor
; trim. Board-native GPS remains.
[nrf52_no_external_sensors_keep_gps]
build_flags =
${nrf52_external_environmental_sensor_trim.build_flags}
-UENV_INCLUDE_INA3221
-UENV_INCLUDE_INA219
-UENV_INCLUDE_INA226
-UENV_INCLUDE_INA260
lib_deps =
stevemarple/MicroNMEA @ ^2.0.6
; Lean nRF52 LoRa OTA roles without onboard GPS also remove the GPS provider.
[nrf52_no_external_sensors]
build_flags =
${nrf52_no_external_sensors_keep_gps.build_flags}
-UENV_INCLUDE_GPS
; RAK reduced OTA roles retain the common I2C voltage/current monitor set at a
; measured 4,808-byte linked-flash cost while omitting the larger optional
; environmental/ranging sensor set.
[nrf52_reduced_sensors_keep_ina_gps]
build_flags =
${nrf52_external_environmental_sensor_trim.build_flags}
${i2c_voltage_monitor_base.build_flags}
lib_deps =
stevemarple/MicroNMEA @ ^2.0.6
${i2c_voltage_monitor_base.lib_deps}
[nrf52_reduced_sensors_keep_ina]
build_flags =
${nrf52_reduced_sensors_keep_ina_gps.build_flags}
-UENV_INCLUDE_GPS
lib_deps =
${i2c_voltage_monitor_base.lib_deps}
; ----------------- TESTING ---------------------
; Host GoogleTest suites for the fork's pure logic. See test/README.md.
; Run: `pio test -e native` (all) or `pio test -e native -f <suite>` (one).
[env:native]
platform = native
test_framework = googletest
build_flags = -std=c++17
-I src
-I test/mocks
-D OTA_FETCH_PIPELINE=4
-D OTA_TRANSPORT_DEFLATE_TEST=1
test_build_src = yes
test_ignore = test_kiss_modem
build_src_filter =
-<*>
+<../src/Utils.cpp>
+<../src/helpers/MQTTPayloadBuilder.cpp>
+<../src/Packet.cpp>
+<../src/Dispatcher.cpp>
+<../src/Identity.cpp>
+<../src/Mesh.cpp>
+<../src/helpers/TxtDataHelpers.cpp>
+<../src/helpers/StaticPoolPacketManager.cpp>
+<../src/helpers/FloodAdvertLimiter.cpp>
+<../src/helpers/ota/MerkleTree.cpp>
+<../src/helpers/ota/MotaContainer.cpp>
+<../src/helpers/ota/FirmwareInfo.cpp>
+<../src/helpers/ota/MotaSourceSerial.cpp>
+<../src/helpers/ota/FolderMotaStore.cpp>
+<../src/helpers/ota/OtaProtocol.cpp>
+<../src/helpers/ota/OtaManager.cpp>
+<../src/helpers/ota/OtaDeflate.cpp>
+<../src/helpers/ota/OtaTinf.c>
+<../src/helpers/ota/detools/detools.c>
+<../src/helpers/UserGpio.cpp>
+<../src/helpers/ConfigSerializer.cpp>
+<../src/helpers/ArduinoSerialInterface.cpp>
+<../src/helpers/DynamicConfigSerializer.cpp>
+<../src/helpers/CommonRadioPrefs.cpp>
+<../src/helpers/ui/MomentaryButton.cpp>
lib_deps =
google/googletest @ 1.17.0
bblanchon/ArduinoJson @ 7.4.3
[env:native_kiss_modem]
platform = native
test_framework = googletest
build_flags = -std=c++17
-I test/mocks
-I src
-I examples/kiss_modem
test_build_src = yes
test_filter = test_kiss_modem
build_src_filter =
-<*>
+<../examples/kiss_modem/KissModem.cpp>
lib_deps =
google/googletest @ 1.17.0