diff --git a/zephcore/boards/esp32/heltec_wifi_lora32_v3/board.overlay b/zephcore/boards/esp32/heltec_wifi_lora32_v3/board.overlay index c5c8ac5..8aea505 100644 --- a/zephcore/boards/esp32/heltec_wifi_lora32_v3/board.overlay +++ b/zephcore/boards/esp32/heltec_wifi_lora32_v3/board.overlay @@ -9,13 +9,14 @@ * LoRa SPI2 pins: SCLK=9, MOSI=10, MISO=11, NSS=8 * LoRa control: DIO1=14, BUSY=13, RESET=12 * Display I2C0: SDA=17, SCL=18 (SSD1306 @ 0x3C) - * Battery ADC: GPIO1 (ADC1 ch0), enable GPIO37, divider 100K/390K + * Battery ADC: GPIO1 = ADC1 ch0 (&adc0), enable GPIO37 active-HIGH, divider 100K/390K * Vext: GPIO36 (active-LOW, powers OLED) — regulator + vin-supply in overlay * Button: GPIO0 * LED: GPIO35 */ #include +#include / { chosen { @@ -33,22 +34,36 @@ buttons: buttons { }; - /* Battery ADC — ZephCore reads via zephyr,user { io-channels } + /* Battery ADC — ZephCore reads via zephyr,user { io-channels }. * Upstream vbatt node uses voltage-divider compatible which ZephCore - * doesn't use. Multiplier: 3300mV full-scale * 5.42x = 17886. - * (5.42x is Arduino's empirical divider factor for V3/V4.) */ + * doesn't use (deleted below). + * + * IMPORTANT: the battery pin is GPIO1 = ADC unit 1 channel 0. In the + * upstream esp32s3 DTS the node *labelled* adc0 is unit 1 and the node + * labelled adc1 is unit 2 (the label is 0-indexed, the unit is 1-indexed). + * So GPIO1 lives on &adc0. The previous &adc1 (unit 2) channel 0 mapped to + * ADC2_CH0 = GPIO11 = the SX1262 MISO — wrong peripheral, always read 0. + * + * Multiplier: Vref_internal(1100) * divider_ratio(5.2) = 5720, matching the + * Heltec Wireless Tracker on the same 100K/390K divider (was 17886, which + * assumed a 3300mV full-scale that ADC_REF_INTERNAL does not provide). */ zephyr,user { - io-channels = <&adc1 0>; - vbat-mv-multiplier = <17886>; + io-channels = <&adc0 0>; + vbat-mv-multiplier = <5720>; }; /* Battery ADC enable — GPIO37 (gpio1 pin 5) must be driven to read. - * Upstream has adc_ctrl power-domain-gpio; ZephCore expects a - * regulator-fixed named vbat_enable. */ + * Upstream has an adc_ctrl power-domain-gpio on the same GPIO37; it is + * deleted below so this regulator is the sole owner (else the two fight + * over the pin and the per-read enable/disable in ZephyrBoard is not + * deterministic). Active-HIGH on V3.2/V4 (transistor in the enable path); + * the previous GPIO_ACTIVE_LOW never powered the divider so the read was 0. + * ZephyrBoard toggles this per read, so between reads the divider draws + * nothing. (Early V3 boards may need GPIO_ACTIVE_LOW — see GH #50.) */ vbat_enable: vbat-enable { compatible = "regulator-fixed"; regulator-name = "vbat-enable"; - enable-gpios = <&gpio1 5 GPIO_ACTIVE_LOW>; + enable-gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>; }; /* Vext — GPIO36, active-LOW enables rail (matches Arduino periph_power / MeshCore @@ -162,3 +177,37 @@ /* After SSD1306 no longer references vext_ctrl, remove upstream power-domain node. */ /delete-node/ &vext_ctrl; + +/* Battery ADC channel configuration on adc0 (ADC unit 1 -> GPIO1). + * adc0 is status="disabled" upstream, so enable it here. Without the channel + * node adc_channel_setup_dt() fails and getBattMilliVolts() returns 0 before it + * ever performs a conversion — the primary cause of the hard "0% (0mV)". + * ADC_GAIN_1 = 0 dB with ADC_REF_INTERNAL (~1100 mV); the divider output for a + * 3.0-4.2 V cell (~580-810 mV) sits well inside range. */ +&adc0 { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + }; +}; + +/* Remove the upstream 'vbatt' voltage-divider (io-channels on &adc1 = unit 2 = + * GPIO11 = SX1262 MISO — wrong node) and the adc_ctrl power-domain that also + * claims GPIO37 and fights the vbat_enable regulator above. */ +/ { + /delete-node/ vbatt; +}; +/delete-node/ &adc_ctrl; + +/* ADC unit 2 (label adc1) is unused on this board; keep it disabled so nothing + * can claim GPIO11 (SX1262 MISO) as an ADC pad. */ +&adc1 { + status = "disabled"; +}; diff --git a/zephcore/boards/esp32/heltec_wifi_lora32_v4/board.overlay b/zephcore/boards/esp32/heltec_wifi_lora32_v4/board.overlay index 8063643..7ed82d9 100644 --- a/zephcore/boards/esp32/heltec_wifi_lora32_v4/board.overlay +++ b/zephcore/boards/esp32/heltec_wifi_lora32_v4/board.overlay @@ -3,14 +3,14 @@ * Heltec WiFi LoRa 32 V4 — ZephCore overlay * * The DTS already defines: SX1262 (lora0), SSD1306 OLED, button, LED, PWM, - * vext_enable, adc_ctrl, fem_power, I2C0, SPI2, BLE, WiFi. + * vext_enable, fem_power, I2C0, SPI2, BLE, WiFi. * This overlay adds ZephCore-specific integration on top. * * LoRa SPI2: SCLK=9, MOSI=10, MISO=11, NSS=8 * LoRa ctrl: DIO1=14, BUSY=13, RESET=12 * FEM: POWER=7, GC1109 CSD=2, CPS=46 (V4.2 — for V4.3/KCT8103L see heltec_wifi_lora32_v43) * Display I2C0: SDA=17, SCL=18, RST=21 - * Battery ADC: GPIO1 (ADC1 ch0), enable GPIO37, divider 100K/390K + * Battery ADC: GPIO1 = ADC1_CH0 (&adc0), enable GPIO37 active-HIGH, divider 100K/390K * Vext: GPIO36 (active-HIGH, powers OLED) * Button: GPIO0 * LED: GPIO35 @@ -26,17 +26,21 @@ }; /* Battery ADC — ZephCore reads via zephyr,user { io-channels }. - * Multiplier: 3300mV full-scale * 5.42x empirical divider = 17886 mV. - * (Same as V3 — identical resistor divider topology.) */ + * GPIO1 = ADC unit 1 = node &adc0 (label is 0-indexed, unit is 1-indexed); + * the old &adc1 was unit 2 = GPIO11 = SX1262 MISO — always read 0. + * Multiplier: Vref_internal(1100) * divider_ratio(5.2) = 5720 on the + * 100K/390K divider (was 17886, which assumed a 3300mV full-scale that + * ADC_REF_INTERNAL does not provide). Channel config is in the DTS. */ zephyr,user { - io-channels = <&adc1 0>; - vbat-mv-multiplier = <17886>; + io-channels = <&adc0 0>; + vbat-mv-multiplier = <5720>; }; /* Battery ADC enable — ZephCore uses vbat_enable regulator alias. - * GPIO37 (gpio1 pin 5) driven HIGH enables the ADC input path. - * The DTS adc_ctrl node is power-domain-gpio which ZephCore doesn't use; - * this regulator-fixed is the actual driver. */ + * GPIO37 (gpio1 pin 5) driven HIGH enables the ADC input path. ZephyrBoard + * toggles this per read (enable, sample, disable), so the divider draws + * nothing between reads. The DTS no longer defines an adc_ctrl power-domain + * on the same GPIO37, so this regulator is the sole owner. */ vbat_enable: vbat-enable { compatible = "regulator-fixed"; regulator-name = "vbat-enable"; diff --git a/zephcore/boards/esp32/heltec_wifi_lora32_v4/heltec_wifi_lora32_v4_procpu.dts b/zephcore/boards/esp32/heltec_wifi_lora32_v4/heltec_wifi_lora32_v4_procpu.dts index 2d67431..8d7d450 100644 --- a/zephcore/boards/esp32/heltec_wifi_lora32_v4/heltec_wifi_lora32_v4_procpu.dts +++ b/zephcore/boards/esp32/heltec_wifi_lora32_v4/heltec_wifi_lora32_v4_procpu.dts @@ -9,7 +9,7 @@ * Button: GPIO0 * LED: GPIO35 (PWM via LEDC0) * Vext: GPIO36 active-HIGH (powers OLED and sensors) - * BatADC: GPIO1 (ADC1 ch0), enable GPIO37 active-LOW + * BatADC: GPIO1 = ADC1_CH0 (&adc0), enable GPIO37 active-HIGH (vbat_enable in overlay) * * V4.2 (this file) — GC1109 PA, TX_EN on GPIO46. * V4.3 (KCT8103L PA, CTX on GPIO5) → see heltec_wifi_lora32_v43. @@ -36,6 +36,7 @@ #include #include #include +#include #include / { @@ -78,12 +79,9 @@ }; }; - vbatt { - compatible = "voltage-divider"; - io-channels = <&adc1 0>; /* GPIO1 = ADC1 ch0 */ - output-ohms = <100000>; - full-ohms = <(100000 + 390000)>; - }; + /* No 'vbatt' voltage-divider node: ZephCore reads the battery via + * zephyr,user { io-channels } (see board.overlay), and a voltage-divider + * on &adc1 ch0 would target GPIO11 = the SX1262 MISO. See &adc0 below. */ /* Vext — powers OLED and external peripherals (active-HIGH on GPIO36). * regulator-fixed + SSD1306 vin-supply: powers rail at boot without relying on @@ -96,14 +94,9 @@ regulator-always-on; }; - /* ADC enable — GPIO37 (gpio1 pin 5) must be driven HIGH to read battery. - * ZephCore uses the vbat_enable regulator alias for this. */ - adc_ctrl: adc_ctrl { - compatible = "power-domain-gpio"; - enable-gpios = <&gpio1 5 GPIO_ACTIVE_LOW>; /* GPIO37 = gpio1 pin 5, active-LOW */ - #power-domain-cells = <0>; - label = "ADC Control"; - }; + /* No adc_ctrl power-domain node: it would claim GPIO37 (gpio1 pin 5) with + * the opposite polarity and fight the vbat_enable regulator in board.overlay, + * which is the actual per-read driver for the ADC divider enable. */ /* FEM LDO power supply — GPIO7 powers the GC1109/KCT8103L LDO. * Must be HIGH before any radio operation. Keep always-on. */ @@ -126,8 +119,30 @@ }; }; -&adc1 { +/* Battery ADC on adc0 (ADC unit 1 -> GPIO1 = ADC1_CH0). The node *labelled* + * adc0 is unit 1; the node labelled adc1 is unit 2 (= GPIO11 = SX1262 MISO). + * The channel@0 config is required — without it adc_channel_setup_dt() fails and + * getBattMilliVolts() returns 0 before ever performing a conversion. ADC_GAIN_1 + * (0 dB) with ADC_REF_INTERNAL (~1100 mV); the 100K/390K divider output for a + * 3.0-4.2 V cell sits well inside range. */ +&adc0 { status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + }; +}; + +/* ADC unit 2 (label adc1) is unused; keep it disabled so nothing can claim + * GPIO11 (SX1262 MISO) as an ADC pad. */ +&adc1 { + status = "disabled"; }; &uart0 { diff --git a/zephcore/boards/esp32/heltec_wifi_lora32_v43/board.overlay b/zephcore/boards/esp32/heltec_wifi_lora32_v43/board.overlay index 7e49c2e..71fcebe 100644 --- a/zephcore/boards/esp32/heltec_wifi_lora32_v43/board.overlay +++ b/zephcore/boards/esp32/heltec_wifi_lora32_v43/board.overlay @@ -3,14 +3,14 @@ * Heltec WiFi LoRa 32 V4.3 — ZephCore overlay * * The DTS already defines: SX1262 (lora0), SSD1306 OLED, button, LED, PWM, - * vext_enable, adc_ctrl, fem_power, I2C0, SPI2, BLE, WiFi. + * vext_enable, fem_power, I2C0, SPI2, BLE, WiFi. * This overlay adds ZephCore-specific integration on top. * * LoRa SPI2: SCLK=9, MOSI=10, MISO=11, NSS=8 * LoRa ctrl: DIO1=14, BUSY=13, RESET=12 * FEM: POWER=7, KCT8103L CSD=2, CTX=5 * Display I2C0: SDA=17, SCL=18, RST=21 - * Battery ADC: GPIO1 (ADC1 ch0), enable GPIO37, divider 100K/390K + * Battery ADC: GPIO1 = ADC1_CH0 (&adc0), enable GPIO37 active-HIGH, divider 100K/390K * Vext: GPIO36 (active-HIGH, powers OLED) * Button: GPIO0 * LED: GPIO35 @@ -26,17 +26,21 @@ }; /* Battery ADC — ZephCore reads via zephyr,user { io-channels }. - * Multiplier: 3300mV full-scale * 5.42x empirical divider = 17886 mV. - * (Same as V3 — identical resistor divider topology.) */ + * GPIO1 = ADC unit 1 = node &adc0 (label is 0-indexed, unit is 1-indexed); + * the old &adc1 was unit 2 = GPIO11 = SX1262 MISO — always read 0. + * Multiplier: Vref_internal(1100) * divider_ratio(5.2) = 5720 on the + * 100K/390K divider (was 17886, which assumed a 3300mV full-scale that + * ADC_REF_INTERNAL does not provide). Channel config is in the DTS. */ zephyr,user { - io-channels = <&adc1 0>; - vbat-mv-multiplier = <17886>; + io-channels = <&adc0 0>; + vbat-mv-multiplier = <5720>; }; /* Battery ADC enable — ZephCore uses vbat_enable regulator alias. - * GPIO37 (gpio1 pin 5) driven HIGH enables the ADC input path. - * The DTS adc_ctrl node is power-domain-gpio which ZephCore doesn't use; - * this regulator-fixed is the actual driver. */ + * GPIO37 (gpio1 pin 5) driven HIGH enables the ADC input path. ZephyrBoard + * toggles this per read (enable, sample, disable), so the divider draws + * nothing between reads. The DTS no longer defines an adc_ctrl power-domain + * on the same GPIO37, so this regulator is the sole owner. */ vbat_enable: vbat-enable { compatible = "regulator-fixed"; regulator-name = "vbat-enable"; diff --git a/zephcore/boards/esp32/heltec_wifi_lora32_v43/heltec_wifi_lora32_v43_procpu.dts b/zephcore/boards/esp32/heltec_wifi_lora32_v43/heltec_wifi_lora32_v43_procpu.dts index f203eca..b40906d 100644 --- a/zephcore/boards/esp32/heltec_wifi_lora32_v43/heltec_wifi_lora32_v43_procpu.dts +++ b/zephcore/boards/esp32/heltec_wifi_lora32_v43/heltec_wifi_lora32_v43_procpu.dts @@ -9,7 +9,7 @@ * Button: GPIO0 * LED: GPIO35 (PWM via LEDC0) * Vext: GPIO36 active-HIGH (powers OLED and sensors) - * BatADC: GPIO1 (ADC1 ch0), enable GPIO37 active-LOW + * BatADC: GPIO1 = ADC1_CH0 (&adc0), enable GPIO37 active-HIGH (vbat_enable in overlay) * * V4.3 vs V4.2 difference — FEM chip changed: * V4.2: GC1109 PA — TX_EN on GPIO46 (CPS) @@ -35,6 +35,7 @@ #include #include #include +#include #include / { @@ -77,12 +78,9 @@ }; }; - vbatt { - compatible = "voltage-divider"; - io-channels = <&adc1 0>; /* GPIO1 = ADC1 ch0 */ - output-ohms = <100000>; - full-ohms = <(100000 + 390000)>; - }; + /* No 'vbatt' voltage-divider node: ZephCore reads the battery via + * zephyr,user { io-channels } (see board.overlay), and a voltage-divider + * on &adc1 ch0 would target GPIO11 = the SX1262 MISO. See &adc0 below. */ /* Vext — powers OLED and external peripherals (active-HIGH on GPIO36). * regulator-fixed + SSD1306 vin-supply: powers rail at boot without relying on @@ -95,13 +93,9 @@ regulator-always-on; }; - /* ADC enable — GPIO37 (gpio1 pin 5) must be driven HIGH to read battery */ - adc_ctrl: adc_ctrl { - compatible = "power-domain-gpio"; - enable-gpios = <&gpio1 5 GPIO_ACTIVE_LOW>; /* GPIO37 = gpio1 pin 5, active-LOW */ - #power-domain-cells = <0>; - label = "ADC Control"; - }; + /* No adc_ctrl power-domain node: it would claim GPIO37 (gpio1 pin 5) with + * the opposite polarity and fight the vbat_enable regulator in board.overlay, + * which is the actual per-read driver for the ADC divider enable. */ /* FEM LDO power supply — GPIO7 powers the KCT8103L LDO. * Must be HIGH before any radio operation. Keep always-on. */ @@ -124,8 +118,30 @@ }; }; -&adc1 { +/* Battery ADC on adc0 (ADC unit 1 -> GPIO1 = ADC1_CH0). The node *labelled* + * adc0 is unit 1; the node labelled adc1 is unit 2 (= GPIO11 = SX1262 MISO). + * The channel@0 config is required — without it adc_channel_setup_dt() fails and + * getBattMilliVolts() returns 0 before ever performing a conversion. ADC_GAIN_1 + * (0 dB) with ADC_REF_INTERNAL (~1100 mV); the 100K/390K divider output for a + * 3.0-4.2 V cell sits well inside range. */ +&adc0 { status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + }; +}; + +/* ADC unit 2 (label adc1) is unused; keep it disabled so nothing can claim + * GPIO11 (SX1262 MISO) as an ADC pad. */ +&adc1 { + status = "disabled"; }; &uart0 {