From df7a8b9e1a8b2740c39e18f6fc11bc0ff7832d81 Mon Sep 17 00:00:00 2001 From: Bruce Fitzsimons Date: Wed, 4 Mar 2026 13:37:35 +1100 Subject: [PATCH 1/2] fix: guard radio !p crash, getTZ() bounds, and worldmap JS variable Three independent stability fixes: 1. Guard !p command when radio HAL not initialized - the test packet command dereferences radioHal without checking, crashing on unconfigured boards. 2. Guard getTZ() against short/empty timezone buffer - unconditionally skipping 3 chars for deduplication prefix reads past buffer end on first boot. Returns "GMT0" as safe default. 3. Correct worldmap JS variable reference - AJAX handler used 'x.status' (console script variable) instead of 'wmx.status', preventing worldmap data updates when both scripts are active. --- tinyGS/src/ConfigManager/ConfigManager.h | 2 +- tinyGS/src/ConfigManager/html.h | 2 +- tinyGS/tinyGS.ino | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tinyGS/src/ConfigManager/ConfigManager.h b/tinyGS/src/ConfigManager/ConfigManager.h index b56dfb1a..81ee7737 100644 --- a/tinyGS/src/ConfigManager/ConfigManager.h +++ b/tinyGS/src/ConfigManager/ConfigManager.h @@ -169,7 +169,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..a2cd0161 100644 --- a/tinyGS/src/ConfigManager/html.h +++ b/tinyGS/src/ConfigManager/html.h @@ -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/tinyGS.ino b/tinyGS/tinyGS.ino index 7cf0b495..532718d7 100644 --- a/tinyGS/tinyGS.ino +++ b/tinyGS/tinyGS.ino @@ -425,6 +425,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")); From aec26d67a3668f3e9f7bc52c183638fd03b78784 Mon Sep 17 00:00:00 2001 From: Bruce Fitzsimons Date: Sun, 22 Feb 2026 14:17:47 +1100 Subject: [PATCH 2/2] feat(board): add LilyGo T-Beam Supreme support with AXP2101 and SH1106 Upgrades the existing TTGO_TBEAM_SX1262 board entry with full hardware support for the LilyGo T-Beam Supreme S3, a newer revision of the same board line. The original entry had basic pin mappings but lacked PMU support, display driver, and auto-detection. Board entry changes: - TCXO voltage 1.6V to 1.8V (Meshtastic/community standard) - LED pin uses SUPREME_LED define, GNSS pins mapped - Renamed to "433 Mhz LilyGo T-Beam Supreme SX1262" - Board index bounds check on boot prevents crash if enum changes New hardware support: - AXP2101 PMU on Wire1 (GPIO 42/41): battery voltage/percentage, VBUS, per-rail power control (ALDO1 sensors, ALDO3 radio, ALDO4 GNSS), sensor deep sleep, unused rails disabled - SH1106 OLED auto-detection with 5-minute display timeout - Auto-detection via AXP2101 probe on Wire1 in boardDetection() - DIO2 RF switch enable for SX1262/SX1268 - Battery voltage via Power API replacing raw analogRead(36) median-sort Includes board JSON, PlatformIO env, and setup documentation. 433 MHz SX1262 variant only. --- boards/lilygo-t-beam-supreme.json | 51 ++++++ doc/LilyGo_T-Beam_Supreme.md | 55 ++++++ platformio.ini | 14 ++ tinyGS/src/ConfigManager/ConfigManager.cpp | 93 ++++++---- tinyGS/src/ConfigManager/ConfigManager.h | 15 +- tinyGS/src/ConfigManager/html.h | 2 +- tinyGS/src/Display/Display.cpp | 37 +++- tinyGS/src/Display/Display.h | 8 +- tinyGS/src/Mqtt/MQTT_Client.cpp | 35 +--- tinyGS/src/Mqtt/MQTT_Client.h | 2 - tinyGS/src/Power/Power.cpp | 202 ++++++++++++++++++--- tinyGS/src/Power/Power.h | 41 +++-- tinyGS/src/Radio/RadioHal.cpp | 8 + tinyGS/tinyGS.ino | 8 +- 14 files changed, 448 insertions(+), 123 deletions(-) create mode 100644 boards/lilygo-t-beam-supreme.json create mode 100644 doc/LilyGo_T-Beam_Supreme.md 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 81ee7737..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; diff --git a/tinyGS/src/ConfigManager/html.h b/tinyGS/src/ConfigManager/html.h index a2cd0161..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", 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 532718d7..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') {