diff --git a/boards/lilygo-t-beam-supreme.json b/boards/lilygo-t-beam-supreme.json
new file mode 100644
index 00000000..52ce3db9
--- /dev/null
+++ b/boards/lilygo-t-beam-supreme.json
@@ -0,0 +1,51 @@
+{
+ "build": {
+ "arduino":{
+ "ldscript": "esp32s3_out.ld",
+ "partitions": "default.csv",
+ "memory_type": "qio_qspi"
+ },
+ "core": "esp32",
+ "extra_flags": [
+ "-DARDUINO_USB_MODE=1",
+ "-DARDUINO_USB_CDC_ON_BOOT=1",
+ "-DARDUINO_RUNNING_CORE=1",
+ "-DARDUINO_EVENT_RUNNING_CORE=1"
+ ],
+ "f_cpu": "240000000L",
+ "f_flash": "80000000L",
+ "flash_mode": "qio",
+ "hwids": [
+ [
+ "0x303A",
+ "0x1001"
+ ]
+ ],
+ "mcu": "esp32s3",
+ "variant": "esp32s3"
+ },
+ "connectivity": [
+ "wifi"
+ ],
+ "debug": {
+ "default_tool": "esp-builtin",
+ "onboard_tools": [
+ "esp-builtin"
+ ],
+ "openocd_target": "esp32s3.cfg"
+ },
+ "frameworks": [
+ "arduino",
+ "espidf"
+ ],
+ "name": "LilyGo T-Beam supreme (8MB Flash 8MB PSRAM)",
+ "upload": {
+ "flash_size": "8MB",
+ "maximum_ram_size": 327680,
+ "maximum_size": 8388608,
+ "require_upload_port": true,
+ "speed": 460800
+ },
+ "url": "https://www.lilygo.cc/products/t-beamsupreme-m",
+ "vendor": "LilyGo"
+}
diff --git a/doc/LilyGo_T-Beam_Supreme.md b/doc/LilyGo_T-Beam_Supreme.md
new file mode 100644
index 00000000..aea8dcae
--- /dev/null
+++ b/doc/LilyGo_T-Beam_Supreme.md
@@ -0,0 +1,55 @@
+# LilyGo T-Beam Supreme (S3) Setup for TinyGS
+
+The LilyGo T-Beam Supreme is an ESP32-S3 based board with an SX1262 LoRa radio, AXP2101 PMU, and an integrated GPS (Ublox M10 or L76K).
+
+## Pin Mapping (Verified)
+
+| Function | Pin (GPIO) |
+| :--- | :--- |
+| **LoRa CS (NSS)** | 10 |
+| **LoRa DIO1** | 1 |
+| **LoRa BUSY** | 4 |
+| **LoRa RESET** | 5 |
+| **LoRa SCK** | 12 |
+| **LoRa MISO** | 13 |
+| **LoRa MOSI** | 11 |
+| **I2C SDA** | 17 |
+| **I2C SCL** | 18 |
+| **GPS TX** | 8 |
+| **GPS RX** | 9 |
+| **GPS Wakeup** | 7 |
+| **PMU SDA** | 42 (Wire1) |
+| **PMU SCL** | 41 (Wire1) |
+| **User Button** | 0 |
+| **Green LED** | 3 |
+
+## Features
+
+### 1. Power Management (AXP2101)
+- Full support for AXP2101 PMU via Wire1.
+- Correct battery voltage reading (1mV/bit).
+- Accurate fuel gauge percentage.
+- Automatic power control for Radio and GPS.
+
+### 2. Display (SH1106)
+- SH1106 OLED driver (vs SSD1306 on other boards).
+- Auto-off after 5 minutes of inactivity to save power.
+- Wakes on button press or serial input.
+
+### 3. Power Saving
+- **Shared Rail (ALDO1)**: Powers the OLED and onboard sensors (QMC5883L Compass, BME280). This rail remains active even when the display is off.
+- **Radio Rail (ALDO3)**: Powers the SX1262 LoRa module.
+- **GPS Rail (ALDO4)**: Powers the GNSS module independently.
+- **Unused Rails**: Unused PMU rails (ALDO2, BLDO1/2, DLDO1/2) are explicitly disabled to minimize quiescent current.
+
+## Configuration
+
+1. Connect to the TinyGS Access Point.
+2. Navigate to `http://192.168.4.1/config`.
+3. Select **433 Mhz LilyGo T-Beam Supreme SX1262** as the board type.
+4. Save and Restart.
+
+## Troubleshooting
+
+- **No Packets**: Ensure you are using a 433MHz or 868/915MHz antenna matching your board variant. The SX1262 pins are identical for all frequency variants.
+- **Battery 0.00V**: Ensure the board is selected as "Supreme" to enable the AXP2101 driver.
diff --git a/platformio.ini b/platformio.ini
index e8bf1584..bffb5b36 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -75,3 +75,17 @@ build_flags =
${env.build_flags}
-DBOARD_HAS_PSRAM
board_build.partitions = tinygs_4M_partition_table.csv
+
+[env:lilygo-t-beam-supreme]
+platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
+board = lilygo-t-beam-supreme
+board_build.mcu = esp32s3
+framework = arduino
+build_flags =
+ ${env.build_flags}
+ -DARDUINO_USB_CDC_ON_BOOT=1
+ -DBOARD_HAS_PSRAM
+board_build.partitions = tinygs_4M_partition_table.csv
+lib_deps =
+ ${env.lib_deps}
+ mikalhart/TinyGPSPlus @ ^1.0.3
diff --git a/tinyGS/src/ConfigManager/ConfigManager.cpp b/tinyGS/src/ConfigManager/ConfigManager.cpp
index fc3f0286..3df1f575 100644
--- a/tinyGS/src/ConfigManager/ConfigManager.cpp
+++ b/tinyGS/src/ConfigManager/ConfigManager.cpp
@@ -50,39 +50,39 @@ na SX1281 2.4–2.5Ghz 130 5.5 2000 0.476-202
ConfigManager::ConfigManager()
: IotWebConf2(thingName, &dnsServer, &server, initialApPassword, configVersion), server(80), gsConfigHtmlFormatProvider(*this), boards{
- //OLED_add, OLED_SDA, OLED_SCL, OLED_RST, PROG_BUTTON, BOARD_LED, L_SX127X?, L_NSS, L_DI00, L_DI01, L_BUSSY, L_RST, L_MISO, L_MOSI, L_SCK, L_TCXO_V, RX_EN, TX_EN, BOARD
+ //OLED_add, OLED_SDA, OLED_SCL, OLED_RST, PROG_BUTTON, BOARD_LED, L_SX127X?, L_NSS, L_DI00, L_DI01, L_BUSSY, L_RST, L_MISO, L_MOSI, L_SCK, L_TCXO_V, RX_EN, TX_EN, GNSS_RX, GNSS_TX, GNSS_WK, BOARD
#if CONFIG_IDF_TARGET_ESP32S3
- { 0x3c, 17, 18, 21, 0, 35, RADIO_SX1262, 8, UNUSED, 14, 13, 12, 11, 10, 9, 1.6f, UNUSED, UNUSED, "150–960Mhz - HELTEC LORA32 V3 SX1262" }, // SX1262
- { 0x3c, 17, 18, UNUSED, 0, 35, RADIO_SX1278, 8, 6, 14, UNUSED, 12, 11, 10, 9, 0.0f, UNUSED, UNUSED, "Custom ESP32-S3 433MHz SX1278" }, // SX1278 @g4lile0
- { 0x3c, 17, 18, UNUSED, 0, 3, RADIO_SX1262, 10, UNUSED, 1, 4, 5, 13, 11, 12, 1.6f, UNUSED, UNUSED, "433 Mhz TTGO T-Beam Sup SX1262 V1.0" }, // SX1268 @ Stephen
- { 0x3c, 17, 18, UNUSED, 0, 37, RADIO_SX1280, 7, UNUSED, 9, UNUSED, 8, 3, 6, 5, 0.0f, 21, 10, "2.4Ghz LILYGO SX1280" }, // SX1280 @ K4KDR
+ { 0x3c, 17, 18, 21, 0, 35, RADIO_SX1262, 8, UNUSED, 14, 13, 12, 11, 10, 9, 1.6f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "150–960Mhz - HELTEC LORA32 V3 SX1262" }, // SX1262
+ { 0x3c, 17, 18, UNUSED, 0, 35, RADIO_SX1278, 8, 6, 14, UNUSED, 12, 11, 10, 9, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "Custom ESP32-S3 433MHz SX1278" }, // SX1278 @g4lile0
+ { 0x3c, 17, 18, UNUSED, 0, SUPREME_LED, RADIO_SX1262, 10, UNUSED, 1, 4, 5, 13, 11, 12, 1.8f, UNUSED, UNUSED, SUPREME_GNSS_RX, SUPREME_GNSS_TX, SUPREME_GNSS_WAKEUP, "433 Mhz LilyGo T-Beam Supreme SX1262" }, // SX1262 (upgraded from TTGO T-Beam Sup V1.0 @ Stephen; TCXO 1.6->1.8V per Meshtastic/community standard)
+ { 0x3c, 17, 18, UNUSED, 0, 37, RADIO_SX1280, 7, UNUSED, 9, UNUSED, 8, 3, 6, 5, 0.0f, 21, 10, UNUSED, UNUSED, UNUSED, "2.4Ghz LILYGO SX1280" }, // SX1280 @ K4KDR
#elif CONFIG_IDF_TARGET_ESP32C3
- { 0x3c, 0, 1, UNUSED, 20, 21, RADIO_SX1262, 8, UNUSED, 3, 4, 5, 6, 7, 10, 1.6f, UNUSED, UNUSED, "433MHz HELTEC LORA32 HT-CT62 SX1262" }, // SX1262 @gargomoma
- { 0x3c, 0, 1, UNUSED, 20, 21, RADIO_SX1278, 8, 4, UNUSED, UNUSED, 5, 6, 7, 10, 0.0f, UNUSED, UNUSED, "Custom ESP32-C3 433MHz SX1278" }, // SX1278 @gargomoma
+ { 0x3c, 0, 1, UNUSED, 20, 21, RADIO_SX1262, 8, UNUSED, 3, 4, 5, 6, 7, 10, 1.6f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "433MHz HELTEC LORA32 HT-CT62 SX1262" }, // SX1262 @gargomoma
+ { 0x3c, 0, 1, UNUSED, 20, 21, RADIO_SX1278, 8, 4, UNUSED, UNUSED, 5, 6, 7, 10, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "Custom ESP32-C3 433MHz SX1278" }, // SX1278 @gargomoma
#else
- { 0x3c, 4, 15, 16, 0, 25, RADIO_SX1278, 18, 26, 12, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "433MHz HELTEC WiFi LoRA 32 V1" }, // SX1278 @4m1g0
- { 0x3c, 4, 15, 16, 0, 25, RADIO_SX1276, 18, 26, 12, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "863-928MHz HELTEC WiFi LoRA 32 V1" }, // SX1276
- { 0x3c, 4, 15, 16, 0, 25, RADIO_SX1278, 18, 26, 35, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "433MHz HELTEC WiFi LoRA 32 V2" }, // SX1278 @4m1g0
- { 0x3c, 4, 15, 16, 0, 25, RADIO_SX1276, 18, 26, 35, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "863-928MHz HELTEC WiFi LoRA 32 V2" }, // SX1276
- { 0x3c, 4, 15, 16, 0, 2, RADIO_SX1278, 18, 26, UNUSED, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "433Mhz TTGO LoRa 32 v1" }, // SX1278 @g4lile0
- { 0x3c, 4, 15, 16, 0, 2, RADIO_SX1276, 18, 26, UNUSED, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "868-915MHz TTGO LoRa 32 v1" }, // SX1276
- { 0x3c, 21, 22, UNUSED, 0, 22, RADIO_SX1278, 18, 26, 33, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "433MHz TTGO LoRA 32 v2" }, // SX1278 @TCRobotics
- { 0x3c, 21, 22, 16, 0, 22, RADIO_SX1276, 18, 26, 33, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "868-915MHz TTGO LoRA 32 v2" }, // SX1276
- { 0x3c, 21, 22, 16, 39, 22, RADIO_SX1278, 18, 26, 33, 32, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "433MHz T-BEAM + OLED" }, // SX1278
- { 0x3c, 21, 22, 16, 39, 22, RADIO_SX1276, 18, 26, 33, 32, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "868-915MHz T-BEAM + OLED" }, // SX1276
- { 0x3c, 21, 22, 16, 0, 25, RADIO_SX1268, 5, UNUSED, 27, 26, 14, 19, 23, 18, 0.0f, UNUSED, UNUSED, "Custom ESP32 Wroom + SX126x (Crystal)" }, // SX1268 @4m1g0, @lillefyr
- { 0x3c, 21, 22, UNUSED, 0, 25, RADIO_SX1268, 18, UNUSED, 33, 32, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "TTGO LoRa 32 V2 Modified with module SX126x (crystal)" }, // SX1268 @TCRobotics
- { 0x3c, 21, 22, 16, 0, 25, RADIO_SX1268, 5, UNUSED, 2, 13, 26, 19, 23, 18, 1.6f, UNUSED, UNUSED, "Custom ESP32 Wroom + SX126x DRF1268T (TCX0) (5, 2, 26, 13)" }, // SX1268 @sdey76
- { 0x3c, 21, 22, 16, 0, 25, RADIO_SX1268, 5, UNUSED, 26, 12, 14, 19, 23, 18, 1.6f, UNUSED, UNUSED, "Custom ESP32 Wroom + SX126x DRF1268T (TCX0) (5, 26, 14, 12)" }, // SX1268 @imants
- { 0x3c, 21, 22, UNUSED, 38, 22, RADIO_SX1278, 18, 26, 33, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "433MHz T-BEAM V1.0 + OLED" }, // SX1278 @fafu
- { 0x3c, 21, 22, 16, 0, 2, RADIO_SX1268, 5, UNUSED, 34, 32, 14, 19, 27, 18, 1.6f, UNUSED, UNUSED, "433MHz FOSSA 1W Ground Station" }, // SX1268 @jgromes
- { 0x3c, 21, 22, 16, 0, 2, RADIO_SX1276, 5, UNUSED, 34, 32, 14, 19, 27, 18, 1.6f, UNUSED, UNUSED, "868-915MHz FOSSA 1W Ground Station" }, //SX1276 @jgromes
- { 0x3c, 21, 22, UNUSED, 0, 22, RADIO_SX1280, 5, 26, 34, 32, 14, 19, 27, 18, 0.0f, UNUSED, UNUSED, "2.4GHz ESP32 + SX1280" }, //SX1280 @g4lile0
- { 0x3c, 21, 22, UNUSED, 38, 22, RADIO_SX1276, 18, 26, 33, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, "868-915MHz T-BEAM V1.0 + OLED" }, // SX1276 @fafu
- { 0x3c, 21, 22, UNUSED, 0, 25, RADIO_SX1278, 18, 26, 33, UNUSED, 23, 19, 27, 5, 0.0f, UNUSED, UNUSED, "433MHz LILYGO T3_V1.6.1" }, // SX1278
- { 0x3c, 21, 22, UNUSED, 0, 25, RADIO_SX1276, 18, 26, 33, UNUSED, 23, 19, 27, 5, 0.0f, UNUSED, UNUSED, "868-915MHz LILYGO T3_V1.6.1" }, // SX1276
- { 0x3c, 21, 22, UNUSED, 0, 25, RADIO_SX1276, 18, 26, UNUSED, 32, 23, 19, 27, 5, 0.0f, UNUSED, UNUSED, "868-915MHz LILYGO T3_V1.6.1 TCXO" }, // SX1276
- { 0x3c, 21, 22, UNUSED, 38, 4, RADIO_SX1268, 18, 26, 33, 32, 23, 19, 27, 5, 1.6f, UNUSED, UNUSED, "433 Mhz T-Beam SX1268 V1.0" }, // SX1268 @ Antonio
+ { 0x3c, 4, 15, 16, 0, 25, RADIO_SX1278, 18, 26, 12, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "433MHz HELTEC WiFi LoRA 32 V1" }, // SX1278 @4m1g0
+ { 0x3c, 4, 15, 16, 0, 25, RADIO_SX1276, 18, 26, 12, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "863-928MHz HELTEC WiFi LoRA 32 V1" }, // SX1276
+ { 0x3c, 4, 15, 16, 0, 25, RADIO_SX1278, 18, 26, 35, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "433MHz HELTEC WiFi LoRA 32 V2" }, // SX1278 @4m1g0
+ { 0x3c, 4, 15, 16, 0, 25, RADIO_SX1276, 18, 26, 35, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "863-928MHz HELTEC WiFi LoRA 32 V2" }, // SX1276
+ { 0x3c, 4, 15, 16, 0, 2, RADIO_SX1278, 18, 26, UNUSED, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "433Mhz TTGO LoRa 32 v1" }, // SX1278 @g4lile0
+ { 0x3c, 4, 15, 16, 0, 2, RADIO_SX1276, 18, 26, UNUSED, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "868-915MHz TTGO LoRa 32 v1" }, // SX1276
+ { 0x3c, 21, 22, UNUSED, 0, 22, RADIO_SX1278, 18, 26, 33, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "433MHz TTGO LoRA 32 v2" }, // SX1278 @TCRobotics
+ { 0x3c, 21, 22, 16, 0, 22, RADIO_SX1276, 18, 26, 33, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "868-915MHz TTGO LoRA 32 v2" }, // SX1276
+ { 0x3c, 21, 22, 16, 39, 22, RADIO_SX1278, 18, 26, 33, 32, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "433MHz T-BEAM + OLED" }, // SX1278
+ { 0x3c, 21, 22, 16, 39, 22, RADIO_SX1276, 18, 26, 33, 32, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "868-915MHz T-BEAM + OLED" }, // SX1276
+ { 0x3c, 21, 22, 16, 0, 25, RADIO_SX1268, 5, UNUSED, 27, 26, 14, 19, 23, 18, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "Custom ESP32 Wroom + SX126x (Crystal)" }, // SX1268 @4m1g0, @lillefyr
+ { 0x3c, 21, 22, UNUSED, 0, 25, RADIO_SX1268, 18, UNUSED, 33, 32, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "TTGO LoRa 32 V2 Modified with module SX126x (crystal)" }, // SX1268 @TCRobotics
+ { 0x3c, 21, 22, 16, 0, 25, RADIO_SX1268, 5, UNUSED, 2, 13, 26, 19, 23, 18, 1.6f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "Custom ESP32 Wroom + SX126x DRF1268T (TCX0) (5, 2, 26, 13)" }, // SX1268 @sdey76
+ { 0x3c, 21, 22, 16, 0, 25, RADIO_SX1268, 5, UNUSED, 26, 12, 14, 19, 23, 18, 1.6f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "Custom ESP32 Wroom + SX126x DRF1268T (TCX0) (5, 26, 14, 12)" }, // SX1268 @imants
+ { 0x3c, 21, 22, UNUSED, 38, 22, RADIO_SX1278, 18, 26, 33, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "433MHz T-BEAM V1.0 + OLED" }, // SX1278 @fafu
+ { 0x3c, 21, 22, 16, 0, 2, RADIO_SX1268, 5, UNUSED, 34, 32, 14, 19, 27, 18, 1.6f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "433MHz FOSSA 1W Ground Station" }, // SX1268 @jgromes
+ { 0x3c, 21, 22, 16, 0, 2, RADIO_SX1276, 5, UNUSED, 34, 32, 14, 19, 27, 18, 1.6f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "868-915MHz FOSSA 1W Ground Station" }, //SX1276 @jgromes
+ { 0x3c, 21, 22, UNUSED, 0, 22, RADIO_SX1280, 5, 26, 34, 32, 14, 19, 27, 18, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "2.4GHz ESP32 + SX1280" }, //SX1280 @g4lile0
+ { 0x3c, 21, 22, UNUSED, 38, 22, RADIO_SX1276, 18, 26, 33, UNUSED, 14, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "868-915MHz T-BEAM V1.0 + OLED" }, // SX1276 @fafu
+ { 0x3c, 21, 22, UNUSED, 0, 25, RADIO_SX1278, 18, 26, 33, UNUSED, 23, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "433MHz LILYGO T3_V1.6.1" }, // SX1278
+ { 0x3c, 21, 22, UNUSED, 0, 25, RADIO_SX1276, 18, 26, 33, UNUSED, 23, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "868-915MHz LILYGO T3_V1.6.1" }, // SX1276
+ { 0x3c, 21, 22, UNUSED, 0, 25, RADIO_SX1276, 18, 26, UNUSED, 32, 23, 19, 27, 5, 0.0f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "868-915MHz LILYGO T3_V1.6.1 TCXO" }, // SX1276
+ { 0x3c, 21, 22, UNUSED, 38, 4, RADIO_SX1268, 18, 26, 33, 32, 23, 19, 27, 5, 1.6f, UNUSED, UNUSED, UNUSED, UNUSED, UNUSED, "433 Mhz T-Beam SX1268 V1.0" }, // SX1268 @ Antonio
#endif
@@ -667,10 +667,14 @@ boolean ConfigManager::init()
// we fall back to AP mode during 2 minutes after which we try to connect again and repeat.
setApTimeoutMs(atoi(AP_TIMEOUT_MS));
- // no board selected
- if (!strcmp(board, ""))
+ // no board selected, or stored index out of range (e.g. after enum consolidation)
{
- boardDetection();
+ int boardIdx = atoi(board);
+ if (!strcmp(board, "") || boardIdx < 0 || boardIdx >= (int)(sizeof(boards) / sizeof(boards[0]))) {
+ if (board[0] != '\0')
+ Log::error(PSTR("Stored board index %d out of range, re-detecting"), boardIdx);
+ boardDetection();
+ }
}
if (strlen(advancedConfig))
@@ -716,7 +720,28 @@ void ConfigManager::boardDetection()
// https://github.com/mpmarks/tinyGS-newboards/commit/e520086f1b43c7cea4cb85d996f0fc379f2d2786
#if CONFIG_IDF_TARGET_ESP32S3
-// nothing yet
+ // Probe for AXP2101 on Wire1 (Supreme PMU bus) to detect T-Beam Supreme
+ Wire1.begin(SUPREME_PMU_SDA, SUPREME_PMU_SCL);
+ Wire1.beginTransmission(0x34);
+ if (!Wire1.endTransmission()) {
+ Log::error(PSTR("AXP PMU found on Wire1 -- T-Beam Supreme detected"));
+ itoa(TTGO_TBEAM_SX1262, board, 10);
+ Wire1.end();
+ return;
+ }
+ Wire1.end();
+
+ // Scan S3 boards for OLED presence
+ for (uint8_t ite = 0; ite < ((sizeof(boards) / sizeof(boards[0]))); ite++) {
+ Wire.begin(boards[ite].OLED__SDA, boards[ite].OLED__SCL);
+ Wire.beginTransmission(boards[ite].OLED__address);
+ if (!Wire.endTransmission()) {
+ Log::error(PSTR("Compatible OLED FOUND for board %u"), ite);
+ itoa(ite, board, 10);
+ return;
+ }
+ }
+ Log::error(PSTR("No compatible S3 board found, please select it manually"));
#elif CONFIG_IDF_TARGET_ESP32C3
// nothing yet
#else
diff --git a/tinyGS/src/ConfigManager/ConfigManager.h b/tinyGS/src/ConfigManager/ConfigManager.h
index b56dfb1a..774eebe4 100644
--- a/tinyGS/src/ConfigManager/ConfigManager.h
+++ b/tinyGS/src/ConfigManager/ConfigManager.h
@@ -69,6 +69,14 @@ constexpr auto configVersion = "0.05"; //max 4 chars
#define MQTT_DEFAULT_SERVER "mqtt.tinygs.com"
#define MQTT_DEFAULT_PORT "8883"
+// T-Beam Supreme specific pins (Wire1 for PMU, separate from OLED Wire bus)
+#define SUPREME_GNSS_RX 9
+#define SUPREME_GNSS_TX 8
+#define SUPREME_GNSS_WAKEUP 7
+#define SUPREME_PMU_SDA 42
+#define SUPREME_PMU_SCL 41
+#define SUPREME_LED 3
+
constexpr auto AP_TIMEOUT_MS = "300000";
enum boardNum
@@ -129,15 +137,18 @@ struct board_t
float L_TCXO_V;
uint8_t RX_EN;
uint8_t TX_EN;
+ uint8_t GNSS_RX;
+ uint8_t GNSS_TX;
+ uint8_t GNSS_WAKEUP;
String BOARD;
board_t() = default;
board_t(uint8_t oled_addr, uint8_t oled_sda, uint8_t oled_scl, uint8_t oled_rst, uint8_t prog_btn, uint8_t board_led,
uint8_t l_radio, uint8_t l_nss, uint8_t l_di00, uint8_t l_di01, uint8_t l_bussy, uint8_t l_rst, uint8_t l_miso, uint8_t l_mosi, uint8_t l_sck,
- float l_tcxo_v, uint8_t rx_en, uint8_t tx_en, String board_name)
+ float l_tcxo_v, uint8_t rx_en, uint8_t tx_en, uint8_t gnss_rx, uint8_t gnss_tx, uint8_t gnss_wakeup, String board_name)
: OLED__address(oled_addr), OLED__SDA(oled_sda), OLED__SCL(oled_scl), OLED__RST(oled_rst), PROG__BUTTON(prog_btn), BOARD_LED(board_led),
L_radio(l_radio), L_NSS(l_nss), L_DI00(l_di00), L_DI01(l_di01), L_BUSSY(l_bussy), L_RST(l_rst), L_MISO(l_miso), L_MOSI(l_mosi), L_SCK(l_sck),
- L_TCXO_V(l_tcxo_v), RX_EN(rx_en), TX_EN(tx_en), BOARD(board_name) {}
+ L_TCXO_V(l_tcxo_v), RX_EN(rx_en), TX_EN(tx_en), GNSS_RX(gnss_rx), GNSS_TX(gnss_tx), GNSS_WAKEUP(gnss_wakeup), BOARD(board_name) {}
};
const uint8_t UNUSED = -1;
@@ -169,7 +180,7 @@ class ConfigManager : public IotWebConf2
const char *getMqttPass() { return mqttPass; }
float getLatitude() { return atof(latitude); }
float getLongitude() { return atof(longitude); }
- const char *getTZ() { return tz + 3; } // +3 removes the first 3 digits used for time zone deduplication
+ const char *getTZ() { return strlen(tz) > 3 ? tz + 3 : "GMT0"; } // +3 removes the first 3 digits used for time zone deduplication
uint8_t getBoard() { return atoi(board); }
uint8_t getOledBright() { return atoi(oledBright); }
bool getAllowTx() { return !strcmp(allowTx, CB_SELECTED_STR); }
diff --git a/tinyGS/src/ConfigManager/html.h b/tinyGS/src/ConfigManager/html.h
index 560b08c5..d281a32e 100644
--- a/tinyGS/src/ConfigManager/html.h
+++ b/tinyGS/src/ConfigManager/html.h
@@ -29,7 +29,7 @@ const char BOARD_NAMES[][BOARD_NAME_LENGTH] PROGMEM =
#if CONFIG_IDF_TARGET_ESP32S3
"433MHz HELTEC LORA32 V3",
"Custom ESP32-S3 433MHz SX1278",
- "433 Mhz TTGO T-Beam Sup SX1262 V1.0",
+ "433 Mhz LilyGo T-Beam Supreme SX1262",
"2.4Ghz LILYGO SX1280",
#elif CONFIG_IDF_TARGET_ESP32C3
"433MHz HELTEC LORA32 HT-CT62 SX1262",
@@ -82,5 +82,5 @@ const char ADVANCED_CONFIG_SCRIPT[] PROGMEM =
"function tableDoneHandler(btn){var tbd=document.getElementById('current-table'); var ds=tableDictString(tbd); current_ctrl.value=ds; document.getElementById('dt-' + current_id).remove(); current_ctrl=null; current_id=null; }"
"function editElementDict(ed){if (current_ctrl===null){var ph=ed.getAttribute('placeholder'); var dstring = ed.value!='' ? ed.value : ph; if(dstring !== ''){ current_id=ed.id; var dict = JSON.parse(dstring); var tblhtml = '
' + dictTable(dict) + '
'; ed.insertAdjacentHTML('afterend', tblhtml); current_ctrl=ed; } } }"
"var current_id, current_ctrl=null; window.addEventListener('load', function() {setup_click('board_template'); setup_click('modem_startup');});";
-const char IOTWEBCONF_WORLDMAP_SCRIPT[] PROGMEM ="var wmx=null,wmt;function wmf(p){var sp,mc,gs,lp;clearTimeout(wmt);wmx=new XMLHttpRequest();wmx.onreadystatechange=function() {if(wmx.readyState==4&&x.status==200){var wma=wmx.responseText;var wmp = wma.split(',');sp=document.getElementById('wmsatpos');sp.setAttribute('cx', wmp[0]);sp.setAttribute('cy', wmp[1]);mc=document.getElementById('modemconfig');for(let r=0;r<6;r++){mc.rows[r].cells[1].innerHTML=wmp[r+2]};if(wmp[2]=='LoRa'){mc.rows[3].cells[0].innerHTML='Spreading Factor ';mc.rows[4].cells[0].innerHTML='Coding Rate ';}else{mc.rows[3].cells[0].innerHTML='Bitrate ';mc.rows[4].cells[0].innerHTML='Frequency dev ';};gs=document.getElementById('gsstatus');for(let r=0;r<6;r++){gs.rows[r].cells[1].innerHTML=wmp[r+8];};sd=document.getElementById('satdata');for(let r=0;r<6;r++){sd.rows[r].cells[1].innerHTML=wmp[r+14];};lp=document.getElementById('lastpacket');for(let r=0;r<4;r++){lp.rows[r].cells[1].innerHTML=wmp[r+20];};lp.rows[4].cells[0].innerHTML=wmp[24];}};wmx.open('GET','wm',true);wmx.send();wmt=setTimeout(wmf,5000);return false;}window.addEventListener('load', wmf);";
+const char IOTWEBCONF_WORLDMAP_SCRIPT[] PROGMEM ="var wmx=null,wmt;function wmf(p){var sp,mc,gs,lp;clearTimeout(wmt);wmx=new XMLHttpRequest();wmx.onreadystatechange=function() {if(wmx.readyState==4&&wmx.status==200){var wma=wmx.responseText;var wmp = wma.split(',');sp=document.getElementById('wmsatpos');sp.setAttribute('cx', wmp[0]);sp.setAttribute('cy', wmp[1]);mc=document.getElementById('modemconfig');for(let r=0;r<6;r++){mc.rows[r].cells[1].innerHTML=wmp[r+2]};if(wmp[2]=='LoRa'){mc.rows[3].cells[0].innerHTML='Spreading Factor ';mc.rows[4].cells[0].innerHTML='Coding Rate ';}else{mc.rows[3].cells[0].innerHTML='Bitrate ';mc.rows[4].cells[0].innerHTML='Frequency dev ';};gs=document.getElementById('gsstatus');for(let r=0;r<6;r++){gs.rows[r].cells[1].innerHTML=wmp[r+8];};sd=document.getElementById('satdata');for(let r=0;r<6;r++){sd.rows[r].cells[1].innerHTML=wmp[r+14];};lp=document.getElementById('lastpacket');for(let r=0;r<4;r++){lp.rows[r].cells[1].innerHTML=wmp[r+20];};lp.rows[4].cells[0].innerHTML=wmp[24];}};wmx.open('GET','wm',true);wmx.send();wmt=setTimeout(wmf,5000);return false;}window.addEventListener('load', wmf);";
const char IOTWEBCONF_CONFIG_STYLE_INNER[] PROGMEM = " fieldset[id='Board config'] div:nth-of-type(3) ~ div { display:none}";
\ No newline at end of file
diff --git a/tinyGS/src/Display/Display.cpp b/tinyGS/src/Display/Display.cpp
index c4ffbedb..46dd0397 100644
--- a/tinyGS/src/Display/Display.cpp
+++ b/tinyGS/src/Display/Display.cpp
@@ -23,9 +23,13 @@
#include "../Mqtt/MQTT_credentials.h"
#include "../Logger/Logger.h"
-SSD1306* display;
+OLEDDisplay* display;
OLEDDisplayUi* ui = NULL;
+#define DISPLAY_TIMEOUT 300000 // 5 minutes
+static unsigned long lastDisplayActivity = 0;
+static bool displayTimedOut = false;
+
void msOverlay(OLEDDisplay *display, OLEDDisplayUiState* state);
void drawFrame1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
void drawFrame2(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
@@ -55,8 +59,18 @@ void displayInit()
if (!ConfigManager::getInstance().getBoardConfig(board))
return;
- display = new SSD1306(board.OLED__address, board.OLED__SDA, board.OLED__SCL);
+ uint8_t boardIdx = ConfigManager::getInstance().getBoard();
+#if CONFIG_IDF_TARGET_ESP32S3
+ if (boardIdx == TTGO_TBEAM_SX1262) {
+ Log::console(PSTR("Display: Initializing SH1106 for T-Beam Supreme"));
+ display = new SH1106Wire(board.OLED__address, board.OLED__SDA, board.OLED__SCL);
+ } else
+#endif
+ {
+ display = new SSD1306Wire(board.OLED__address, board.OLED__SDA, board.OLED__SCL);
+ }
+ lastDisplayActivity = millis();
ui = new OLEDDisplayUi(display);
ui->setTargetFPS(60);
ui->setActiveSymbol(activeSymbol);
@@ -355,6 +369,15 @@ void displayUpdate()
// Get the current OLED brightness from configuration
uint8_t oledBright = ConfigManager::getInstance().getOledBright();
+ // Check display timeout
+ if (oledBright && !displayTimedOut && (millis() - lastDisplayActivity > DISPLAY_TIMEOUT)) {
+ displayTimedOut = true;
+ display->displayOff();
+ return;
+ }
+
+ if (displayTimedOut) return;
+
// Check if brightness has changed
if (oldOledBright != oledBright) {
if (oledBright) {
@@ -374,6 +397,16 @@ void displayUpdate()
}
}
+void displayResetTimeout()
+{
+ lastDisplayActivity = millis();
+ if (displayTimedOut) {
+ displayTimedOut = false;
+ display->displayOn();
+ oldOledBright = -1; // force brightness reapply
+ }
+}
+
void displayTurnOff()
{
display->displayOff();
diff --git a/tinyGS/src/Display/Display.h b/tinyGS/src/Display/Display.h
index a6dbe88f..619606fd 100644
--- a/tinyGS/src/Display/Display.h
+++ b/tinyGS/src/Display/Display.h
@@ -17,8 +17,10 @@
along with this program. If not, see .
*/
-#include "SSD1306.h" // https://github.com/ThingPulse/esp8266-oled-ssd1306
-#include "OLEDDisplayUi.h" // https://github.com/ThingPulse/esp8266-oled-ssd1306
+#include "OLEDDisplay.h" // https://github.com/ThingPulse/esp8266-oled-ssd1306
+#include "SSD1306Wire.h"
+#include "SH1106Wire.h"
+#include "OLEDDisplayUi.h"
#include "../ConfigManager/ConfigManager.h"
#include "../Status.h"
@@ -30,7 +32,9 @@ void displayShowStaMode(bool ap);
void displayUpdate();
void displayTurnOff();
void displayNextFrame();
+void displayResetTimeout();
+extern OLEDDisplay* display;
extern Status status;
diff --git a/tinyGS/src/Mqtt/MQTT_Client.cpp b/tinyGS/src/Mqtt/MQTT_Client.cpp
index 51a9d318..d3b4106b 100644
--- a/tinyGS/src/Mqtt/MQTT_Client.cpp
+++ b/tinyGS/src/Mqtt/MQTT_Client.cpp
@@ -26,6 +26,7 @@
#include "../Radio/Radio.h"
#include "../OTA/OTA.h"
#include "../Logger/Logger.h"
+#include "../Power/Power.h"
#include
@@ -108,7 +109,7 @@ void MQTT_Client::loop()
else
{
StaticJsonDocument<192> doc;
- doc["Vbat"] = voltage();
+ doc["Vbat"] = Power::getInstance().getBatteryVoltage();
doc["Mem"] = ESP.getFreeHeap();
doc["MinMem"] = ESP.getMinFreeHeap(); // Mínimo histórico
doc["MaxBlk"] = ESP.getMaxAllocHeap(); // Bloque más grande disponible
@@ -249,7 +250,7 @@ void MQTT_Client::sendWelcome()
doc["board"] = configManager.getBoard();
doc["mac"] = clientId;
doc["seconds"] = millis()/1000;
- doc["Vbat"] = voltage();
+ doc["Vbat"] = Power::getInstance().getBatteryVoltage();
doc["chip"] = ESP.getChipModel();
doc["slot"] = esp_ota_get_running_partition ()->label;
doc["pSize"] = esp_ota_get_running_partition ()->size;
@@ -1185,34 +1186,4 @@ void MQTT_Client::begin()
-int MQTT_Client::voltage() {
- int medianVoltage;
- int length = 21;
- int voltages[22];
-
- for (int i = 0; i < 21; i++)
- {
- voltages[i] = analogRead(36);
- }
-
- // BubbleSortAsc from https://www.luisllamas.es/arduino-bubble-sort/
- int i, j, flag = 1;
- int temp;
- for (i = 1; (i <= length) && flag; i++)
- {
- flag = 0;
- for (j = 0; j < (length - 1); j++)
- {
- if (voltages[j + 1] < voltages[j])
- {
- temp = voltages[j];
- voltages[j] = voltages[j + 1];
- voltages[j + 1] = temp;
- flag = 1;
- }
- }
- }
- medianVoltage = voltages[10];
- return medianVoltage;
-}
diff --git a/tinyGS/src/Mqtt/MQTT_Client.h b/tinyGS/src/Mqtt/MQTT_Client.h
index 4aed4b8a..4eddefeb 100644
--- a/tinyGS/src/Mqtt/MQTT_Client.h
+++ b/tinyGS/src/Mqtt/MQTT_Client.h
@@ -112,8 +112,6 @@ class MQTT_Client : public PubSubClient {
void processRxQueue();
void sendRxFromQueue(const RxPacketMessage& msg); // Envía paquete desde la cola
- int voltage();
-
//bool usingNewCert = true;
SemaphoreHandle_t radioConfigMutex;
QueueHandle_t rxQueue;
diff --git a/tinyGS/src/Power/Power.cpp b/tinyGS/src/Power/Power.cpp
index 9d783ce1..9669d99f 100644
--- a/tinyGS/src/Power/Power.cpp
+++ b/tinyGS/src/Power/Power.cpp
@@ -31,47 +31,61 @@ byte irqstat0;
byte irqstat1;
byte irqstat2;
+Power::Power() : pmuWire(&Wire) {}
void Power::I2CwriteByte(uint8_t Address, uint8_t Register, uint8_t Data)
{
- Wire.beginTransmission(Address);
- Wire.write(Register);
- Wire.write(Data);
- Wire.endTransmission();
+ pmuWire->beginTransmission(Address);
+ pmuWire->write(Register);
+ pmuWire->write(Data);
+ pmuWire->endTransmission();
}
uint8_t Power::I2CreadByte(uint8_t Address, uint8_t Register)
{
uint8_t Nbytes = 1;
- Wire.beginTransmission(Address);
- Wire.write(Register);
- Wire.endTransmission();
- Wire.requestFrom(Address, Nbytes);
- byte slaveByte = Wire.read();
- Wire.endTransmission();
+ pmuWire->beginTransmission(Address);
+ pmuWire->write(Register);
+ pmuWire->endTransmission();
+ pmuWire->requestFrom(Address, Nbytes);
+ byte slaveByte = pmuWire->read();
+ pmuWire->endTransmission();
return slaveByte;
}
void Power::I2Cread(uint8_t Address, uint8_t Register, uint8_t Nbytes, uint8_t* Data)
{
- Wire.beginTransmission(Address);
- Wire.write(Register);
- Wire.endTransmission();
- Wire.requestFrom(Address, Nbytes);
+ pmuWire->beginTransmission(Address);
+ pmuWire->write(Register);
+ pmuWire->endTransmission();
+ pmuWire->requestFrom(Address, Nbytes);
uint8_t index = 0;
- while (Wire.available())
- Data[index++] = Wire.read();
+ while (pmuWire->available())
+ Data[index++] = pmuWire->read();
}
void Power::checkAXP()
{
board_t board;
+ uint8_t boardIdx = ConfigManager::getInstance().getBoard();
if (!ConfigManager::getInstance().getBoardConfig(board))
return;
Log::console(PSTR("AXPxxx chip?"));
byte regV = 0;
- Wire.begin(board.OLED__SDA, board.OLED__SCL); // I2C_SDA, I2C_SCL on all new boards
+
+#if CONFIG_IDF_TARGET_ESP32S3
+ if (boardIdx == TTGO_TBEAM_SX1262) {
+ Log::console(PSTR("PMU: Using Wire1 on pins %d/%d for Supreme"), SUPREME_PMU_SDA, SUPREME_PMU_SCL);
+ Wire1.begin(SUPREME_PMU_SDA, SUPREME_PMU_SCL);
+ pmuWire = &Wire1;
+ } else
+#endif
+ {
+ Wire.begin(board.OLED__SDA, board.OLED__SCL); // I2C_SDA, I2C_SCL on all new boards
+ pmuWire = &Wire;
+ }
+
byte ChipID = I2CreadByte(0x34, 0x03); // read byte from xxx_IC_TYPE register
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
if (ChipID == XPOWERS_AXP192_CHIP_ID) { // 0x03
@@ -97,17 +111,44 @@ void Power::checkAXP()
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
if (ChipID == XPOWERS_AXP2101_CHIP_ID) {// 0x4A
AXPchip = 2;
- Log::console(PSTR("AXP2101 found")); // T-Beam V1.2 with AXP2101 power controller
- I2CwriteByte(0x34, 0x93, 0x1C); // set ALDO2 voltage to 3.3V ( LoRa VCC )
- I2CwriteByte(0x34, 0x94, 0x1C); // set ALDO3 voltage to 3.3V ( GPS VDD )
- I2CwriteByte(0x34, 0x6A, 0x04); // set Button battery voltage to 3.0V ( backup battery )
- I2CwriteByte(0x34, 0x64, 0x03); // set Main battery voltage to 4.2V ( 18650 battery )
- I2CwriteByte(0x34, 0x61, 0x05); // set Main battery precharge current to 125mA
- I2CwriteByte(0x34, 0x62, 0x0A); // set Main battery charger current to 400mA ( 0x08-200mA, 0x09-300mA, 0x0A-400mA )
- I2CwriteByte(0x34, 0x63, 0x15); // set Main battery term charge current to 125mA
- regV = I2CreadByte(0x34, 0x90); // XPOWERS_AXP2101_LDO_ONOFF_CTRL0
- regV = regV | 0x06; // set bit 1 (ALDO2) and bit 2 (ALDO3)
- I2CwriteByte(0x34, 0x90, regV); // and power channels now enabled
+ Log::console(PSTR("AXP2101 found")); // T-Beam V1.2 or Supreme with AXP2101 power controller
+
+#if CONFIG_IDF_TARGET_ESP32S3
+ if (boardIdx == TTGO_TBEAM_SX1262) {
+ // T-Beam Supreme: ALDO1 (display/sensors), ALDO3 (radio), ALDO4 (GPS)
+ Log::console(PSTR("Configuring AXP2101 for T-Beam Supreme"));
+ I2CwriteByte(0x34, 0x92, 0x1C); // set ALDO1 voltage to 3.3V (Display + sensors)
+ I2CwriteByte(0x34, 0x94, 0x1C); // set ALDO3 voltage to 3.3V (Radio)
+ I2CwriteByte(0x34, 0x95, 0x1C); // set ALDO4 voltage to 3.3V (GPS)
+ I2CwriteByte(0x34, 0x6A, 0x04); // set Button battery voltage to 3.0V
+ I2CwriteByte(0x34, 0x64, 0x03); // set Main battery voltage to 4.2V
+ I2CwriteByte(0x34, 0x61, 0x05); // set Main battery precharge current to 125mA
+ I2CwriteByte(0x34, 0x62, 0x0A); // set Main battery charger current to 400mA
+ I2CwriteByte(0x34, 0x63, 0x15); // set Main battery term charge current to 125mA
+
+ // Disable unused rails to minimize quiescent current
+ I2CwriteByte(0x34, 0x91, 0x00); // Disable BLDO1, BLDO2, DLDO1, DLDO2
+
+ regV = I2CreadByte(0x34, AXP2101_LDO_ONOFF_CTRL0);
+ regV &= ~(1 << AXP2101_ALDO2_BIT); // Disable ALDO2 (unused on Supreme)
+ regV = regV | (1 << AXP2101_ALDO1_BIT) | (1 << AXP2101_ALDO3_BIT) | (1 << AXP2101_ALDO4_BIT);
+ I2CwriteByte(0x34, AXP2101_LDO_ONOFF_CTRL0, regV);
+ } else
+#endif
+ {
+ // T-Beam V1.2: ALDO2 (radio), ALDO3 (GPS)
+ I2CwriteByte(0x34, 0x93, 0x1C); // set ALDO2 voltage to 3.3V (LoRa VCC)
+ I2CwriteByte(0x34, 0x94, 0x1C); // set ALDO3 voltage to 3.3V (GPS VDD)
+ I2CwriteByte(0x34, 0x6A, 0x04); // set Button battery voltage to 3.0V
+ I2CwriteByte(0x34, 0x64, 0x03); // set Main battery voltage to 4.2V
+ I2CwriteByte(0x34, 0x61, 0x05); // set Main battery precharge current to 125mA
+ I2CwriteByte(0x34, 0x62, 0x0A); // set Main battery charger current to 400mA
+ I2CwriteByte(0x34, 0x63, 0x15); // set Main battery term charge current to 125mA
+ regV = I2CreadByte(0x34, AXP2101_LDO_ONOFF_CTRL0);
+ regV = regV | (1 << AXP2101_ALDO2_BIT) | (1 << AXP2101_ALDO3_BIT);
+ I2CwriteByte(0x34, AXP2101_LDO_ONOFF_CTRL0, regV);
+ }
+
regV = I2CreadByte(0x34, 0x18); // XPOWERS_AXP2101_CHARGE_GAUGE_WDT_CTRL
regV = regV | 0x06; // set bit 1 (Main Battery) and bit 2 (Button battery)
I2CwriteByte(0x34, 0x18, regV); // and chargers now enabled
@@ -117,14 +158,115 @@ void Power::checkAXP()
I2CwriteByte(0x34, 0x50, 0x14); // set TS pin to EXTERNAL input (not temperature)
I2CwriteByte(0x34, 0x69, 0x01); // set CHGLED for 'type A' and enable pin function
I2CwriteByte(0x34, 0x27, 0x00); // set IRQLevel/OFFLevel/ONLevel to minimum (1S/4S/128mS)
- I2CwriteByte(0x34, 0x30, 0x0F); // enable ADC for SYS, VBUS, TS and Battery
+ I2CwriteByte(0x34, 0x30, 0xFF); // enable all ADC channels
pmustat1 = I2CreadByte(0x34, 0x00); pmustat2 = I2CreadByte(0x34, 0x01);
pwronsta = I2CreadByte(0x34, 0x20); pwrofsta = I2CreadByte(0x34, 0x21);
irqstat0 = I2CreadByte(0x34, 0x48); irqstat1 = I2CreadByte(0x34, 0x49); irqstat2 = I2CreadByte(0x34, 0x4A);
Log::console(PSTR("PMU status1,status2 : %02X,%02X"), pmustat1, pmustat2);
Log::console(PSTR("PWRON,PWROFF status : %02X,%02X"), pwronsta, pwrofsta);
Log::console(PSTR("IRQ status 0,1,2 : %02X,%02X,%02X"), irqstat0, irqstat1, irqstat2);
+
+#if CONFIG_IDF_TARGET_ESP32S3
+ if (boardIdx == TTGO_TBEAM_SX1262) {
+ deepSleepSensors();
+ }
+#endif
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Wire.end();
-}
\ No newline at end of file
+}
+
+void Power::deepSleepSensors() {
+ board_t board;
+ if (!ConfigManager::getInstance().getBoardConfig(board)) return;
+
+ // Initialize primary Wire bus for sensors/OLED (pins 17/18 on Supreme)
+ Wire.begin(board.OLED__SDA, board.OLED__SCL);
+
+ // Check for QMC5883L (Compass) at 0x0D
+ Wire.beginTransmission(0x0D);
+ if (Wire.endTransmission() == 0) {
+ Log::console(PSTR("Found QMC5883L at 0x0D, putting to sleep"));
+ Wire.beginTransmission(0x0D);
+ Wire.write(0x09); // Control Register 1
+ Wire.write(0x00); // Standby Mode
+ Wire.endTransmission();
+ }
+
+ // Check for BME280 (Environment) at 0x77
+ Wire.beginTransmission(0x77);
+ if (Wire.endTransmission() == 0) {
+ Log::console(PSTR("Found BME280 at 0x77, putting to sleep"));
+ Wire.beginTransmission(0x77);
+ Wire.write(0xF4); // CTRL_MEAS register
+ Wire.write(0x00); // Sleep mode
+ Wire.endTransmission();
+ }
+}
+
+float Power::getBatteryVoltage() {
+ uint8_t boardIdx = ConfigManager::getInstance().getBoard();
+ if (AXPchip == 1) {
+ // AXP192: 12-bit ADC, 1.1mV/step
+ uint8_t h8 = I2CreadByte(0x34, 0x78);
+ uint8_t l4 = I2CreadByte(0x34, 0x79);
+ return ((h8 << 4) | (l4 & 0x0F)) * 1.1f / 1000.0f;
+ } else if (AXPchip == 2) {
+ // AXP2101: 14-bit register pair, 1mV/step
+ uint8_t h = I2CreadByte(0x34, AXP2101_BATTERY_VOLT_H);
+ uint8_t l = I2CreadByte(0x34, AXP2101_BATTERY_VOLT_L);
+ uint16_t raw = ((uint16_t)(h & AXP2101_BATT_VOLT_MASK) << AXP2101_BATT_VOLT_SHIFT) | l;
+ return raw / 1000.0f;
+ } else {
+ // Fallback: direct ADC (e.g. Heltec boards)
+ return analogRead(36) * 2.0f / 4095.0f * 3.3f;
+ }
+}
+
+int Power::getBatteryPercentage() {
+ if (AXPchip == 2) {
+ return I2CreadByte(0x34, AXP2101_FUEL_GAUGE) & 0x7F;
+ }
+ // Simple estimation from voltage for AXP192 and others
+ float v = getBatteryVoltage();
+ if (v > 4.2f) return 100;
+ if (v < 3.0f) return 0;
+ return (int)((v - 3.0f) / 1.2f * 100.0f);
+}
+
+float Power::getVbusVoltage() {
+ if (AXPchip == 1) {
+ uint8_t h8 = I2CreadByte(0x34, 0x5A);
+ uint8_t l4 = I2CreadByte(0x34, 0x5B);
+ return ((h8 << 4) | (l4 & 0x0F)) * 1.7f / 1000.0f;
+ } else if (AXPchip == 2) {
+ uint8_t h = I2CreadByte(0x34, AXP2101_VBUS_VOLT_H);
+ uint8_t l = I2CreadByte(0x34, AXP2101_VBUS_VOLT_L);
+ uint16_t raw = ((uint16_t)(h & AXP2101_VBUS_VOLT_MASK) << AXP2101_VBUS_VOLT_SHIFT) | l;
+ return raw / 1000.0f;
+ }
+ return 0.0f;
+}
+
+void Power::setGnssPower(bool on) {
+ uint8_t boardIdx = ConfigManager::getInstance().getBoard();
+ if (AXPchip != 2) return;
+
+ byte regV = I2CreadByte(0x34, AXP2101_LDO_ONOFF_CTRL0);
+
+#if CONFIG_IDF_TARGET_ESP32S3
+ if (boardIdx == TTGO_TBEAM_SX1262) {
+ // Supreme: GPS on ALDO4
+ if (on) regV |= (1 << AXP2101_ALDO4_BIT);
+ else regV &= ~(1 << AXP2101_ALDO4_BIT);
+ } else
+#endif
+ {
+ // T-Beam V1.2: GPS on ALDO3
+ if (on) regV |= (1 << AXP2101_ALDO3_BIT);
+ else regV &= ~(1 << AXP2101_ALDO3_BIT);
+ }
+
+ I2CwriteByte(0x34, AXP2101_LDO_ONOFF_CTRL0, regV);
+ Log::console(PSTR("GNSS power %s"), on ? "ON" : "OFF");
+}
diff --git a/tinyGS/src/Power/Power.h b/tinyGS/src/Power/Power.h
index 6bcc79ef..88cdd575 100644
--- a/tinyGS/src/Power/Power.h
+++ b/tinyGS/src/Power/Power.h
@@ -44,20 +44,22 @@
#define XPOWERS_AXP202_IC_TYPE (0x03)
#define XPOWERS_AXP202_CHIP_ID (0x41)
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// * * * * * * * A X P C H I P R E A D I N G * * * * * * * * *
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-#define XPOWERS_AXP2101_ADC_DATA_RELUST0 (0x34)
-#define XPOWERS_AXP2101_ADC_DATA_RELUST1 (0x35)
-#define XPOWERS_AXP2101_ADC_DATA_RELUST2 (0x36)
-#define XPOWERS_AXP2101_ADC_DATA_RELUST3 (0x37)
-#define XPOWERS_AXP2101_ADC_DATA_RELUST4 (0x38)
-#define XPOWERS_AXP2101_ADC_DATA_RELUST5 (0x39)
-#define XPOWERS_AXP2101_ADC_DATA_RELUST6 (0x3A)
-#define XPOWERS_AXP2101_ADC_DATA_RELUST7 (0x3B)
-#define XPOWERS_AXP2101_ADC_DATA_RELUST8 (0x3C)
-#define XPOWERS_AXP2101_ADC_DATA_RELUST9 (0x3D)
+// AXP2101 Registers and Bits
+#define AXP2101_LDO_ONOFF_CTRL0 0x90
+#define AXP2101_ALDO1_BIT 0
+#define AXP2101_ALDO2_BIT 1
+#define AXP2101_ALDO3_BIT 2
+#define AXP2101_ALDO4_BIT 3
+#define AXP2101_BATTERY_VOLT_H 0x34
+#define AXP2101_BATTERY_VOLT_L 0x35
+#define AXP2101_VBUS_VOLT_H 0x38
+#define AXP2101_VBUS_VOLT_L 0x39
+#define AXP2101_FUEL_GAUGE 0xA4
+#define AXP2101_BATT_VOLT_MASK 0xFF
+#define AXP2101_BATT_VOLT_SHIFT 8
+#define AXP2101_VBUS_VOLT_MASK 0xFF
+#define AXP2101_VBUS_VOLT_SHIFT 8
extern Status status;
@@ -65,14 +67,21 @@ class Power {
public:
static Power& getInstance()
{
- static Power instance;
+ static Power instance;
return instance;
}
- void checkAXP();
+ Power();
+ void checkAXP();
+ float getBatteryVoltage();
+ int getBatteryPercentage();
+ float getVbusVoltage();
+ void setGnssPower(bool on);
+ void deepSleepSensors();
+ TwoWire* getPmuWire() { return pmuWire; }
private:
void I2CwriteByte(uint8_t Address, uint8_t Register, uint8_t Data);
uint8_t I2CreadByte(uint8_t Address, uint8_t Register);
void I2Cread(uint8_t Address, uint8_t Register, uint8_t Nbytes, uint8_t* Data);
-
+ TwoWire* pmuWire;
};
#endif
\ No newline at end of file
diff --git a/tinyGS/src/Radio/RadioHal.cpp b/tinyGS/src/Radio/RadioHal.cpp
index 46fcf0a6..01190ee5 100644
--- a/tinyGS/src/Radio/RadioHal.cpp
+++ b/tinyGS/src/Radio/RadioHal.cpp
@@ -29,11 +29,15 @@ template<>
int16_t RadioHal::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint16_t preambleLength, uint8_t gain, float tcxoVoltage)
{
if (power>=17) radio->setCurrentLimit(150);
+ // DIO2 controls the RF switch on SX126x reference designs. On boards
+ // without an external switch on DIO2, the pin simply toggles unused.
+ radio->setDio2AsRfSwitch(true);
return radio->begin(freq, bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage);
}
template<>
int16_t RadioHal::begin()
{
+ radio->setDio2AsRfSwitch(true);
return radio->begin();
}
@@ -41,11 +45,13 @@ template<>
int16_t RadioHal::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint16_t preambleLength, uint8_t gain, float tcxoVoltage)
{
if (power>=17) radio->setCurrentLimit(150);
+ radio->setDio2AsRfSwitch(true);
return radio->begin(freq, bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage);
}
template<>
int16_t RadioHal::begin()
{
+ radio->setDio2AsRfSwitch(true);
return radio->begin();
}
@@ -80,6 +86,7 @@ template<>
int16_t RadioHal::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t power, uint16_t preambleLength, bool enableOOK, float tcxoVoltage, bool useRegulatorLDO)
{
if (power>=17) radio->setCurrentLimit(150);
+ radio->setDio2AsRfSwitch(true);
return radio->beginFSK(freq, br, freqDev, rxBw, power, preambleLength, tcxoVoltage, useRegulatorLDO);
}
@@ -87,6 +94,7 @@ template<>
int16_t RadioHal::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t power, uint16_t preambleLength, bool enableOOK, float tcxoVoltage, bool useRegulatorLDO)
{
if (power>=17) radio->setCurrentLimit(150);
+ radio->setDio2AsRfSwitch(true);
return radio->beginFSK(freq, br, freqDev, rxBw, power, preambleLength, tcxoVoltage, useRegulatorLDO);
}
diff --git a/tinyGS/tinyGS.ino b/tinyGS/tinyGS.ino
index 7cf0b495..0a083c1c 100644
--- a/tinyGS/tinyGS.ino
+++ b/tinyGS/tinyGS.ino
@@ -80,7 +80,7 @@
#include "time.h"
#include "src/Mqtt/MQTT_credentials.h"
#include "src/Improv/tinygs_improv.h"
-
+#include "src/Power/Power.h"
#if RADIOLIB_VERSION_MAJOR != (0x07) || RADIOLIB_VERSION_MINOR != (0x06) || RADIOLIB_VERSION_PATCH != (0x00) || RADIOLIB_VERSION_EXTRA != (0x00)
#error "You are not using the correct version of RadioLib please copy TinyGS/lib/RadioLib on Arduino/libraries"
@@ -113,6 +113,7 @@ void configured()
{
configManager.setConfiguredCallback(NULL);
configManager.printConfig();
+ Power::getInstance().checkAXP();
radio.init();
}
@@ -376,14 +377,17 @@ void checkButton()
}
else {
unsigned long elapsedTime = millis() - buttPressedStart;
- if (elapsedTime > 30 && elapsedTime < 1000) // short press
+ if (elapsedTime > 30 && elapsedTime < 1000) { // short press
+ displayResetTimeout();
displayNextFrame();
+ }
buttPressedStart = 0;
}
}
void handleSerial () {
while (Serial.available () > 0) {
+ displayResetTimeout();
yield ();
byte next = Serial.peek ();
if (next == 'I') {
@@ -425,6 +429,11 @@ void handleRawSerial()
ESP.restart();
break;
case 'p':
+ if (!radio.isReady())
+ {
+ Log::console(PSTR("Radio is not initialized. Configure the board first."));
+ break;
+ }
if (!configManager.getAllowTx())
{
Log::console(PSTR("Radio transmission is not allowed by config! Check your config on the web panel and make sure transmission is allowed by local regulations"));