From b1f8945e1c20ee1dc36fe20e2770c292571d8cca Mon Sep 17 00:00:00 2001 From: mikecarper Date: Thu, 27 Aug 2026 17:06:20 -0700 Subject: [PATCH] fix: stabilize Heltec RC32 startup --- build.sh | 19 ++++++++++++++--- examples/companion_radio/main.cpp | 4 +++- examples/simple_sensor/main.cpp | 2 ++ src/helpers/radiolib/CustomSX1262.h | 14 ++++++++++++ test/test_build_profiles.sh | 27 ++++++++++++++++++++++++ tools/mota/test_mota.py | 6 +++--- variants/heltec_rc32/HeltecRC32Board.cpp | 19 +++++++++++++++++ variants/heltec_rc32/HeltecRC32Board.h | 1 + variants/heltec_rc32/platformio.ini | 9 +++++++- variants/heltec_rc32/target.cpp | 3 ++- 10 files changed, 95 insertions(+), 9 deletions(-) diff --git a/build.sh b/build.sh index 9202beda9..fce925239 100755 --- a/build.sh +++ b/build.sh @@ -2416,11 +2416,24 @@ is_esp32_dual_cdc_companion_radio_full_target() { esac } -prepare_esp32_dual_cdc_framework() { +requires_esp32_arduino3_framework() { local env_name=$1 local pio_env_name=$2 - is_esp32_dual_cdc_companion_radio_full_target "$env_name" || return 0 + if is_esp32_dual_cdc_companion_radio_full_target "$env_name"; then + return 0 + fi + case "${env_name,,}:${pio_env_name,,}" in + heltec_rc32_*:*|*:heltec_rc32_*) return 0 ;; + *) return 1 ;; + esac +} + +prepare_esp32_arduino3_framework() { + local env_name=$1 + local pio_env_name=$2 + + requires_esp32_arduino3_framework "$env_name" "$pio_env_name" || return 0 # PlatformIO's standard ESP32 platform and pioarduino publish incompatible # Arduino 2.x/3.x cores under the same global package name. `pio run` restores @@ -3667,7 +3680,7 @@ build_firmware() { fi fi if [ "$build_status" -eq 0 ]; then - prepare_esp32_dual_cdc_framework "$env_name" "$pio_env_name" + prepare_esp32_arduino3_framework "$env_name" "$pio_env_name" build_status=$? fi pio_run_args=(run -e "$pio_env_name") diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 06196e44c..da78e3402 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -289,7 +289,9 @@ static int8_t attempted_power_saving = -1; static unsigned long power_saving_retry_at = 0; static uint32_t companionNominalCpuMhz() { -#ifdef ESP32_CPU_FREQ +#ifdef ESP32_POST_BOOT_CPU_FREQ + return ESP32_POST_BOOT_CPU_FREQ; +#elif defined(ESP32_CPU_FREQ) return ESP32_CPU_FREQ; #else return F_CPU / 1000000UL; diff --git a/examples/simple_sensor/main.cpp b/examples/simple_sensor/main.cpp index b12decf50..e8353d7d5 100644 --- a/examples/simple_sensor/main.cpp +++ b/examples/simple_sensor/main.cpp @@ -141,6 +141,8 @@ void setup() { #if ENABLE_ADVERT_ON_BOOT == 1 the_mesh.sendSelfAdvertisement(16000, false); #endif + + board.onBootComplete(); } void loop() { diff --git a/src/helpers/radiolib/CustomSX1262.h b/src/helpers/radiolib/CustomSX1262.h index 3a037801c..9f8ef9ee4 100644 --- a/src/helpers/radiolib/CustomSX1262.h +++ b/src/helpers/radiolib/CustomSX1262.h @@ -92,9 +92,23 @@ class CustomSX1262 : public SX1262 { #endif int status = begin(LORA_FREQ, LORA_BW, LORA_SF, cr, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 16, tcxo, useRegulatorLDO); if (status != RADIOLIB_ERR_NONE) { +#if defined(SX126X_ALLOW_RECOVERABLE_INIT_STATUS) && SX126X_ALLOW_RECOVERABLE_INIT_STATUS + // Some SX1262 modules report a transient command status after the + // chip has already answered its identity probe. Continue configuring + // those devices; an absent chip or a hard command failure is fatal. + if (status == RADIOLIB_ERR_CHIP_NOT_FOUND + || status == RADIOLIB_ERR_SPI_CMD_FAILED) { + mesh::usbLoggingPort().print("ERROR: radio init failed: "); + mesh::usbLoggingPort().println(status); + return false; + } + mesh::usbLoggingPort().print("WARN: recoverable radio init status: "); + mesh::usbLoggingPort().println(status); +#else mesh::usbLoggingPort().print("ERROR: radio init failed: "); mesh::usbLoggingPort().println(status); return false; // fail +#endif } // RadioLib's default DIO3 TCXO delay is 5 ms and its RX duty-cycle diff --git a/test/test_build_profiles.sh b/test/test_build_profiles.sh index bdd0bf014..b541dbacc 100644 --- a/test/test_build_profiles.sh +++ b/test/test_build_profiles.sh @@ -12,6 +12,25 @@ fail() { [ "$OPTION3_BUILD_WORKERS" -eq 1 ] \ || fail "logging matrix permits concurrent PlatformIO target builds" +requires_esp32_arduino3_framework \ + heltec_rc32_repeater heltec_rc32_repeater \ + || fail "RC32 omitted its Arduino-ESP32 3.x package preflight" +if requires_esp32_arduino3_framework \ + Heltec_v3_repeater Heltec_v3_repeater; then + fail "ordinary ESP32 target unexpectedly selected Arduino-ESP32 3.x" +fi + +grep -Fq 'void HeltecRC32Board::onBootComplete()' \ + variants/heltec_rc32/HeltecRC32Board.cpp \ + || fail "RC32 omitted its post-boot clock transition" +grep -Fq 'current_mhz > ESP32_POST_BOOT_CPU_FREQ' \ + variants/heltec_rc32/HeltecRC32Board.cpp \ + || fail "RC32 post-boot clock transition overrides lower power-saving clocks" +grep -Fq 'return ESP32_POST_BOOT_CPU_FREQ;' examples/companion_radio/main.cpp \ + || fail "Companion power saving ignores the RC32 post-boot nominal clock" +grep -Fq 'board.onBootComplete();' examples/simple_sensor/main.cpp \ + || fail "sensor firmware never applies board post-boot policy" + # Interactive builds can carry the complete version from the newest artifact # in out/, while an empty directory falls through to the existing tag-derived # editable prompt. @@ -88,6 +107,14 @@ require("Heltec_E290_companion_usb_ble", "build_flags", "BLE_PIN_CODE=123456") require("Heltec_T190_companion_radio_usb_ble_", "build_flags", "ENABLE_USB_INTERFACE") require("Heltec_T190_companion_radio_usb_ble_", "build_flags", "BLE_PIN_CODE=123456") +rc32_repeater = "heltec_rc32_repeater" +require(rc32_repeater, "platform", "55.03.311") +require(rc32_repeater, "platform_packages", "esp32-core-3.3.11.tar.xz") +require(rc32_repeater, "build_flags", "ESP32_POST_BOOT_CPU_FREQ=160") +require(rc32_repeater, "build_flags", "RC32_PERIPHERAL_WARMUP_MS=100") +require(rc32_repeater, "build_flags", "SX126X_ALLOW_RECOVERABLE_INIT_STATUS=1") +reject(rc32_repeater, "build_flags", "ESP32_CPU_FREQ=160") + for env_name in ( "Heltec_t114_without_display_repeater", "Heltec_t114_repeater", diff --git a/tools/mota/test_mota.py b/tools/mota/test_mota.py index c92a0b124..9ef2e8f54 100644 --- a/tools/mota/test_mota.py +++ b/tools/mota/test_mota.py @@ -414,16 +414,16 @@ def test_esp32_s3_full_profiles_inherit_dio_boot_mode(): build = (root / "build.sh").read_text(encoding="utf-8") framework_preflight = build.split( - "prepare_esp32_dual_cdc_framework()", 1 + "prepare_esp32_arduino3_framework()", 1 )[1].split("requires_esp32_companion_full_ota_fallback()", 1)[0] - assert "is_esp32_dual_cdc_companion_radio_full_target" in framework_preflight + assert "requires_esp32_arduino3_framework" in framework_preflight assert 'pio pkg install -e "$pio_env_name"' in framework_preflight build_call = build.split( 'print_build_flags "$pio_env_name" "$env_name"', 1 )[1].split( "restore_platformio_build_flags", 1 )[0] - assert 'prepare_esp32_dual_cdc_framework "$env_name" "$pio_env_name"' in build_call + assert 'prepare_esp32_arduino3_framework "$env_name" "$pio_env_name"' in build_call assert 'if [ "$build_status" -eq 0 ]; then' in build_call assert 'flock "$platformio_package_lock_fd"' in build_call assert 'flock -u "$platformio_package_lock_fd"' in build_call diff --git a/variants/heltec_rc32/HeltecRC32Board.cpp b/variants/heltec_rc32/HeltecRC32Board.cpp index 59920d238..ae8e8b662 100644 --- a/variants/heltec_rc32/HeltecRC32Board.cpp +++ b/variants/heltec_rc32/HeltecRC32Board.cpp @@ -18,6 +18,9 @@ void HeltecRC32Board::begin() { periph_power.begin(); periph_power.claim(); +#ifdef RC32_PERIPHERAL_WARMUP_MS + delay(RC32_PERIPHERAL_WARMUP_MS); +#endif esp_reset_reason_t reason = esp_reset_reason(); if (reason == ESP_RST_DEEPSLEEP) { @@ -31,6 +34,22 @@ void HeltecRC32Board::begin() { } } +void HeltecRC32Board::onBootComplete() { +#ifdef ESP32_POST_BOOT_CPU_FREQ + const uint32_t current_mhz = getCpuFrequencyMhz(); + if (current_mhz > ESP32_POST_BOOT_CPU_FREQ) { + if (setCpuFrequencyMhz(ESP32_POST_BOOT_CPU_FREQ)) { + MESH_DEBUG_PRINTLN("RC32 startup complete; CPU reduced from %lu to %u MHz", + (unsigned long)current_mhz, + (unsigned)ESP32_POST_BOOT_CPU_FREQ); + } else { + MESH_DEBUG_PRINTLN("RC32 could not set post-boot CPU frequency to %u MHz", + (unsigned)ESP32_POST_BOOT_CPU_FREQ); + } + } +#endif +} + void HeltecRC32Board::powerOff() { enterDeepSleep(0); } diff --git a/variants/heltec_rc32/HeltecRC32Board.h b/variants/heltec_rc32/HeltecRC32Board.h index 5b2093c8f..837126a0e 100644 --- a/variants/heltec_rc32/HeltecRC32Board.h +++ b/variants/heltec_rc32/HeltecRC32Board.h @@ -19,6 +19,7 @@ public: HeltecRC32Board() : periph_power(SENSOR_POWER_CTRL_PIN, SENSOR_POWER_ON){} void begin(); + void onBootComplete() override; void onBeforeTransmit() override; void onAfterTransmit() override; void powerOff() override; diff --git a/variants/heltec_rc32/platformio.ini b/variants/heltec_rc32/platformio.ini index 1b9803954..2777ca94e 100644 --- a/variants/heltec_rc32/platformio.ini +++ b/variants/heltec_rc32/platformio.ini @@ -1,5 +1,9 @@ [Heltec_RC32] extends = esp32_base +platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.311/platform-espressif32.zip +platform_packages = + framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32/releases/download/3.3.11/esp32-core-3.3.11.tar.xz + framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.3.11/esp32-core-3.3.11-libs.tar.xz board = heltec-rc32 board_build.partitions = default_16MB.csv build_flags = @@ -9,7 +13,10 @@ build_flags = -I src/helpers/ui -D HELTEC_RC32 -D USE_SX1262 - -D ESP32_CPU_FREQ=160 + ; Keep the board at its 240 MHz default through peripheral initialization. + -D ESP32_POST_BOOT_CPU_FREQ=160 + -D RC32_PERIPHERAL_WARMUP_MS=100 + -D SX126X_ALLOW_RECOVERABLE_INIT_STATUS=1 -D RADIO_CLASS=CustomSX1262 -D WRAPPER_CLASS=CustomSX1262Wrapper -D P_LORA_DIO_1=14 diff --git a/variants/heltec_rc32/target.cpp b/variants/heltec_rc32/target.cpp index cc7e4deb7..233b31bd8 100644 --- a/variants/heltec_rc32/target.cpp +++ b/variants/heltec_rc32/target.cpp @@ -8,7 +8,8 @@ HeltecRC32Board board; #if defined(P_LORA_SCLK) static SPIClass spi(FSPI); - RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi, + SPISettings(4000000, MSBFIRST, SPI_MODE0)); #else RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); #endif