Skip to content
Closed
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
17 changes: 15 additions & 2 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ data_dir = tinyGS/data

[env]
build_flags =
!python git_rev_macro.py
!python3 git_rev_macro.py
-DMQTT_MAX_PACKET_SIZE=1000
-DCORE_DEBUG_LEVEL=0
-DIOTWEBCONF_DEBUG_DISABLED=0
Expand All @@ -23,12 +23,13 @@ build_flags =
lib_deps =
improv/Improv@^1.2.4
jgromes/RadioLib @ 7.5.0
mikalhart/TinyGPSPlus @ ^1.0.3
# Uncomment these 2 lines by deleting ";" and edit as needed to upload through OTA
;upload_protocol = espota
;upload_port = IP_OF_THE_BOARD

monitor_speed = 115200
upload_speed = 921600
upload_speed = 460800

; NOTE: There is no need to change anything below here, the board is configured through the Web panel
; Only make changes if you know what you are doing
Expand Down Expand Up @@ -74,3 +75,15 @@ build_flags =
${env.build_flags}
-DBOARD_HAS_PSRAM
board_build.partitions = tinygs_4M_partition_table.csv

[env:lilygo-t-beam-supreme]
platform = espressif32@ 6.10.0
board = lilygo-t-beam-supreme
board_build.mcu = esp32s3
framework = arduino
build_flags =
${env.build_flags}
-DBOARD_HAS_PSRAM
-DARDUINO_USB_CDC_ON_BOOT=1
-DARDUINO_USB_MODE=1
board_build.partitions = tinygs_4M_partition_table.csv
325 changes: 150 additions & 175 deletions tinyGS/src/ConfigManager/ConfigManager.cpp

Large diffs are not rendered by default.

38 changes: 35 additions & 3 deletions tinyGS/src/ConfigManager/ConfigManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,25 @@ constexpr auto configVersion = "0.05"; //max 4 chars

constexpr auto AP_TIMEOUT_MS = "300000";

// LilyGo T-Beam Supreme Pin Definitions
constexpr uint8_t SUPREME_SDA = 17;
constexpr uint8_t SUPREME_SCL = 18;
constexpr uint8_t SUPREME_GNSS_TX = 8; // Connected to ESP RX
constexpr uint8_t SUPREME_GNSS_RX = 9; // Connected to ESP TX
constexpr uint8_t SUPREME_GNSS_WAKEUP = 7;
constexpr uint8_t SUPREME_GNSS_PPS = 6;
constexpr uint8_t SUPREME_PMU_SDA = 42;
constexpr uint8_t SUPREME_PMU_SCL = 41;
constexpr uint8_t SUPREME_LED = 3;

enum boardNum
{
#if CONFIG_IDF_TARGET_ESP32S3
HELTEC_LORA32_V3 = 0,
ESP32S3_SX1278_LF,
TTGO_TBEAM_SX1262,
LILYGO_T3S3_SX1280,
LILYGO_TBEAM_SUPREME,
#elif CONFIG_IDF_TARGET_ESP32C3
HELTEC_LORA32_HTCT62 = 0,
ESP32C3_SX1278_LF,
Expand Down Expand Up @@ -129,6 +141,9 @@ typedef struct
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;

Expand All @@ -155,13 +170,15 @@ class ConfigManager : public IotWebConf2
boolean init();
void printConfig();

bool getAutoLocation() { return !strcmp(autoLocation, CB_SELECTED_STR); }
uint16_t getGnssInterval() { return (uint16_t)atoi(gnssInterval); }
uint16_t getMqttPort() { return (uint16_t)atoi(mqttPort); }
const char *getMqttServer() { return mqttServer; }
const char *getMqttUser() { return mqttUser; }
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"; } // Safe offset, default to GMT0
uint8_t getBoard() { return atoi(board); }
uint8_t getOledBright() { return atoi(oledBright); }
bool getAllowTx() { return !strcmp(allowTx, CB_SELECTED_STR); }
Expand Down Expand Up @@ -215,6 +232,16 @@ class ConfigManager : public IotWebConf2
strcpy(longitude, buffer);
this->saveConfig();
}
// Runtime setters that do not save to flash
void setLatRuntime(float lat) {
if (isnan(lat)) return;
dtostrf(lat, 1, 3, latitude);
}
void setLonRuntime(float lon) {
if (isnan(lon)) return;
dtostrf(lon, 1, 3, longitude);
}

void setName(const char *buffer)
{
strncpy(getThingNameParameter()->valueBuffer, buffer, IOTWEBCONF_WORD_LEN);
Expand All @@ -228,6 +255,7 @@ class ConfigManager : public IotWebConf2
}

const char *getWiFiSSID() { return getWifiSsidParameter()->valueBuffer; }
const char *getWiFiPassword() { return getWifiPasswordParameter()->valueBuffer; }
bool isConnected() { return getState() == IOTWEBCONF_STATE_ONLINE; };
bool isApMode() { return (getState() != IOTWEBCONF_STATE_CONNECTING && getState() != IOTWEBCONF_STATE_ONLINE); }
bool getFlipOled() { return advancedConf.flipOled; }
Expand All @@ -238,7 +266,7 @@ class ConfigManager : public IotWebConf2
bool ret = true;
if (!currentBoardDirty) { board = currentBoard; return ret; }

if (getBoardTemplate()[0] == '\0') { currentBoard = boards[getBoard()]; }
if (getBoardTemplate()[0] == '\0') { currentBoard = boards[getBoard()]; }
else { ret = parseBoardTemplate(currentBoard); }
currentBoardDirty = false;
board = currentBoard;
Expand Down Expand Up @@ -328,6 +356,8 @@ class ConfigManager : public IotWebConf2
char telemetry3rd[CHECKBOX_LENGTH] = "";
char testMode[CHECKBOX_LENGTH] = "";
char autoUpdate[CHECKBOX_LENGTH] = "";
char autoLocation[CHECKBOX_LENGTH] = "selected";
char gnssInterval[NUMBER_LEN] = "";
char boardTemplate[TEMPLATE_LEN] = "";
char modemStartup[MODEM_LEN] = "";
char advancedConfig[ADVANCED_LEN] = "";
Expand All @@ -351,11 +381,13 @@ class ConfigManager : public IotWebConf2
iotwebconf2::CheckboxParameter telemetry3rdParam = iotwebconf2::CheckboxParameter("Allow sending telemetry to third party", "telemetry3rd", telemetry3rd, CHECKBOX_LENGTH, true);
iotwebconf2::CheckboxParameter testParam = iotwebconf2::CheckboxParameter("Test mode", "test", testMode, CHECKBOX_LENGTH, false);
iotwebconf2::CheckboxParameter autoUpdateParam = iotwebconf2::CheckboxParameter("Automatic Firmware Update", "auto_update", autoUpdate, CHECKBOX_LENGTH, true);
iotwebconf2::CheckboxParameter autoLocationParam = iotwebconf2::CheckboxParameter("Auto Location (GNSS)", "auto_loc", autoLocation, CHECKBOX_LENGTH, true);
iotwebconf2::NumberParameter gnssIntervalParam = iotwebconf2::NumberParameter("GNSS Update Interval (sec, 0=once)", "gnss_int", gnssInterval, NUMBER_LEN, "0", "0..3600", "min='0' max='3600' step='1'");

iotwebconf2::ParameterGroup groupAdvanced = iotwebconf2::ParameterGroup("Advanced config", "Advanced Config (do not modify unless you know what you are doing)");
iotwebconf2::TextParameter boardTemplateParam = iotwebconf2::TextParameter("Board Template (requires manual restart)", "board_template", boardTemplate, TEMPLATE_LEN, NULL, NULL, "type=\"text\" maxlength=255");
iotwebconf2::TextParameter modemParam = iotwebconf2::TextParameter("Modem startup", "modem_startup", modemStartup, MODEM_LEN, "", "", "type=\"text\" maxlength=255");
iotwebconf2::TextParameter advancedConfigParam = iotwebconf2::TextParameter("Advanced parameters", "advanced_config", advancedConfig, ADVANCED_LEN, NULL, NULL, "type=\"text\" maxlength=255");
};

#endif
#endif
7 changes: 4 additions & 3 deletions tinyGS/src/ConfigManager/html.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const char BOARD_NAMES[][BOARD_NAME_LENGTH] PROGMEM =
"Custom ESP32-S3 433MHz SX1278",
"433 Mhz TTGO T-Beam Sup SX1262 V1.0",
"2.4Ghz LILYGO SX1280",
"433 Mhz LilyGo T-Beam Supreme",
#elif CONFIG_IDF_TARGET_ESP32C3
"433MHz HELTEC LORA32 HT-CT62 SX1262",
"Custom ESP32-C3 433MHz SX1278",
Expand Down Expand Up @@ -64,7 +65,7 @@ const char BOARD_NAMES[][BOARD_NAME_LENGTH] PROGMEM =
constexpr auto BOARD_LENGTH = 3;

#if CONFIG_IDF_TARGET_ESP32S3
const char BOARD_VALUES[][BOARD_LENGTH] PROGMEM = {"0", "1", "2","3" };
const char BOARD_VALUES[][BOARD_LENGTH] PROGMEM = {"0", "1", "2","3", "4" };
#elif CONFIG_IDF_TARGET_ESP32C3
const char BOARD_VALUES[][BOARD_LENGTH] PROGMEM = {"0", "1" };
#else
Expand All @@ -82,5 +83,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_CONFIG_STYLE_INNER[] PROGMEM = " fieldset[id='Board config'] div:nth-of-type(3) ~ div { display:none}";
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<7;r++){mc.rows[r].cells[1].innerHTML=wmp[r+2]};if(wmp[2]=='LoRa'){mc.rows[4].cells[0].innerHTML='Spreading Factor ';mc.rows[5].cells[0].innerHTML='Coding Rate ';}else{mc.rows[4].cells[0].innerHTML='Bitrate ';mc.rows[5].cells[0].innerHTML='Frequency dev ';};gs=document.getElementById('gsstatus');for(let r=0;r<7;r++){gs.rows[r].cells[1].innerHTML=wmp[r+9];};sd=document.getElementById('satdata');for(let r=0;r<6;r++){sd.rows[r].cells[1].innerHTML=wmp[r+16];};lp=document.getElementById('lastpacket');for(let r=0;r<4;r++){lp.rows[r].cells[1].innerHTML=wmp[r+22];};lp.rows[4].cells[0].innerHTML=wmp[26];}};wmx.open('GET','wm',true);wmx.send();wmt=setTimeout(wmf,5000);return false;}window.addEventListener('load', wmf);";
const char IOTWEBCONF_CONFIG_STYLE_INNER[] PROGMEM = "";
45 changes: 41 additions & 4 deletions tinyGS/src/Display/Display.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
#include "../ConfigManager/ConfigManager.h"
#include "../Mqtt/MQTT_credentials.h"
#include "../Logger/Logger.h"
#include "../GnssManager/GnssManager.h"

SSD1306* display;
OLEDDisplay* display;
OLEDDisplayUi* ui = NULL;

void msOverlay(OLEDDisplay *display, OLEDDisplayUiState* state);
Expand All @@ -49,13 +50,27 @@ int graphVal = 1;
int delta = 1;
uint8_t oldOledBright = -1; // to force brightness update on first run

#define DISPLAY_TIMEOUT 300000 // 5 minutes
static unsigned long lastDisplayActivity = 0;

void displayResetTimeout() {
lastDisplayActivity = millis();
}

void displayInit()
{
lastDisplayActivity = millis();
board_t board;
uint8_t boardIdx = ConfigManager::getInstance().getBoard();
if (!ConfigManager::getInstance().getBoardConfig(board))
return;

display = new SSD1306(board.OLED__address, board.OLED__SDA, board.OLED__SCL);
if (boardIdx == LILYGO_TBEAM_SUPREME || boardIdx == TTGO_TBEAM_SX1262) {
Log::console(PSTR("Display: Initializing SH1106 for Supreme"));
display = new SH1106Wire(board.OLED__address, board.OLED__SDA, board.OLED__SCL);
} else {
display = new SSD1306Wire(board.OLED__address, board.OLED__SDA, board.OLED__SCL);
}

ui = new OLEDDisplayUi(display);
ui->setTargetFPS(60);
Expand Down Expand Up @@ -105,7 +120,7 @@ void msOverlay(OLEDDisplay *display, OLEDDisplayUiState* state)
timeinfo = localtime (&currenttime);

// Usar buffer estático para evitar fragmentación del heap
char timeBuffer[12];
char timeBuffer[20];
snprintf(timeBuffer, sizeof(timeBuffer), "%2d:%02d:%02d",
timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec);
display->drawString(128, 0, timeBuffer);
Expand Down Expand Up @@ -293,6 +308,22 @@ void drawFrame8(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int1
display->drawString(x+2, 16+y, "MQTT:" );
if (status.mqtt_connected) { display->drawString( x+7, 26+y, "ON"); } else { display->drawString( x+5, 26+y, "OFF"); }
display->drawXbm(x + 32, y + 4, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits);

// GNSS on Right
display->setTextAlignment(TEXT_ALIGN_RIGHT);
char gnssBuf[16];
if (GnssManager::getInstance().hasFix()) {
snprintf(gnssBuf, sizeof(gnssBuf), "GPS:%d", GnssManager::getInstance().getSatellites());
} else {
snprintf(gnssBuf, sizeof(gnssBuf), "GPS:..");
}
display->drawString(x+126, 16+y, gnssBuf);
if (GnssManager::getInstance().hasFix()) {
display->drawString(x+126, 26+y, "FIX");
} else {
display->drawString(x+126, 26+y, "No Fix");
}

// The coordinates define the center of the text
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->drawString(64 + x, 42 + y, "Connected " + (WiFi.localIP().toString()));
Expand Down Expand Up @@ -355,6 +386,12 @@ void displayUpdate()
// Get the current OLED brightness from configuration
uint8_t oledBright = ConfigManager::getInstance().getOledBright();

// Check for timeout
if (oledBright > 0 && millis() - lastDisplayActivity > DISPLAY_TIMEOUT) {
displayTurnOff();
return;
}

// Check if brightness has changed
if (oldOledBright != oledBright) {
if (oledBright) {
Expand Down Expand Up @@ -383,4 +420,4 @@ void displayTurnOff()
void displayNextFrame() {
if (ui)
ui->nextFrame();
}
}
6 changes: 5 additions & 1 deletion tinyGS/src/Display/Display.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

#include "SSD1306.h" // https://github.com/ThingPulse/esp8266-oled-ssd1306
#include "OLEDDisplay.h"
#include "SSD1306Wire.h"
#include "SH1106Wire.h"
#include "OLEDDisplayUi.h" // https://github.com/ThingPulse/esp8266-oled-ssd1306
#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 Status status;
extern OLEDDisplay* display;


Loading