diff --git a/zephcore/CMakeLists.txt b/zephcore/CMakeLists.txt index 9e426f5..4e45a1e 100644 --- a/zephcore/CMakeLists.txt +++ b/zephcore/CMakeLists.txt @@ -323,12 +323,25 @@ endif() # Enables WiFi AP + HTTP server + MCUboot image management. # Requires --sysbuild to build MCUboot alongside the app. # Non-ESP32 builds and companion builds are unaffected. +# Boards with insufficient DRAM (e.g. classic ESP32 PICO-D4) can opt out +# by setting CONFIG_ZEPHCORE_WIFI_OTA=n in their board.conf. if(ZEPHCORE_PLATFORM_CONF MATCHES "esp32_common" AND EXTRA_CONF_FILE MATCHES "repeater") - set(ZEPHCORE_WIFI_OTA_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/wifi_ota.conf") - if(EXISTS ${ZEPHCORE_WIFI_OTA_CONF}) - list(APPEND ZEPHCORE_CONF_FILES ${ZEPHCORE_WIFI_OTA_CONF}) - zephcore_auto_pair_overlay("${ZEPHCORE_WIFI_OTA_CONF}") - message(STATUS " WiFi OTA: auto-enabled (ESP32 repeater)") + set(_wifi_ota_disabled FALSE) + if(ZEPHCORE_BOARD_CONF AND EXISTS "${ZEPHCORE_BOARD_CONF}") + file(STRINGS "${ZEPHCORE_BOARD_CONF}" _board_conf_lines) + if("CONFIG_ZEPHCORE_WIFI_OTA=n" IN_LIST _board_conf_lines) + set(_wifi_ota_disabled TRUE) + endif() + endif() + if(NOT _wifi_ota_disabled) + set(ZEPHCORE_WIFI_OTA_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/wifi_ota.conf") + if(EXISTS ${ZEPHCORE_WIFI_OTA_CONF}) + list(APPEND ZEPHCORE_CONF_FILES ${ZEPHCORE_WIFI_OTA_CONF}) + zephcore_auto_pair_overlay("${ZEPHCORE_WIFI_OTA_CONF}") + message(STATUS " WiFi OTA: auto-enabled (ESP32 repeater)") + endif() + else() + message(STATUS " WiFi OTA: disabled (board.conf opt-out)") endif() endif() diff --git a/zephcore/adapters/board/ZephyrBoard.cpp b/zephcore/adapters/board/ZephyrBoard.cpp index 5475ace..c74e8b8 100644 --- a/zephcore/adapters/board/ZephyrBoard.cpp +++ b/zephcore/adapters/board/ZephyrBoard.cpp @@ -64,6 +64,48 @@ static const struct device *vbat_enable_dev = NULL; #define VBAT_MV_MULTIPLIER CONFIG_ZEPHCORE_VBAT_MV_MULTIPLIER #endif #define VBAT_ADC_SAMPLES 8 +#elif DT_NODE_EXISTS(DT_NODELABEL(axp192_pmic)) +/* AXP192 PMIC: battery voltage via I2C registers 0x78/0x79. + * Used on boards without a GPIO-connected battery voltage divider, + * e.g. LilyGo T-Beam v1.x where battery management is internal to the PMIC. */ +#include + +/* AXP192 register map (datasheet section 9) */ +#define AXP192_REG_ADC_EN1 0x82U /* ADC Enable 1 */ +#define AXP192_BATT_V_ADC_EN BIT(7) /* reg 0x82 bit 7: battery voltage ADC */ +#define AXP192_REG_BATT_VH 0x78U /* battery voltage ADC high 8 bits [11:4] */ +#define AXP192_REG_BATT_VL 0x79U /* battery voltage ADC low 4 bits [7:4] */ +#define AXP192_REG_GPIO0_FUNC 0x90U /* GPIO0 function control */ +#define AXP192_GPIO0_FUNC_FLOAT 0x06U /* float / high-Z — cuts LED current path */ + +static const struct i2c_dt_spec axp192_i2c = + I2C_DT_SPEC_GET(DT_NODELABEL(axp192_pmic)); + +/* One-time AXP192 init at boot. + * + * 1. Enable battery voltage ADC (off at POR). + * 2. Float GPIO0 to turn off the red power-indicator LED. + * GPIO0 is open-drain; at POR its NMOS is conducting, sinking current + * from the VCC→LED→GPIO0 circuit and keeping the LED on. FLOAT mode + * puts GPIO0 in high-impedance, breaking the current path. */ +static int axp192_batt_adc_init(void) +{ + if (!i2c_is_ready_dt(&axp192_i2c)) { + return -ENODEV; + } + + /* Enable battery voltage ADC */ + int ret = i2c_reg_update_byte_dt(&axp192_i2c, AXP192_REG_ADC_EN1, + AXP192_BATT_V_ADC_EN, AXP192_BATT_V_ADC_EN); + if (ret < 0) { + return ret; + } + + /* Turn off red power-indicator LED (GPIO0 → float) */ + return i2c_reg_write_byte_dt(&axp192_i2c, AXP192_REG_GPIO0_FUNC, + AXP192_GPIO0_FUNC_FLOAT); +} +SYS_INIT(axp192_batt_adc_init, APPLICATION, 91); #endif /* Initialize TX LED GPIO at boot */ @@ -142,6 +184,17 @@ uint16_t ZephyrBoard::getBattMilliVolts() uint16_t mv = (uint16_t)((mult * (int64_t)raw) / 4096); LOG_DBG("Battery: raw=%d multiplier=%lld mv=%u", (int)raw, (long long)mult, mv); return mv; +#elif DT_NODE_EXISTS(DT_NODELABEL(axp192_pmic)) + /* 12-bit ADC: reg 0x78 = bits[11:4], reg 0x79 bits[7:4] = bits[3:0] */ + uint8_t vh = 0, vl = 0; + if (!i2c_is_ready_dt(&axp192_i2c) || + i2c_reg_read_byte_dt(&axp192_i2c, AXP192_REG_BATT_VH, &vh) < 0 || + i2c_reg_read_byte_dt(&axp192_i2c, AXP192_REG_BATT_VL, &vl) < 0) { + return 0; + } + uint16_t raw = ((uint16_t)vh << 4) | (vl >> 4); + /* 1.1 mV per LSB — multiply by 11 then divide by 10 to avoid floats */ + return (uint16_t)((uint32_t)raw * 11U / 10U); #else return 0; #endif diff --git a/zephcore/boards/esp32/ttgo_tbeam/board.conf b/zephcore/boards/esp32/ttgo_tbeam/board.conf new file mode 100644 index 0000000..3521315 --- /dev/null +++ b/zephcore/boards/esp32/ttgo_tbeam/board.conf @@ -0,0 +1,47 @@ +# LilyGo T-Beam v1.x — ESP32 PICO-D4 + SX1276 + AXP192 PMIC +# +# Hardware: +# ESP32 PICO-D4 (4MB flash) +# SX1276 on SPI3 (NSS=18, SCK=5, MISO=19, MOSI=27, RESET=23, DIO0=26, DIO1=33, DIO2=32) +# AXP192 PMIC on I2C0 — LDO2→LoRa 3.3V, LDO3→GPS 3.3V +# NEO-6M GPS on UART1 (TX=12, RX=34) +# Optional SSD1306 OLED on I2C0 (SDA=21, SCL=22) +# +# Include order: prj.conf → zephcore_common.conf → esp32_common.conf → board.conf +# +# DRAM budget note (ESP32 PICO-D4): +# BT blob reserves ~56 KB DRAM. WiFi stack uses ~80 KB BSS. +# Both together overflow by ~55 KB. Use role-specific conf to select: +# boards/esp32/ttgo_tbeam/repeater.conf — WiFi OTA, no BT +# boards/esp32/ttgo_tbeam/companion.conf — BT, no WiFi OTA + +CONFIG_ZEPHCORE_BOARD_NAME="LilyGo T-Beam" +CONFIG_BT_DIS_MODEL_NUMBER_STR="LilyGo T-Beam" + +# Radio: SX1276 via loramac-node backend (not the native SX126x driver) +CONFIG_LORA_MODULE_BACKEND_NATIVE=n +CONFIG_LORA_MODULE_BACKEND_LORAMAC_NODE=y +CONFIG_ZEPHCORE_RADIO_SX127X=y + +# SX1276 PA_BOOST output: 17 dBm is the safe maximum without an external PA +CONFIG_ZEPHCORE_DEFAULT_TX_POWER_DBM=17 + +# RX duty cycle not supported by the loramac-node SX127x driver +CONFIG_ZEPHCORE_LORA_RX_DUTY_CYCLE=n + +# ESP32 PICO-D4 rev 1.0: use DIO flash mode, not QIO. +# bootloader_enable_qio_mode() polls WIP indefinitely on PICO-D4 rev 1.0, +# causing a bootloop after the "Proceeding" chip-revision message. +CONFIG_ESPTOOLPY_FLASHMODE_QIO=n +CONFIG_ESPTOOLPY_FLASHMODE_DIO=y + +# Default: BT enabled for companion app. WiFi OTA disabled (DRAM constraint). +# Override with repeater.conf for WiFi OTA + no BT. +CONFIG_ZEPHCORE_WIFI_OTA=n + +# ESP32 PICO-D4 companion DRAM budget. +# BT blob reserves ~56 KB, leaving 136 KB for data+bss. +# Default 350 contacts × 184 bytes = 65 KB alone — reduce to 32 to fit. +# These are companion-role symbols; silently ignored on repeater builds. +CONFIG_ZEPHCORE_MAX_CONTACTS=32 +CONFIG_ZEPHCORE_MAX_CHANNELS=8 diff --git a/zephcore/boards/esp32/ttgo_tbeam/board.overlay b/zephcore/boards/esp32/ttgo_tbeam/board.overlay new file mode 100644 index 0000000..41a8ac3 --- /dev/null +++ b/zephcore/boards/esp32/ttgo_tbeam/board.overlay @@ -0,0 +1,143 @@ +/* + * ZephCore overlay — LilyGo T-Beam v1.x (ESP32 PICO-D4 + SX1276 + AXP192) + * + * The upstream Zephyr ttgo_tbeam DTS targets a newer T-Beam revision that + * ships with AXP2101. The v1.0/v1.1 hardware uses AXP192. This overlay: + * 1. Removes the upstream AXP2101 node + * 2. Adds AXP192 with correct T-Beam v1.x LDO assignments: + * LDO2 → SX1276 LoRa 3.3V (always-on) + * LDO3 → GPS (NEO-6M) 3.3V + * 3. Repurposes storage_partition as LittleFS + * + * SX1276 pins (from upstream DTS, verified against T-Beam v1.x schematic): + * NSS=18 SCK=5 MISO=19 MOSI=27 RESET=23 DIO0=26 DIO1=33 DIO2=32 + * If your board has RESET on GPIO14 (very early T-Beam v0.7), override + * reset-gpios in a local DTS fragment. + */ + +#include + +/* Remove the upstream AXP2101 node — wrong PMIC for v1.x hardware */ +/delete-node/ &axp2101; + +/ { + aliases { + regulator0 = &axp192_pmic; + /* GPIO4 LED is wired active-low (cathode to GPIO, anode to VCC). + * Upstream DTS has GPIO_ACTIVE_HIGH which inverts the logic and + * keeps the LED on permanently. We fix the polarity here and + * register the LED as the LoRa TX indicator so it flashes on + * each transmit in addition to the 4-second heartbeat pulse. */ + lora-tx-led = &red_led; + }; + + /* + * T-Beam v1.x IO38 button (gpio1 pin 6). + * GPIO38 is input-only on ESP32 PICO-D4 (no internal pull-up). + * T-Beam PCB has 100 K pull-up to 3.3 V; button shorts GPIO38 to GND. + */ + buttons: buttons { + compatible = "gpio-keys"; + button0: button_0 { + label = "IO38 button"; + gpios = <&gpio1 6 GPIO_ACTIVE_LOW>; + zephyr,code = ; + }; + }; + + user_btn_longpress { + compatible = "zephyr,input-longpress"; + input = <&buttons>; + input-codes = ; + short-codes = ; + long-codes = ; + long-delay-ms = <1000>; + }; + + page_btn_multitap { + compatible = "zephcore,input-multi-tap"; + input-codes = ; + tap-codes = ; + tap-delay-ms = <400>; + }; +}; + +/* Fix LED polarity: hardware is active-low, upstream DTS says active-high */ +&red_led { + gpios = <&gpio0 4 GPIO_ACTIVE_LOW>; +}; + +/* + * Flash layout — repurpose storage_partition as LittleFS. + * 192 KB at 0x3B0000: same location and size as the deleted storage_partition. + */ +/delete-node/ &storage_partition; + +&flash0 { + partitions { + lfs_partition: partition@3b0000 { + label = "lfs"; + reg = <0x3B0000 0x30000>; + }; + }; +}; + +#include "../../common/filesystem.dtsi" + +&i2c0 { + /* + * AXP192 PMIC — replaces AXP2101 from upstream DTS. + * T-Beam v1.x LDO assignments: + * DCDC1: ESP32 core VDD 3.3V + * LDO2: SX1276 LoRa VDD 3.3V + * LDO3: GPS VDD 3.3V + */ + axp192_pmic: axp192@34 { + compatible = "x-powers,axp192"; + reg = <0x34>; + status = "okay"; + + axp192_regulator: axp192_regulator { + compatible = "x-powers,axp192-regulator"; + status = "okay"; + + /* DCDC1 → ESP32 core VDD */ + DCDC1 { + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + /* LDO2 → SX1276 VDD — must be on before radio init */ + LDO2 { + regulator-init-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + /* LDO3 → GPS VDD */ + LDO3 { + regulator-init-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + }; + }; + }; + + /* I2C sensors — auto-detected at runtime */ + #include "../../common/sensors-i2c.dtsi" +}; + +/* WiFi — required for observer and wifi_ota roles */ +&wifi { + status = "okay"; +}; + +/* Bluetooth HCI — disabled by default in SoC DTSI, enable for repeater/companion */ +&esp32_bt_hci { + status = "okay"; +};