From 53cf183a5c5a0d797f1797de5031da4432fc982a Mon Sep 17 00:00:00 2001 From: liquidraver <504870+liquidraver@users.noreply.github.com> Date: Mon, 7 Sep 2026 22:29:10 +0200 Subject: [PATCH] esp gps and BT to 9dBm --- zephcore/boards/common/esp32_common.conf | 39 +++++++++- .../esp32/heltec_wifi_lora32_v4/board.overlay | 76 +++++++++++++++++++ .../heltec_wifi_lora32_v43/board.overlay | 76 +++++++++++++++++++ 3 files changed, 190 insertions(+), 1 deletion(-) diff --git a/zephcore/boards/common/esp32_common.conf b/zephcore/boards/common/esp32_common.conf index f6865d6..6e157ff 100644 --- a/zephcore/boards/common/esp32_common.conf +++ b/zephcore/boards/common/esp32_common.conf @@ -6,7 +6,44 @@ # ========== BLE: ESP32-specific controller settings ========== # DLE managed internally by Espressif blob — no CONFIG_BT_CTLR_DATA_LENGTH_MAX -# TX power managed internally by Espressif blob — no CONFIG_BT_CTLR_TX_PWR_* + +# BLE TX power — +9 dBm, matching stock Arduino MeshCore. +# +# This was previously left unset, on the belief that the Espressif blob managed +# TX power internally. It does not. Zephyr's HCI driver initialises the +# controller with BT_CONTROLLER_INIT_CONFIG_DEFAULT() (hci_esp32.c), whose +# .txpwr_dft comes from CONFIG_ESP32_BT_CTLR_DFT_TX_POWER_LEVEL_EFF — and that +# macro is a #if chain over Zephyr's CONFIG_BT_CTLR_TX_PWR_* symbols with an +# #else /* use 0dB TX power as default */ +# fallback. With none of them set the chain fell through and every ESP32 board +# advertised and connected at 0 dBm, while stock Arduino MeshCore inherits the +# Arduino-ESP32 sdkconfig (CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P9=y) and runs at +# +9 dBm. That 9 dB deficit is the reported "ZephCore has worse BLE range than +# official firmware" on Heltec V4 — roughly 2.8x the free-space distance. +# +# The symbols ARE settable here: BT_ESP32 selects HAS_BT_CTLR, and upstream +# gates the +9/+12/+15/+18/+21 options on `depends on SOC_FAMILY_ESPRESSIF_ESP32` +# specifically so ESP32 boards can pick one. +# +# Why +9 and not the +20 dBm ceiling the S3/C3/C6 parts can reach: +9 is what +# stock MeshCore runs, it is comfortably inside the EU 2.4 GHz 100 mW (20 dBm) +# EIRP limit, and it is the ONLY level that appears in all three Espressif +# header families — so one line covers every board with no per-board override: +# +# include/esp32c3 (also S3) PLUS_9 -> ESP_PWR_LVL_P9 ceiling +20 dBm +# include/esp32c6 (c5/h2) PLUS_9 -> literal 9 ceiling +20 dBm +# include/esp32 (classic) PLUS_9 -> ESP_PWR_LVL_P9 ceiling +9 dBm +# +# That last row is the trap if anyone raises this later: the classic ESP32 +# (ttgo_tbeam, ttgo_lora32) tops out at ESP_PWR_LVL_P9 and its chain has no +# PLUS_12/15/18/21 arm at all, so any higher setting matches nothing there and +# silently drops those two boards back to the 0 dBm `#else` — no warning, no +# build error. Raising this above +9 therefore requires a PLUS_9 override in +# both classic board.conf files. +# +# nRF/nRF54L use CONFIG_BT_CTLR_TX_PWR_PLUS_8 (their own ceiling); +8 is not +# selectable on ESP32 and the ESP32 power enum has no P8 — 3 dB steps only. +CONFIG_BT_CTLR_TX_PWR_PLUS_9=y # Host privacy: ESP32-only override. Load-bearing for iOS on the Espressif # controller (Zephyr #84182-class). nRF/MG24 keep privacy OFF. diff --git a/zephcore/boards/esp32/heltec_wifi_lora32_v4/board.overlay b/zephcore/boards/esp32/heltec_wifi_lora32_v4/board.overlay index 0fb6d1e..7a74636 100644 --- a/zephcore/boards/esp32/heltec_wifi_lora32_v4/board.overlay +++ b/zephcore/boards/esp32/heltec_wifi_lora32_v4/board.overlay @@ -14,6 +14,8 @@ * Vext: GPIO36 (active-HIGH, powers OLED) * Button: GPIO0 * LED: GPIO35 + * GPS UART1: TX=GPIO39, RX=GPIO38, 9600 baud (optional external module) + * GPS ctrl: enable GPIO34 (active-LOW), reset GPIO42 (active-LOW) */ #include @@ -47,6 +49,45 @@ enable-gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>; /* GPIO37 = gpio1 pin 5, HIGH enables (V4/V4.3) */ }; + /* GPS enable — GPIO34, active-LOW (Arduino MeshCore heltec_v4: + * PIN_GPS_EN=34, PIN_GPS_EN_ACTIVE=LOW). ZephCore's GPS manager drives + * this via the gps-enable alias. + * + * GPIO33-37 are the octal-PSRAM pads on ESP32-S3 modules that carry one; + * the V4 has 2 MB *quad* PSRAM, so 33/34 are free here — 35/36/37 are + * already spent on LED/Vext/ADC-ctrl above, which is the same evidence. */ + gps_en: gps-enable { + compatible = "gpio-leds"; + gps_enable_pin: gps_enable { + gpios = <&gpio1 2 GPIO_ACTIVE_LOW>; /* GPIO34 = gpio1 pin 2 */ + label = "GPS Enable"; + }; + }; + + /* GPS reset — GPIO42, active-LOW (Arduino PIN_GPS_RESET=42, + * PIN_GPS_RESET_ACTIVE=LOW). Declared so the line is always driven to a + * defined level rather than left floating into the module's reset input. + * + * Declaring gps-reset moves this board onto the manager's + * HAS_T1000_GPS_CONTROL power sequence (EN -> reset -> release). That is + * safe without a VRTC pin — every VRTC/SLEEP step in that path is behind + * its own #if. Note the power-ON pulse uses the raw GPIO_OUTPUT_HIGH flag, + * so on an active-LOW line it de-asserts rather than pulsing; the useful + * half is power-OFF, which asserts reset (logical 1) and holds the module + * down while its supply is gated. */ + gps_rst: gps-reset { + compatible = "gpio-leds"; + gps_reset_pin: gps_reset { + gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; /* GPIO42 = gpio1 pin 10 */ + label = "GPS Reset"; + }; + }; + + aliases { + gps-enable = &gps_enable_pin; + gps-reset = &gps_reset_pin; + }; + /* Button chain — same mapping as RAK4631 / WisMesh Pocket (rak4631/board.overlay). */ user_btn_longpress { compatible = "zephyr,input-longpress"; @@ -77,6 +118,41 @@ rx-boosted; }; +/* GPS on UART1 — optional external NMEA module on the pin header, wired to the + * same pins Arduino MeshCore uses for this board (PIN_GPS_RX=38, PIN_GPS_TX=39; + * Arduino names them from the MCU's point of view, so RX=38 is the MCU input). + * Upstream leaves uart1 disabled and gives it no pinctrl, so both are set here. + * GPIO38/39 are otherwise unclaimed on the V4; the console stays on usb_serial + * (or uart0/GPIO43-44 for companion USB builds). 9600 baud matches Arduino's + * default for this variant. + * + * The node is unconditional, like the V3's gnss node for its optional module: + * with nothing fitted the driver simply never sees a fix. */ +&pinctrl { + uart1_default: uart1_default { + group1 { + pinmux = ; + output-high; + }; + + group2 { + pinmux = ; + bias-pull-up; + }; + }; +}; + +&uart1 { + status = "okay"; + current-speed = <9600>; + pinctrl-0 = <&uart1_default>; + pinctrl-names = "default"; + + gnss: gnss-nmea-generic { + compatible = "gnss-nmea-generic"; + }; +}; + /* I2C sensors — auto-detected at runtime */ &i2c0 { #include "../../common/sensors-i2c.dtsi" diff --git a/zephcore/boards/esp32/heltec_wifi_lora32_v43/board.overlay b/zephcore/boards/esp32/heltec_wifi_lora32_v43/board.overlay index d8d73f8..3cf9173 100644 --- a/zephcore/boards/esp32/heltec_wifi_lora32_v43/board.overlay +++ b/zephcore/boards/esp32/heltec_wifi_lora32_v43/board.overlay @@ -14,6 +14,8 @@ * Vext: GPIO36 (active-HIGH, powers OLED) * Button: GPIO0 * LED: GPIO35 + * GPS UART1: TX=GPIO39, RX=GPIO38, 9600 baud (optional external module) + * GPS ctrl: enable GPIO34 (active-LOW), reset GPIO42 (active-LOW) */ #include @@ -47,6 +49,45 @@ enable-gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>; /* GPIO37 = gpio1 pin 5, HIGH enables (V4/V4.3) */ }; + /* GPS enable — GPIO34, active-LOW (Arduino MeshCore heltec_v4: + * PIN_GPS_EN=34, PIN_GPS_EN_ACTIVE=LOW). ZephCore's GPS manager drives + * this via the gps-enable alias. + * + * GPIO33-37 are the octal-PSRAM pads on ESP32-S3 modules that carry one; + * the V4 has 2 MB *quad* PSRAM, so 33/34 are free here — 35/36/37 are + * already spent on LED/Vext/ADC-ctrl above, which is the same evidence. */ + gps_en: gps-enable { + compatible = "gpio-leds"; + gps_enable_pin: gps_enable { + gpios = <&gpio1 2 GPIO_ACTIVE_LOW>; /* GPIO34 = gpio1 pin 2 */ + label = "GPS Enable"; + }; + }; + + /* GPS reset — GPIO42, active-LOW (Arduino PIN_GPS_RESET=42, + * PIN_GPS_RESET_ACTIVE=LOW). Declared so the line is always driven to a + * defined level rather than left floating into the module's reset input. + * + * Declaring gps-reset moves this board onto the manager's + * HAS_T1000_GPS_CONTROL power sequence (EN -> reset -> release). That is + * safe without a VRTC pin — every VRTC/SLEEP step in that path is behind + * its own #if. Note the power-ON pulse uses the raw GPIO_OUTPUT_HIGH flag, + * so on an active-LOW line it de-asserts rather than pulsing; the useful + * half is power-OFF, which asserts reset (logical 1) and holds the module + * down while its supply is gated. */ + gps_rst: gps-reset { + compatible = "gpio-leds"; + gps_reset_pin: gps_reset { + gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; /* GPIO42 = gpio1 pin 10 */ + label = "GPS Reset"; + }; + }; + + aliases { + gps-enable = &gps_enable_pin; + gps-reset = &gps_reset_pin; + }; + /* Button chain — same mapping as RAK4631 / WisMesh Pocket (rak4631/board.overlay). */ user_btn_longpress { compatible = "zephyr,input-longpress"; @@ -77,6 +118,41 @@ rx-boosted; }; +/* GPS on UART1 — optional external NMEA module on the pin header, wired to the + * same pins Arduino MeshCore uses for this board (PIN_GPS_RX=38, PIN_GPS_TX=39; + * Arduino names them from the MCU's point of view, so RX=38 is the MCU input). + * Upstream leaves uart1 disabled and gives it no pinctrl, so both are set here. + * GPIO38/39 are otherwise unclaimed on the V4; the console stays on usb_serial + * (or uart0/GPIO43-44 for companion USB builds). 9600 baud matches Arduino's + * default for this variant. + * + * The node is unconditional, like the V3's gnss node for its optional module: + * with nothing fitted the driver simply never sees a fix. */ +&pinctrl { + uart1_default: uart1_default { + group1 { + pinmux = ; + output-high; + }; + + group2 { + pinmux = ; + bias-pull-up; + }; + }; +}; + +&uart1 { + status = "okay"; + current-speed = <9600>; + pinctrl-0 = <&uart1_default>; + pinctrl-names = "default"; + + gnss: gnss-nmea-generic { + compatible = "gnss-nmea-generic"; + }; +}; + /* I2C sensors — auto-detected at runtime */ &i2c0 { #include "../../common/sensors-i2c.dtsi"