Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions boards/lilygo-t-beam-supreme.json
Original file line number Diff line number Diff line change
@@ -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"
}
55 changes: 55 additions & 0 deletions doc/LilyGo_T-Beam_Supreme.md
Original file line number Diff line number Diff line change
@@ -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.
14 changes: 14 additions & 0 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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
93 changes: 59 additions & 34 deletions tinyGS/src/ConfigManager/ConfigManager.cpp

Large diffs are not rendered by default.

17 changes: 14 additions & 3 deletions tinyGS/src/ConfigManager/ConfigManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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); }
Expand Down
4 changes: 2 additions & 2 deletions tinyGS/src/ConfigManager/html.h
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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 = '<div id=""dt-' + current_id + '"">' + dictTable(dict) + '<input type=""button"" value=""Done ' + current_id + '"" onclick=""tableDoneHandler()"" style=""height:35px;width:100px;background-color:lightblue;""></div>'; 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}";
37 changes: 35 additions & 2 deletions tinyGS/src/Display/Display.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand All @@ -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();
Expand Down
8 changes: 6 additions & 2 deletions tinyGS/src/Display/Display.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

#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"

Expand All @@ -30,7 +32,9 @@ void displayShowStaMode(bool ap);
void displayUpdate();
void displayTurnOff();
void displayNextFrame();
void displayResetTimeout();

extern OLEDDisplay* display;
extern Status status;


35 changes: 3 additions & 32 deletions tinyGS/src/Mqtt/MQTT_Client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "../Radio/Radio.h"
#include "../OTA/OTA.h"
#include "../Logger/Logger.h"
#include "../Power/Power.h"
#include <esp_ota_ops.h>


Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

2 changes: 0 additions & 2 deletions tinyGS/src/Mqtt/MQTT_Client.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading