Skip to content

Commit 9d60348

Browse files
committed
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.
1 parent 10c0f9c commit 9d60348

14 files changed

Lines changed: 451 additions & 127 deletions

File tree

boards/lilygo-t-beam-supreme.json

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{
2+
"build": {
3+
"arduino":{
4+
"ldscript": "esp32s3_out.ld",
5+
"partitions": "default.csv",
6+
"memory_type": "qio_qspi"
7+
},
8+
"core": "esp32",
9+
"extra_flags": [
10+
"-DARDUINO_USB_MODE=1",
11+
"-DARDUINO_USB_CDC_ON_BOOT=1",
12+
"-DARDUINO_RUNNING_CORE=1",
13+
"-DARDUINO_EVENT_RUNNING_CORE=1"
14+
],
15+
"f_cpu": "240000000L",
16+
"f_flash": "80000000L",
17+
"flash_mode": "qio",
18+
"hwids": [
19+
[
20+
"0x303A",
21+
"0x1001"
22+
]
23+
],
24+
"mcu": "esp32s3",
25+
"variant": "esp32s3"
26+
},
27+
"connectivity": [
28+
"wifi"
29+
],
30+
"debug": {
31+
"default_tool": "esp-builtin",
32+
"onboard_tools": [
33+
"esp-builtin"
34+
],
35+
"openocd_target": "esp32s3.cfg"
36+
},
37+
"frameworks": [
38+
"arduino",
39+
"espidf"
40+
],
41+
"name": "LilyGo T-Beam supreme (8MB Flash 8MB PSRAM)",
42+
"upload": {
43+
"flash_size": "8MB",
44+
"maximum_ram_size": 327680,
45+
"maximum_size": 8388608,
46+
"require_upload_port": true,
47+
"speed": 460800
48+
},
49+
"url": "https://www.lilygo.cc/products/t-beamsupreme-m",
50+
"vendor": "LilyGo"
51+
}

doc/LilyGo_T-Beam_Supreme.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# LilyGo T-Beam Supreme (S3) Setup for TinyGS
2+
3+
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).
4+
5+
## Pin Mapping (Verified)
6+
7+
| Function | Pin (GPIO) |
8+
| :--- | :--- |
9+
| **LoRa CS (NSS)** | 10 |
10+
| **LoRa DIO1** | 1 |
11+
| **LoRa BUSY** | 4 |
12+
| **LoRa RESET** | 5 |
13+
| **LoRa SCK** | 12 |
14+
| **LoRa MISO** | 13 |
15+
| **LoRa MOSI** | 11 |
16+
| **I2C SDA** | 17 |
17+
| **I2C SCL** | 18 |
18+
| **GPS TX** | 8 |
19+
| **GPS RX** | 9 |
20+
| **GPS Wakeup** | 7 |
21+
| **PMU SDA** | 42 (Wire1) |
22+
| **PMU SCL** | 41 (Wire1) |
23+
| **User Button** | 0 |
24+
| **Green LED** | 3 |
25+
26+
## Features
27+
28+
### 1. Power Management (AXP2101)
29+
- Full support for AXP2101 PMU via Wire1.
30+
- Correct battery voltage reading (1mV/bit).
31+
- Accurate fuel gauge percentage.
32+
- Automatic power control for Radio and GPS.
33+
34+
### 2. Display (SH1106)
35+
- SH1106 OLED driver (vs SSD1306 on other boards).
36+
- Auto-off after 5 minutes of inactivity to save power.
37+
- Wakes on button press or serial input.
38+
39+
### 3. Power Saving
40+
- **Shared Rail (ALDO1)**: Powers the OLED and onboard sensors (QMC5883L Compass, BME280). This rail remains active even when the display is off.
41+
- **Radio Rail (ALDO3)**: Powers the SX1262 LoRa module.
42+
- **GPS Rail (ALDO4)**: Powers the GNSS module independently.
43+
- **Unused Rails**: Unused PMU rails (ALDO2, BLDO1/2, DLDO1/2) are explicitly disabled to minimize quiescent current.
44+
45+
## Configuration
46+
47+
1. Connect to the TinyGS Access Point.
48+
2. Navigate to `http://192.168.4.1/config`.
49+
3. Select **433 Mhz LilyGo T-Beam Supreme SX1262** as the board type.
50+
4. Save and Restart.
51+
52+
## Troubleshooting
53+
54+
- **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.
55+
- **Battery 0.00V**: Ensure the board is selected as "Supreme" to enable the AXP2101 driver.

platformio.ini

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,17 @@ build_flags =
8181
${env.build_flags}
8282
-DBOARD_HAS_PSRAM
8383
board_build.partitions = tinygs_4M_partition_table.csv
84+
85+
[env:lilygo-t-beam-supreme]
86+
platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
87+
board = lilygo-t-beam-supreme
88+
board_build.mcu = esp32s3
89+
framework = arduino
90+
build_flags =
91+
${env.build_flags}
92+
-DARDUINO_USB_CDC_ON_BOOT=1
93+
-DBOARD_HAS_PSRAM
94+
board_build.partitions = tinygs_4M_partition_table.csv
95+
lib_deps =
96+
${env.lib_deps}
97+
mikalhart/TinyGPSPlus @ ^1.0.3

tinyGS/src/ConfigManager/ConfigManager.cpp

Lines changed: 61 additions & 37 deletions
Large diffs are not rendered by default.

tinyGS/src/ConfigManager/ConfigManager.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ constexpr auto configVersion = "0.05"; //max 4 chars
6969
#define MQTT_DEFAULT_SERVER "mqtt.tinygs.com"
7070
#define MQTT_DEFAULT_PORT "8883"
7171

72+
// T-Beam Supreme specific pins (Wire1 for PMU, separate from OLED Wire bus)
73+
#define SUPREME_GNSS_RX 9
74+
#define SUPREME_GNSS_TX 8
75+
#define SUPREME_GNSS_WAKEUP 7
76+
#define SUPREME_PMU_SDA 42
77+
#define SUPREME_PMU_SCL 41
78+
#define SUPREME_LED 3
79+
7280
constexpr auto AP_TIMEOUT_MS = "300000";
7381

7482
enum boardNum
@@ -130,15 +138,18 @@ struct board_t
130138
float L_TCXO_V;
131139
uint8_t RX_EN;
132140
uint8_t TX_EN;
141+
uint8_t GNSS_RX;
142+
uint8_t GNSS_TX;
143+
uint8_t GNSS_WAKEUP;
133144
String BOARD;
134145

135146
board_t() = default;
136147
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,
137148
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,
138-
float l_tcxo_v, uint8_t rx_en, uint8_t tx_en, String board_name)
149+
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)
139150
: OLED__address(oled_addr), OLED__SDA(oled_sda), OLED__SCL(oled_scl), OLED__RST(oled_rst), PROG__BUTTON(prog_btn), BOARD_LED(board_led),
140151
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),
141-
L_TCXO_V(l_tcxo_v), RX_EN(rx_en), TX_EN(tx_en), BOARD(board_name) {}
152+
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) {}
142153
};
143154

144155
const uint8_t UNUSED = -1;

tinyGS/src/ConfigManager/html.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ const char BOARD_NAMES[][BOARD_NAME_LENGTH] PROGMEM =
2929
#if CONFIG_IDF_TARGET_ESP32S3
3030
"433MHz HELTEC LORA32 V3",
3131
"Custom ESP32-S3 433MHz SX1278",
32-
"433 Mhz TTGO T-Beam Sup SX1262 V1.0",
33-
"EBYTE EoRa-HUB ESP32S3 + LR1121",
32+
"433 Mhz LilyGo T-Beam Supreme SX1262",
3433
"2.4Ghz LILYGO SX1280",
34+
"EBYTE EoRa-HUB ESP32S3 + LR1121",
3535
#elif CONFIG_IDF_TARGET_ESP32C3
3636
"433MHz HELTEC LORA32 HT-CT62 SX1262",
3737
"Custom ESP32-C3 433MHz SX1278",

tinyGS/src/Display/Display.cpp

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,13 @@
2323
#include "../Mqtt/MQTT_credentials.h"
2424
#include "../Logger/Logger.h"
2525

26-
SSD1306* display;
26+
OLEDDisplay* display;
2727
OLEDDisplayUi* ui = NULL;
2828

29+
#define DISPLAY_TIMEOUT 300000 // 5 minutes
30+
static unsigned long lastDisplayActivity = 0;
31+
static bool displayTimedOut = false;
32+
2933
void msOverlay(OLEDDisplay *display, OLEDDisplayUiState* state);
3034
void drawFrame1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
3135
void drawFrame2(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
@@ -55,8 +59,18 @@ void displayInit()
5559
if (!ConfigManager::getInstance().getBoardConfig(board))
5660
return;
5761

58-
display = new SSD1306(board.OLED__address, board.OLED__SDA, board.OLED__SCL);
62+
uint8_t boardIdx = ConfigManager::getInstance().getBoard();
63+
#if CONFIG_IDF_TARGET_ESP32S3
64+
if (boardIdx == TTGO_TBEAM_SX1262) {
65+
Log::console(PSTR("Display: Initializing SH1106 for T-Beam Supreme"));
66+
display = new SH1106Wire(board.OLED__address, board.OLED__SDA, board.OLED__SCL);
67+
} else
68+
#endif
69+
{
70+
display = new SSD1306Wire(board.OLED__address, board.OLED__SDA, board.OLED__SCL);
71+
}
5972

73+
lastDisplayActivity = millis();
6074
ui = new OLEDDisplayUi(display);
6175
ui->setTargetFPS(60);
6276
ui->setActiveSymbol(activeSymbol);
@@ -355,6 +369,15 @@ void displayUpdate()
355369
// Get the current OLED brightness from configuration
356370
uint8_t oledBright = ConfigManager::getInstance().getOledBright();
357371

372+
// Check display timeout
373+
if (oledBright && !displayTimedOut && (millis() - lastDisplayActivity > DISPLAY_TIMEOUT)) {
374+
displayTimedOut = true;
375+
display->displayOff();
376+
return;
377+
}
378+
379+
if (displayTimedOut) return;
380+
358381
// Check if brightness has changed
359382
if (oldOledBright != oledBright) {
360383
if (oledBright) {
@@ -374,6 +397,16 @@ void displayUpdate()
374397
}
375398
}
376399

400+
void displayResetTimeout()
401+
{
402+
lastDisplayActivity = millis();
403+
if (displayTimedOut) {
404+
displayTimedOut = false;
405+
display->displayOn();
406+
oldOledBright = -1; // force brightness reapply
407+
}
408+
}
409+
377410
void displayTurnOff()
378411
{
379412
display->displayOff();

tinyGS/src/Display/Display.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
along with this program. If not, see <https://www.gnu.org/licenses/>.
1818
*/
1919

20-
#include "SSD1306.h" // https://github.com/ThingPulse/esp8266-oled-ssd1306
21-
#include "OLEDDisplayUi.h" // https://github.com/ThingPulse/esp8266-oled-ssd1306
20+
#include "OLEDDisplay.h" // https://github.com/ThingPulse/esp8266-oled-ssd1306
21+
#include "SSD1306Wire.h"
22+
#include "SH1106Wire.h"
23+
#include "OLEDDisplayUi.h"
2224
#include "../ConfigManager/ConfigManager.h"
2325
#include "../Status.h"
2426

@@ -30,7 +32,9 @@ void displayShowStaMode(bool ap);
3032
void displayUpdate();
3133
void displayTurnOff();
3234
void displayNextFrame();
35+
void displayResetTimeout();
3336

37+
extern OLEDDisplay* display;
3438
extern Status status;
3539

3640

tinyGS/src/Mqtt/MQTT_Client.cpp

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "../Radio/Radio.h"
2727
#include "../OTA/OTA.h"
2828
#include "../Logger/Logger.h"
29+
#include "../Power/Power.h"
2930
#include <esp_ota_ops.h>
3031

3132

@@ -108,7 +109,7 @@ void MQTT_Client::loop()
108109
else
109110
{
110111
StaticJsonDocument<192> doc;
111-
doc["Vbat"] = voltage();
112+
doc["Vbat"] = Power::getInstance().getBatteryVoltage();
112113
doc["Mem"] = ESP.getFreeHeap();
113114
doc["MinMem"] = ESP.getMinFreeHeap(); // Mínimo histórico
114115
doc["MaxBlk"] = ESP.getMaxAllocHeap(); // Bloque más grande disponible
@@ -249,7 +250,7 @@ void MQTT_Client::sendWelcome()
249250
doc["board"] = configManager.getBoard();
250251
doc["mac"] = clientId;
251252
doc["seconds"] = millis()/1000;
252-
doc["Vbat"] = voltage();
253+
doc["Vbat"] = Power::getInstance().getBatteryVoltage();
253254
doc["chip"] = ESP.getChipModel();
254255
doc["slot"] = esp_ota_get_running_partition ()->label;
255256
doc["pSize"] = esp_ota_get_running_partition ()->size;
@@ -1180,34 +1181,4 @@ void MQTT_Client::begin()
11801181

11811182

11821183

1183-
int MQTT_Client::voltage() {
1184-
int medianVoltage;
1185-
int length = 21;
1186-
int voltages[22];
1187-
1188-
for (int i = 0; i < 21; i++)
1189-
{
1190-
voltages[i] = analogRead(36);
1191-
}
1192-
1193-
// BubbleSortAsc from https://www.luisllamas.es/arduino-bubble-sort/
1194-
int i, j, flag = 1;
1195-
int temp;
1196-
for (i = 1; (i <= length) && flag; i++)
1197-
{
1198-
flag = 0;
1199-
for (j = 0; j < (length - 1); j++)
1200-
{
1201-
if (voltages[j + 1] < voltages[j])
1202-
{
1203-
temp = voltages[j];
1204-
voltages[j] = voltages[j + 1];
1205-
voltages[j + 1] = temp;
1206-
flag = 1;
1207-
}
1208-
}
1209-
}
1210-
medianVoltage = voltages[10];
1211-
return medianVoltage;
1212-
}
12131184

tinyGS/src/Mqtt/MQTT_Client.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,6 @@ class MQTT_Client : public PubSubClient {
115115
void processRxQueue();
116116
void sendRxFromQueue(const RxPacketMessage& msg); // Envía paquete desde la cola
117117

118-
int voltage();
119-
120118
//bool usingNewCert = true;
121119
SemaphoreHandle_t radioConfigMutex;
122120
QueueHandle_t rxQueue;

0 commit comments

Comments
 (0)