mirror of
https://github.com/pelgraine/Meck.git
synced 2026-05-04 20:42:41 +02:00
GPS duty cycle and cpu power management for extended battery life implemented
This commit is contained in:
@@ -12,7 +12,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef FIRMWARE_VERSION
|
#ifndef FIRMWARE_VERSION
|
||||||
#define FIRMWARE_VERSION "Meck v0.8.3"
|
#define FIRMWARE_VERSION "Meck v0.8.4"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
#include "MyMesh.h"
|
#include "MyMesh.h"
|
||||||
#include "variant.h" // Board-specific defines (HAS_GPS, etc.)
|
#include "variant.h" // Board-specific defines (HAS_GPS, etc.)
|
||||||
#include "target.h" // For sensors, board, etc.
|
#include "target.h" // For sensors, board, etc.
|
||||||
|
#include "GPSDutyCycle.h"
|
||||||
|
#include "CPUPowerManager.h"
|
||||||
|
|
||||||
// T-Deck Pro Keyboard support
|
// T-Deck Pro Keyboard support
|
||||||
#if defined(LilyGo_TDeck_Pro)
|
#if defined(LilyGo_TDeck_Pro)
|
||||||
@@ -11,7 +13,6 @@
|
|||||||
#include "TextReaderScreen.h"
|
#include "TextReaderScreen.h"
|
||||||
#include "ContactsScreen.h"
|
#include "ContactsScreen.h"
|
||||||
#include "ChannelScreen.h"
|
#include "ChannelScreen.h"
|
||||||
#include "SettingsScreen.h"
|
|
||||||
extern SPIClass displaySpi; // From GxEPDDisplay.cpp, shared SPI bus
|
extern SPIClass displaySpi; // From GxEPDDisplay.cpp, shared SPI bus
|
||||||
|
|
||||||
TCA8418Keyboard keyboard(I2C_ADDR_KEYBOARD, &Wire);
|
TCA8418Keyboard keyboard(I2C_ADDR_KEYBOARD, &Wire);
|
||||||
@@ -40,6 +41,12 @@
|
|||||||
|
|
||||||
// Text reader mode state
|
// Text reader mode state
|
||||||
static bool readerMode = false;
|
static bool readerMode = false;
|
||||||
|
|
||||||
|
// Power management
|
||||||
|
#if HAS_GPS
|
||||||
|
GPSDutyCycle gpsDuty;
|
||||||
|
#endif
|
||||||
|
CPUPowerManager cpuPower;
|
||||||
|
|
||||||
void initKeyboard();
|
void initKeyboard();
|
||||||
void handleKeyboardInput();
|
void handleKeyboardInput();
|
||||||
@@ -392,7 +399,7 @@ void setup() {
|
|||||||
MESH_DEBUG_PRINTLN("setup() - SPIFFS.begin() done");
|
MESH_DEBUG_PRINTLN("setup() - SPIFFS.begin() done");
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Early SD card init — needed BEFORE the_mesh.begin() so we can restore
|
// Early SD card init — needed BEFORE the_mesh.begin() so we can restore
|
||||||
// settings from a previous firmware flash. The display SPI bus is already
|
// settings from a previous firmware flash. The display SPI bus is already
|
||||||
// up (display.begin() ran earlier), so SD can share it now.
|
// up (display.begin() ran earlier), so SD can share it now.
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -448,12 +455,6 @@ void setup() {
|
|||||||
the_mesh.startInterface(serial_interface);
|
the_mesh.startInterface(serial_interface);
|
||||||
MESH_DEBUG_PRINTLN("setup() - the_mesh.startInterface() done");
|
MESH_DEBUG_PRINTLN("setup() - the_mesh.startInterface() done");
|
||||||
|
|
||||||
// T-Deck Pro: default BLE to OFF on boot (user can toggle with Bluetooth page)
|
|
||||||
#if defined(LilyGo_TDeck_Pro)
|
|
||||||
serial_interface.disable();
|
|
||||||
MESH_DEBUG_PRINTLN("setup() - BLE disabled by default (toggle via home screen)");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#error "need to define filesystem"
|
#error "need to define filesystem"
|
||||||
#endif
|
#endif
|
||||||
@@ -501,6 +502,7 @@ void setup() {
|
|||||||
if (reader) {
|
if (reader) {
|
||||||
reader->setSDReady(true);
|
reader->setSDReady(true);
|
||||||
if (disp) {
|
if (disp) {
|
||||||
|
cpuPower.setBoost(); // Boost CPU for EPUB processing
|
||||||
reader->bootIndex(*disp);
|
reader->bootIndex(*disp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -510,30 +512,29 @@ void setup() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// GPS duty cycle — honour saved pref, default to enabled on first boot
|
||||||
// First-boot onboarding detection
|
#if HAS_GPS
|
||||||
// Check if node name is still the default hex prefix (first 4 bytes of pub key)
|
|
||||||
// If so, launch onboarding wizard to set name and radio preset
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
#if defined(LilyGo_TDeck_Pro)
|
|
||||||
{
|
{
|
||||||
char defaultName[10];
|
bool gps_wanted = the_mesh.getNodePrefs()->gps_enabled;
|
||||||
mesh::Utils::toHex(defaultName, the_mesh.self_id.pub_key, 4);
|
gpsDuty.setStreamCounter(&gpsStream);
|
||||||
NodePrefs* prefs = the_mesh.getNodePrefs();
|
gpsDuty.begin(gps_wanted);
|
||||||
if (strcmp(prefs->node_name, defaultName) == 0) {
|
if (gps_wanted) {
|
||||||
MESH_DEBUG_PRINTLN("setup() - Default node name detected, launching onboarding");
|
sensors.setSettingValue("gps", "1");
|
||||||
ui_task.gotoOnboarding();
|
} else {
|
||||||
|
sensors.setSettingValue("gps", "0");
|
||||||
}
|
}
|
||||||
|
MESH_DEBUG_PRINTLN("setup() - GPS duty cycle started (enabled=%d)", gps_wanted);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Enable GPS by default on T-Deck Pro
|
// CPU frequency scaling — drop to 80 MHz for idle mesh listening
|
||||||
#if HAS_GPS
|
cpuPower.begin();
|
||||||
// Set GPS enabled in both sensor manager and node prefs
|
|
||||||
sensors.setSettingValue("gps", "1");
|
// T-Deck Pro: BLE starts disabled for standalone-first operation
|
||||||
the_mesh.getNodePrefs()->gps_enabled = 1;
|
// User can toggle it on from the Bluetooth home page (Enter or long-press)
|
||||||
the_mesh.savePrefs(); // SD backup triggered automatically
|
#if defined(LilyGo_TDeck_Pro) && defined(BLE_PIN_CODE)
|
||||||
MESH_DEBUG_PRINTLN("setup() - GPS enabled by default");
|
serial_interface.disable();
|
||||||
|
MESH_DEBUG_PRINTLN("setup() - BLE disabled at boot (standalone mode)");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
MESH_DEBUG_PRINTLN("=== setup() - COMPLETE ===");
|
MESH_DEBUG_PRINTLN("=== setup() - COMPLETE ===");
|
||||||
@@ -541,7 +542,24 @@ void setup() {
|
|||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
the_mesh.loop();
|
the_mesh.loop();
|
||||||
|
|
||||||
|
// GPS duty cycle — check for fix and manage power state
|
||||||
|
#if HAS_GPS
|
||||||
|
{
|
||||||
|
bool gps_hw_on = gpsDuty.loop();
|
||||||
|
if (gps_hw_on) {
|
||||||
|
LocationProvider* lp = sensors.getLocationProvider();
|
||||||
|
if (lp != NULL && lp->isValid()) {
|
||||||
|
gpsDuty.notifyFix();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
sensors.loop();
|
sensors.loop();
|
||||||
|
|
||||||
|
// CPU frequency auto-timeout back to idle
|
||||||
|
cpuPower.loop();
|
||||||
#ifdef DISPLAY_CLASS
|
#ifdef DISPLAY_CLASS
|
||||||
// Skip UITask rendering when in compose mode to prevent flickering
|
// Skip UITask rendering when in compose mode to prevent flickering
|
||||||
#if defined(LilyGo_TDeck_Pro)
|
#if defined(LilyGo_TDeck_Pro)
|
||||||
@@ -769,28 +787,20 @@ void handleKeyboardInput() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// All other keys pass through to the reader screen
|
// C key: allow entering compose mode from reader
|
||||||
ui_task.injectKey(key);
|
if (key == 'c' || key == 'C') {
|
||||||
return;
|
composeDM = false;
|
||||||
}
|
composeDMContactIdx = -1;
|
||||||
|
composeMode = true;
|
||||||
// *** SETTINGS MODE ***
|
composeBuffer[0] = '\0';
|
||||||
if (ui_task.isOnSettingsScreen()) {
|
composePos = 0;
|
||||||
SettingsScreen* settings = (SettingsScreen*)ui_task.getSettingsScreen();
|
Serial.printf("Entering compose mode from reader, channel %d\n", composeChannelIdx);
|
||||||
|
drawComposeScreen();
|
||||||
// Q key: exit settings (when not editing)
|
lastComposeRefresh = millis();
|
||||||
if (!settings->isEditing() && (key == 'q' || key == 'Q')) {
|
|
||||||
if (settings->hasRadioChanges()) {
|
|
||||||
// Let settings show "apply changes?" confirm dialog
|
|
||||||
ui_task.injectKey(key);
|
|
||||||
} else {
|
|
||||||
Serial.println("Exiting settings");
|
|
||||||
ui_task.gotoHomeScreen();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// All other keys → settings screen via injectKey (no forceRefresh)
|
// All other keys pass through to the reader screen
|
||||||
ui_task.injectKey(key);
|
ui_task.injectKey(key);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -799,11 +809,38 @@ void handleKeyboardInput() {
|
|||||||
switch (key) {
|
switch (key) {
|
||||||
case 'c':
|
case 'c':
|
||||||
case 'C':
|
case 'C':
|
||||||
// Open contacts list
|
// Enter compose mode - DM if on contacts screen, channel otherwise
|
||||||
Serial.println("Opening contacts");
|
if (ui_task.isOnContactsScreen()) {
|
||||||
ui_task.gotoContactsScreen();
|
ContactsScreen* cs = (ContactsScreen*)ui_task.getContactsScreen();
|
||||||
|
int idx = cs->getSelectedContactIdx();
|
||||||
|
uint8_t ctype = cs->getSelectedContactType();
|
||||||
|
if (idx >= 0 && ctype == ADV_TYPE_CHAT) {
|
||||||
|
composeDM = true;
|
||||||
|
composeDMContactIdx = idx;
|
||||||
|
cs->getSelectedContactName(composeDMName, sizeof(composeDMName));
|
||||||
|
composeMode = true;
|
||||||
|
composeBuffer[0] = '\0';
|
||||||
|
composePos = 0;
|
||||||
|
Serial.printf("Entering DM compose to %s (idx %d)\n", composeDMName, idx);
|
||||||
|
drawComposeScreen();
|
||||||
|
lastComposeRefresh = millis();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
composeDM = false;
|
||||||
|
composeDMContactIdx = -1;
|
||||||
|
composeMode = true;
|
||||||
|
composeBuffer[0] = '\0';
|
||||||
|
composePos = 0;
|
||||||
|
// If on channel screen, sync compose channel with viewed channel
|
||||||
|
if (ui_task.isOnChannelScreen()) {
|
||||||
|
composeChannelIdx = ui_task.getChannelScreenViewIdx();
|
||||||
|
}
|
||||||
|
Serial.printf("Entering compose mode, channel %d\n", composeChannelIdx);
|
||||||
|
drawComposeScreen();
|
||||||
|
lastComposeRefresh = millis();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'm':
|
case 'm':
|
||||||
case 'M':
|
case 'M':
|
||||||
// Go to channel message screen
|
// Go to channel message screen
|
||||||
@@ -811,22 +848,18 @@ void handleKeyboardInput() {
|
|||||||
ui_task.gotoChannelScreen();
|
ui_task.gotoChannelScreen();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'e':
|
case 'r':
|
||||||
case 'E':
|
case 'R':
|
||||||
// Open text reader (ebooks)
|
// Open text reader
|
||||||
Serial.println("Opening text reader");
|
Serial.println("Opening text reader");
|
||||||
ui_task.gotoTextReader();
|
ui_task.gotoTextReader();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 's':
|
case 'n':
|
||||||
case 'S':
|
case 'N':
|
||||||
// Open settings (from home), or navigate down on channel/contacts
|
// Open contacts list
|
||||||
if (ui_task.isOnChannelScreen() || ui_task.isOnContactsScreen()) {
|
Serial.println("Opening contacts");
|
||||||
ui_task.injectKey('s'); // Pass directly for channel/contacts scrolling
|
ui_task.gotoContactsScreen();
|
||||||
} else {
|
|
||||||
Serial.println("Opening settings");
|
|
||||||
ui_task.gotoSettingsScreen();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'w':
|
case 'w':
|
||||||
@@ -840,6 +873,17 @@ void handleKeyboardInput() {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 's':
|
||||||
|
case 'S':
|
||||||
|
// Navigate down/next (scroll on channel screen)
|
||||||
|
if (ui_task.isOnChannelScreen() || ui_task.isOnContactsScreen()) {
|
||||||
|
ui_task.injectKey('s'); // Pass directly for channel/contacts switching
|
||||||
|
} else {
|
||||||
|
Serial.println("Nav: Next");
|
||||||
|
ui_task.injectKey(0xF1); // KEY_NEXT
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'a':
|
case 'a':
|
||||||
case 'A':
|
case 'A':
|
||||||
// Navigate left or switch channel (on channel screen)
|
// Navigate left or switch channel (on channel screen)
|
||||||
@@ -863,7 +907,7 @@ void handleKeyboardInput() {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case '\r':
|
case '\r':
|
||||||
// Enter = compose (only from channel or contacts screen)
|
// Select/Enter - if on contacts screen, enter DM compose for chat contacts
|
||||||
if (ui_task.isOnContactsScreen()) {
|
if (ui_task.isOnContactsScreen()) {
|
||||||
ContactsScreen* cs = (ContactsScreen*)ui_task.getContactsScreen();
|
ContactsScreen* cs = (ContactsScreen*)ui_task.getContactsScreen();
|
||||||
int idx = cs->getSelectedContactIdx();
|
int idx = cs->getSelectedContactIdx();
|
||||||
@@ -879,21 +923,12 @@ void handleKeyboardInput() {
|
|||||||
drawComposeScreen();
|
drawComposeScreen();
|
||||||
lastComposeRefresh = millis();
|
lastComposeRefresh = millis();
|
||||||
} else if (idx >= 0) {
|
} else if (idx >= 0) {
|
||||||
|
// Non-chat contact selected (repeater, room, etc.) - future use
|
||||||
Serial.printf("Selected non-chat contact type=%d idx=%d\n", ctype, idx);
|
Serial.printf("Selected non-chat contact type=%d idx=%d\n", ctype, idx);
|
||||||
}
|
}
|
||||||
} else if (ui_task.isOnChannelScreen()) {
|
|
||||||
composeDM = false;
|
|
||||||
composeDMContactIdx = -1;
|
|
||||||
composeChannelIdx = ui_task.getChannelScreenViewIdx();
|
|
||||||
composeMode = true;
|
|
||||||
composeBuffer[0] = '\0';
|
|
||||||
composePos = 0;
|
|
||||||
Serial.printf("Entering compose mode, channel %d\n", composeChannelIdx);
|
|
||||||
drawComposeScreen();
|
|
||||||
lastComposeRefresh = millis();
|
|
||||||
} else {
|
} else {
|
||||||
// Other screens: pass Enter as generic select
|
Serial.println("Nav: Enter/Select");
|
||||||
ui_task.injectKey(13);
|
ui_task.injectKey(13); // KEY_ENTER
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1059,6 +1094,8 @@ void drawEmojiPicker() {
|
|||||||
void sendComposedMessage() {
|
void sendComposedMessage() {
|
||||||
if (composePos == 0) return;
|
if (composePos == 0) return;
|
||||||
|
|
||||||
|
cpuPower.setBoost(); // Boost CPU for crypto + radio TX
|
||||||
|
|
||||||
// Convert escape bytes back to UTF-8 for mesh transmission and BLE app
|
// Convert escape bytes back to UTF-8 for mesh transmission and BLE app
|
||||||
char utf8Buf[512];
|
char utf8Buf[512];
|
||||||
emojiUnescape(composeBuffer, utf8Buf, sizeof(utf8Buf));
|
emojiUnescape(composeBuffer, utf8Buf, sizeof(utf8Buf));
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include <helpers/TxtDataHelpers.h>
|
#include <helpers/TxtDataHelpers.h>
|
||||||
#include "../MyMesh.h"
|
#include "../MyMesh.h"
|
||||||
#include "target.h"
|
#include "target.h"
|
||||||
|
#include "GPSDutyCycle.h"
|
||||||
#ifdef WIFI_SSID
|
#ifdef WIFI_SSID
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -33,7 +34,6 @@
|
|||||||
#include "ChannelScreen.h"
|
#include "ChannelScreen.h"
|
||||||
#include "ContactsScreen.h"
|
#include "ContactsScreen.h"
|
||||||
#include "TextReaderScreen.h"
|
#include "TextReaderScreen.h"
|
||||||
#include "SettingsScreen.h"
|
|
||||||
|
|
||||||
class SplashScreen : public UIScreen {
|
class SplashScreen : public UIScreen {
|
||||||
UITask* _task;
|
UITask* _task;
|
||||||
@@ -329,21 +329,37 @@ public:
|
|||||||
display.drawTextCentered(display.width() / 2, 64 - 11, "advert: " PRESS_LABEL);
|
display.drawTextCentered(display.width() / 2, 64 - 11, "advert: " PRESS_LABEL);
|
||||||
#if ENV_INCLUDE_GPS == 1
|
#if ENV_INCLUDE_GPS == 1
|
||||||
} else if (_page == HomePage::GPS) {
|
} else if (_page == HomePage::GPS) {
|
||||||
|
extern GPSDutyCycle gpsDuty;
|
||||||
|
extern GPSStreamCounter gpsStream;
|
||||||
LocationProvider* nmea = sensors.getLocationProvider();
|
LocationProvider* nmea = sensors.getLocationProvider();
|
||||||
char buf[50];
|
char buf[50];
|
||||||
int y = 18;
|
int y = 18;
|
||||||
bool gps_state = _task->getGPSState();
|
|
||||||
#ifdef PIN_GPS_SWITCH
|
// GPS state line with duty cycle info
|
||||||
bool hw_gps_state = digitalRead(PIN_GPS_SWITCH);
|
if (!_node_prefs->gps_enabled) {
|
||||||
if (gps_state != hw_gps_state) {
|
strcpy(buf, "gps off");
|
||||||
strcpy(buf, gps_state ? "gps off(hw)" : "gps off(sw)");
|
|
||||||
} else {
|
} else {
|
||||||
strcpy(buf, gps_state ? "gps on" : "gps off");
|
switch (gpsDuty.getState()) {
|
||||||
|
case GPSDutyState::ACQUIRING: {
|
||||||
|
uint32_t elapsed = gpsDuty.acquireElapsedSecs();
|
||||||
|
sprintf(buf, "acquiring %us", (unsigned)elapsed);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GPSDutyState::SLEEPING: {
|
||||||
|
uint32_t remain = gpsDuty.sleepRemainingSecs();
|
||||||
|
if (remain >= 60) {
|
||||||
|
sprintf(buf, "sleep %um%02us", (unsigned)(remain / 60), (unsigned)(remain % 60));
|
||||||
|
} else {
|
||||||
|
sprintf(buf, "sleep %us", (unsigned)remain);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
strcpy(buf, "gps off");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
strcpy(buf, gps_state ? "gps on" : "gps off");
|
|
||||||
#endif
|
|
||||||
display.drawTextLeftAlign(0, y, buf);
|
display.drawTextLeftAlign(0, y, buf);
|
||||||
|
|
||||||
if (nmea == NULL) {
|
if (nmea == NULL) {
|
||||||
y = y + 12;
|
y = y + 12;
|
||||||
display.drawTextLeftAlign(0, y, "Can't access GPS");
|
display.drawTextLeftAlign(0, y, "Can't access GPS");
|
||||||
@@ -355,6 +371,19 @@ public:
|
|||||||
sprintf(buf, "%d", nmea->satellitesCount());
|
sprintf(buf, "%d", nmea->satellitesCount());
|
||||||
display.drawTextRightAlign(display.width()-1, y, buf);
|
display.drawTextRightAlign(display.width()-1, y, buf);
|
||||||
y = y + 12;
|
y = y + 12;
|
||||||
|
|
||||||
|
// NMEA sentence counter — confirms baud rate and data flow
|
||||||
|
display.drawTextLeftAlign(0, y, "sentences");
|
||||||
|
if (gpsDuty.isHardwareOn()) {
|
||||||
|
uint16_t sps = gpsStream.getSentencesPerSec();
|
||||||
|
uint32_t total = gpsStream.getSentenceCount();
|
||||||
|
sprintf(buf, "%u/s (%lu)", sps, (unsigned long)total);
|
||||||
|
} else {
|
||||||
|
strcpy(buf, "hw off");
|
||||||
|
}
|
||||||
|
display.drawTextRightAlign(display.width()-1, y, buf);
|
||||||
|
y = y + 12;
|
||||||
|
|
||||||
display.drawTextLeftAlign(0, y, "pos");
|
display.drawTextLeftAlign(0, y, "pos");
|
||||||
sprintf(buf, "%.4f %.4f",
|
sprintf(buf, "%.4f %.4f",
|
||||||
nmea->getLatitude()/1000000., nmea->getLongitude()/1000000.);
|
nmea->getLatitude()/1000000., nmea->getLongitude()/1000000.);
|
||||||
@@ -524,6 +553,12 @@ public:
|
|||||||
|
|
||||||
if (c == KEY_LEFT || c == KEY_PREV) {
|
if (c == KEY_LEFT || c == KEY_PREV) {
|
||||||
_page = (_page + HomePage::Count - 1) % HomePage::Count;
|
_page = (_page + HomePage::Count - 1) % HomePage::Count;
|
||||||
|
#if ENV_INCLUDE_GPS == 1
|
||||||
|
if (_page == HomePage::GPS) {
|
||||||
|
extern GPSDutyCycle gpsDuty;
|
||||||
|
gpsDuty.forceWake();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (c == KEY_NEXT || c == KEY_RIGHT) {
|
if (c == KEY_NEXT || c == KEY_RIGHT) {
|
||||||
@@ -531,6 +566,12 @@ public:
|
|||||||
if (_page == HomePage::RECENT) {
|
if (_page == HomePage::RECENT) {
|
||||||
_task->showAlert("Recent adverts", 800);
|
_task->showAlert("Recent adverts", 800);
|
||||||
}
|
}
|
||||||
|
#if ENV_INCLUDE_GPS == 1
|
||||||
|
if (_page == HomePage::GPS) {
|
||||||
|
extern GPSDutyCycle gpsDuty;
|
||||||
|
gpsDuty.forceWake();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (c == KEY_ENTER && _page == HomePage::BLUETOOTH) {
|
if (c == KEY_ENTER && _page == HomePage::BLUETOOTH) {
|
||||||
@@ -717,7 +758,6 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no
|
|||||||
channel_screen = new ChannelScreen(this, &rtc_clock);
|
channel_screen = new ChannelScreen(this, &rtc_clock);
|
||||||
contacts_screen = new ContactsScreen(this, &rtc_clock);
|
contacts_screen = new ContactsScreen(this, &rtc_clock);
|
||||||
text_reader = new TextReaderScreen(this);
|
text_reader = new TextReaderScreen(this);
|
||||||
settings_screen = new SettingsScreen(this, &rtc_clock, node_prefs);
|
|
||||||
setCurrScreen(splash);
|
setCurrScreen(splash);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1037,39 +1077,36 @@ char UITask::handleTripleClick(char c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool UITask::getGPSState() {
|
bool UITask::getGPSState() {
|
||||||
if (_sensors != NULL) {
|
#if ENV_INCLUDE_GPS == 1
|
||||||
int num = _sensors->getNumSettings();
|
return _node_prefs != NULL && _node_prefs->gps_enabled;
|
||||||
for (int i = 0; i < num; i++) {
|
#else
|
||||||
if (strcmp(_sensors->getSettingName(i), "gps") == 0) {
|
return false;
|
||||||
return !strcmp(_sensors->getSettingValue(i), "1");
|
#endif
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UITask::toggleGPS() {
|
void UITask::toggleGPS() {
|
||||||
|
#if ENV_INCLUDE_GPS == 1
|
||||||
|
extern GPSDutyCycle gpsDuty;
|
||||||
|
|
||||||
if (_sensors != NULL) {
|
if (_sensors != NULL) {
|
||||||
// toggle GPS on/off
|
if (_node_prefs->gps_enabled) {
|
||||||
int num = _sensors->getNumSettings();
|
// Disable GPS — cut hardware power
|
||||||
for (int i = 0; i < num; i++) {
|
_sensors->setSettingValue("gps", "0");
|
||||||
if (strcmp(_sensors->getSettingName(i), "gps") == 0) {
|
_node_prefs->gps_enabled = 0;
|
||||||
if (strcmp(_sensors->getSettingValue(i), "1") == 0) {
|
gpsDuty.disable();
|
||||||
_sensors->setSettingValue("gps", "0");
|
notify(UIEventType::ack);
|
||||||
_node_prefs->gps_enabled = 0;
|
} else {
|
||||||
notify(UIEventType::ack);
|
// Enable GPS — start duty cycle
|
||||||
} else {
|
_sensors->setSettingValue("gps", "1");
|
||||||
_sensors->setSettingValue("gps", "1");
|
_node_prefs->gps_enabled = 1;
|
||||||
_node_prefs->gps_enabled = 1;
|
gpsDuty.enable();
|
||||||
notify(UIEventType::ack);
|
notify(UIEventType::ack);
|
||||||
}
|
|
||||||
the_mesh.savePrefs();
|
|
||||||
showAlert(_node_prefs->gps_enabled ? "GPS: Enabled" : "GPS: Disabled", 800);
|
|
||||||
_next_refresh = 0;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
the_mesh.savePrefs();
|
||||||
|
showAlert(_node_prefs->gps_enabled ? "GPS: Enabled" : "GPS: Disabled", 800);
|
||||||
|
_next_refresh = 0;
|
||||||
}
|
}
|
||||||
}
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void UITask::toggleBuzzer() {
|
void UITask::toggleBuzzer() {
|
||||||
@@ -1157,26 +1194,6 @@ void UITask::gotoTextReader() {
|
|||||||
_next_refresh = 100;
|
_next_refresh = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UITask::gotoSettingsScreen() {
|
|
||||||
((SettingsScreen*)settings_screen)->enter();
|
|
||||||
setCurrScreen(settings_screen);
|
|
||||||
if (_display != NULL && !_display->isOn()) {
|
|
||||||
_display->turnOn();
|
|
||||||
}
|
|
||||||
_auto_off = millis() + AUTO_OFF_MILLIS;
|
|
||||||
_next_refresh = 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UITask::gotoOnboarding() {
|
|
||||||
((SettingsScreen*)settings_screen)->enterOnboarding();
|
|
||||||
setCurrScreen(settings_screen);
|
|
||||||
if (_display != NULL && !_display->isOn()) {
|
|
||||||
_display->turnOn();
|
|
||||||
}
|
|
||||||
_auto_off = millis() + AUTO_OFF_MILLIS;
|
|
||||||
_next_refresh = 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t UITask::getChannelScreenViewIdx() const {
|
uint8_t UITask::getChannelScreenViewIdx() const {
|
||||||
return ((ChannelScreen *) channel_screen)->getViewChannelIdx();
|
return ((ChannelScreen *) channel_screen)->getViewChannelIdx();
|
||||||
}
|
}
|
||||||
|
|||||||
70
variants/lilygo_tdeck_pro/CPUPowerManager.h
Normal file
70
variants/lilygo_tdeck_pro/CPUPowerManager.h
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
// CPU Frequency Scaling for ESP32-S3
|
||||||
|
//
|
||||||
|
// Typical current draw (CPU only, rough):
|
||||||
|
// 240 MHz ~70-80 mA
|
||||||
|
// 160 MHz ~50-60 mA
|
||||||
|
// 80 MHz ~30-40 mA
|
||||||
|
//
|
||||||
|
// SPI peripherals and UART use their own clock dividers from the APB clock,
|
||||||
|
// so LoRa, e-ink, and GPS serial all work fine at 80MHz.
|
||||||
|
|
||||||
|
#ifdef ESP32
|
||||||
|
|
||||||
|
#ifndef CPU_FREQ_IDLE
|
||||||
|
#define CPU_FREQ_IDLE 80 // MHz — normal mesh listening
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CPU_FREQ_BOOST
|
||||||
|
#define CPU_FREQ_BOOST 240 // MHz — heavy processing
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CPU_BOOST_TIMEOUT_MS
|
||||||
|
#define CPU_BOOST_TIMEOUT_MS 10000 // 10 seconds
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class CPUPowerManager {
|
||||||
|
public:
|
||||||
|
CPUPowerManager() : _boosted(false), _boost_started(0) {}
|
||||||
|
|
||||||
|
void begin() {
|
||||||
|
setCpuFrequencyMhz(CPU_FREQ_IDLE);
|
||||||
|
_boosted = false;
|
||||||
|
MESH_DEBUG_PRINTLN("CPU power: idle at %d MHz", CPU_FREQ_IDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if (_boosted && (millis() - _boost_started >= CPU_BOOST_TIMEOUT_MS)) {
|
||||||
|
setIdle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBoost() {
|
||||||
|
if (!_boosted) {
|
||||||
|
setCpuFrequencyMhz(CPU_FREQ_BOOST);
|
||||||
|
_boosted = true;
|
||||||
|
MESH_DEBUG_PRINTLN("CPU power: boosted to %d MHz", CPU_FREQ_BOOST);
|
||||||
|
}
|
||||||
|
_boost_started = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setIdle() {
|
||||||
|
if (_boosted) {
|
||||||
|
setCpuFrequencyMhz(CPU_FREQ_IDLE);
|
||||||
|
_boosted = false;
|
||||||
|
MESH_DEBUG_PRINTLN("CPU power: idle at %d MHz", CPU_FREQ_IDLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isBoosted() const { return _boosted; }
|
||||||
|
uint32_t getFrequencyMHz() const { return getCpuFrequencyMhz(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _boosted;
|
||||||
|
unsigned long _boost_started;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ESP32
|
||||||
185
variants/lilygo_tdeck_pro/GPSDutyCycle.h
Normal file
185
variants/lilygo_tdeck_pro/GPSDutyCycle.h
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "variant.h"
|
||||||
|
#include "GPSStreamCounter.h"
|
||||||
|
|
||||||
|
// GPS Duty Cycle Manager
|
||||||
|
// Controls the hardware GPS enable pin (PIN_GPS_EN) to save power.
|
||||||
|
// When enabled, cycles between acquiring a fix and sleeping with power cut.
|
||||||
|
//
|
||||||
|
// States:
|
||||||
|
// OFF – User has disabled GPS. Hardware power is cut.
|
||||||
|
// ACQUIRING – GPS module powered on, waiting for a fix or timeout.
|
||||||
|
// SLEEPING – GPS module powered off, timer counting down to next cycle.
|
||||||
|
|
||||||
|
#if HAS_GPS
|
||||||
|
|
||||||
|
// How long to leave GPS powered on while acquiring a fix (ms)
|
||||||
|
#ifndef GPS_ACQUIRE_TIMEOUT_MS
|
||||||
|
#define GPS_ACQUIRE_TIMEOUT_MS 60000 // 60 seconds
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// How long to sleep between acquisition cycles (ms)
|
||||||
|
#ifndef GPS_SLEEP_DURATION_MS
|
||||||
|
#define GPS_SLEEP_DURATION_MS 900000 // 15 minutes
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// If we get a fix quickly, power off immediately but still respect
|
||||||
|
// a minimum on-time so the RTC can sync properly
|
||||||
|
#ifndef GPS_MIN_ON_TIME_MS
|
||||||
|
#define GPS_MIN_ON_TIME_MS 5000 // 5 seconds after fix
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum class GPSDutyState : uint8_t {
|
||||||
|
OFF = 0, // User-disabled, hardware power off
|
||||||
|
ACQUIRING, // Hardware on, waiting for fix
|
||||||
|
SLEEPING // Hardware off, timer running
|
||||||
|
};
|
||||||
|
|
||||||
|
class GPSDutyCycle {
|
||||||
|
public:
|
||||||
|
GPSDutyCycle() : _state(GPSDutyState::OFF), _state_entered(0),
|
||||||
|
_last_fix_time(0), _got_fix(false), _time_synced(false),
|
||||||
|
_stream(nullptr) {}
|
||||||
|
|
||||||
|
// Attach the stream counter so we can reset it on power cycles
|
||||||
|
void setStreamCounter(GPSStreamCounter* stream) { _stream = stream; }
|
||||||
|
|
||||||
|
// Call once in setup() after board.begin() and GPS serial init.
|
||||||
|
void begin(bool initial_enable) {
|
||||||
|
if (initial_enable) {
|
||||||
|
_powerOn();
|
||||||
|
_setState(GPSDutyState::ACQUIRING);
|
||||||
|
} else {
|
||||||
|
_powerOff();
|
||||||
|
_setState(GPSDutyState::OFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call every iteration of loop().
|
||||||
|
// Returns true if GPS hardware is currently powered on.
|
||||||
|
bool loop() {
|
||||||
|
switch (_state) {
|
||||||
|
case GPSDutyState::OFF:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case GPSDutyState::ACQUIRING: {
|
||||||
|
unsigned long elapsed = millis() - _state_entered;
|
||||||
|
|
||||||
|
if (_got_fix && elapsed >= GPS_MIN_ON_TIME_MS) {
|
||||||
|
MESH_DEBUG_PRINTLN("GPS duty: fix acquired, powering off for %u min",
|
||||||
|
(unsigned)(GPS_SLEEP_DURATION_MS / 60000));
|
||||||
|
_powerOff();
|
||||||
|
_setState(GPSDutyState::SLEEPING);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elapsed >= GPS_ACQUIRE_TIMEOUT_MS) {
|
||||||
|
MESH_DEBUG_PRINTLN("GPS duty: acquire timeout (%us), sleeping",
|
||||||
|
(unsigned)(GPS_ACQUIRE_TIMEOUT_MS / 1000));
|
||||||
|
_powerOff();
|
||||||
|
_setState(GPSDutyState::SLEEPING);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case GPSDutyState::SLEEPING: {
|
||||||
|
if (millis() - _state_entered >= GPS_SLEEP_DURATION_MS) {
|
||||||
|
MESH_DEBUG_PRINTLN("GPS duty: waking up for next acquisition cycle");
|
||||||
|
_got_fix = false;
|
||||||
|
_powerOn();
|
||||||
|
_setState(GPSDutyState::ACQUIRING);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyFix() {
|
||||||
|
if (_state == GPSDutyState::ACQUIRING && !_got_fix) {
|
||||||
|
_got_fix = true;
|
||||||
|
_last_fix_time = millis();
|
||||||
|
MESH_DEBUG_PRINTLN("GPS duty: fix notification received");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyTimeSync() {
|
||||||
|
_time_synced = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void enable() {
|
||||||
|
if (_state == GPSDutyState::OFF) {
|
||||||
|
_got_fix = false;
|
||||||
|
_powerOn();
|
||||||
|
_setState(GPSDutyState::ACQUIRING);
|
||||||
|
MESH_DEBUG_PRINTLN("GPS duty: enabled, starting acquisition");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable() {
|
||||||
|
_powerOff();
|
||||||
|
_setState(GPSDutyState::OFF);
|
||||||
|
_got_fix = false;
|
||||||
|
MESH_DEBUG_PRINTLN("GPS duty: disabled, power off");
|
||||||
|
}
|
||||||
|
|
||||||
|
void forceWake() {
|
||||||
|
if (_state == GPSDutyState::SLEEPING) {
|
||||||
|
_got_fix = false;
|
||||||
|
_powerOn();
|
||||||
|
_setState(GPSDutyState::ACQUIRING);
|
||||||
|
MESH_DEBUG_PRINTLN("GPS duty: forced wake for user request");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GPSDutyState getState() const { return _state; }
|
||||||
|
bool isHardwareOn() const { return _state == GPSDutyState::ACQUIRING; }
|
||||||
|
bool hadFix() const { return _got_fix; }
|
||||||
|
bool hasTimeSynced() const { return _time_synced; }
|
||||||
|
|
||||||
|
uint32_t sleepRemainingSecs() const {
|
||||||
|
if (_state != GPSDutyState::SLEEPING) return 0;
|
||||||
|
unsigned long elapsed = millis() - _state_entered;
|
||||||
|
if (elapsed >= GPS_SLEEP_DURATION_MS) return 0;
|
||||||
|
return (GPS_SLEEP_DURATION_MS - elapsed) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t acquireElapsedSecs() const {
|
||||||
|
if (_state != GPSDutyState::ACQUIRING) return 0;
|
||||||
|
return (millis() - _state_entered) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void _powerOn() {
|
||||||
|
#ifdef PIN_GPS_EN
|
||||||
|
digitalWrite(PIN_GPS_EN, GPS_EN_ACTIVE);
|
||||||
|
delay(10);
|
||||||
|
#endif
|
||||||
|
if (_stream) _stream->resetCounters();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _powerOff() {
|
||||||
|
#ifdef PIN_GPS_EN
|
||||||
|
digitalWrite(PIN_GPS_EN, !GPS_EN_ACTIVE);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setState(GPSDutyState s) {
|
||||||
|
_state = s;
|
||||||
|
_state_entered = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
GPSDutyState _state;
|
||||||
|
unsigned long _state_entered;
|
||||||
|
unsigned long _last_fix_time;
|
||||||
|
bool _got_fix;
|
||||||
|
bool _time_synced;
|
||||||
|
GPSStreamCounter* _stream;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // HAS_GPS
|
||||||
72
variants/lilygo_tdeck_pro/GPSStreamCounter.h
Normal file
72
variants/lilygo_tdeck_pro/GPSStreamCounter.h
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
// Transparent Stream wrapper that counts NMEA sentences (newline-delimited)
|
||||||
|
// flowing from the GPS serial port to the MicroNMEA parser.
|
||||||
|
//
|
||||||
|
// Usage: Instead of MicroNMEALocationProvider gps(Serial2, &rtc_clock);
|
||||||
|
// Use: GPSStreamCounter gpsStream(Serial2);
|
||||||
|
// MicroNMEALocationProvider gps(gpsStream, &rtc_clock);
|
||||||
|
//
|
||||||
|
// Every read() call passes through to the underlying stream; when a '\n'
|
||||||
|
// is seen the sentence counter increments. This lets the UI display a
|
||||||
|
// live "nmea" count so users can confirm the baud rate is correct and
|
||||||
|
// the GPS module is actually sending data.
|
||||||
|
|
||||||
|
class GPSStreamCounter : public Stream {
|
||||||
|
public:
|
||||||
|
GPSStreamCounter(Stream& inner)
|
||||||
|
: _inner(inner), _sentences(0), _sentences_snapshot(0),
|
||||||
|
_last_snapshot(0), _sentences_per_sec(0) {}
|
||||||
|
|
||||||
|
// --- Stream read interface (passes through) ---
|
||||||
|
int available() override { return _inner.available(); }
|
||||||
|
int peek() override { return _inner.peek(); }
|
||||||
|
|
||||||
|
int read() override {
|
||||||
|
int c = _inner.read();
|
||||||
|
if (c == '\n') {
|
||||||
|
_sentences++;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Stream write interface (pass through for NMEA commands if needed) ---
|
||||||
|
size_t write(uint8_t b) override { return _inner.write(b); }
|
||||||
|
|
||||||
|
// --- Sentence counting API ---
|
||||||
|
|
||||||
|
// Total sentences received since boot (or last reset)
|
||||||
|
uint32_t getSentenceCount() const { return _sentences; }
|
||||||
|
|
||||||
|
// Sentences received per second (updated each time you call it,
|
||||||
|
// with a 1-second rolling window)
|
||||||
|
uint16_t getSentencesPerSec() {
|
||||||
|
unsigned long now = millis();
|
||||||
|
unsigned long elapsed = now - _last_snapshot;
|
||||||
|
if (elapsed >= 1000) {
|
||||||
|
uint32_t delta = _sentences - _sentences_snapshot;
|
||||||
|
// Scale to per-second if interval wasn't exactly 1000ms
|
||||||
|
_sentences_per_sec = (uint16_t)((delta * 1000UL) / elapsed);
|
||||||
|
_sentences_snapshot = _sentences;
|
||||||
|
_last_snapshot = now;
|
||||||
|
}
|
||||||
|
return _sentences_per_sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset all counters (e.g. when GPS hardware power cycles)
|
||||||
|
void resetCounters() {
|
||||||
|
_sentences = 0;
|
||||||
|
_sentences_snapshot = 0;
|
||||||
|
_sentences_per_sec = 0;
|
||||||
|
_last_snapshot = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Stream& _inner;
|
||||||
|
volatile uint32_t _sentences;
|
||||||
|
uint32_t _sentences_snapshot;
|
||||||
|
unsigned long _last_snapshot;
|
||||||
|
uint16_t _sentences_per_sec;
|
||||||
|
};
|
||||||
@@ -17,7 +17,10 @@ ESP32RTCClock fallback_clock;
|
|||||||
AutoDiscoverRTCClock rtc_clock(fallback_clock);
|
AutoDiscoverRTCClock rtc_clock(fallback_clock);
|
||||||
|
|
||||||
#if HAS_GPS
|
#if HAS_GPS
|
||||||
MicroNMEALocationProvider gps(Serial2, &rtc_clock);
|
// Wrap Serial2 with a sentence counter so the UI can show NMEA throughput.
|
||||||
|
// MicroNMEALocationProvider reads through this wrapper transparently.
|
||||||
|
GPSStreamCounter gpsStream(Serial2);
|
||||||
|
MicroNMEALocationProvider gps(gpsStream, &rtc_clock);
|
||||||
EnvironmentSensorManager sensors(gps);
|
EnvironmentSensorManager sensors(gps);
|
||||||
#else
|
#else
|
||||||
SensorManager sensors;
|
SensorManager sensors;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#if HAS_GPS
|
#if HAS_GPS
|
||||||
#include "helpers/sensors/EnvironmentSensorManager.h"
|
#include "helpers/sensors/EnvironmentSensorManager.h"
|
||||||
#include "helpers/sensors/MicroNMEALocationProvider.h"
|
#include "helpers/sensors/MicroNMEALocationProvider.h"
|
||||||
|
#include "GPSStreamCounter.h"
|
||||||
#else
|
#else
|
||||||
#include <helpers/SensorManager.h>
|
#include <helpers/SensorManager.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -27,6 +28,7 @@ extern WRAPPER_CLASS radio_driver;
|
|||||||
extern AutoDiscoverRTCClock rtc_clock;
|
extern AutoDiscoverRTCClock rtc_clock;
|
||||||
|
|
||||||
#if HAS_GPS
|
#if HAS_GPS
|
||||||
|
extern GPSStreamCounter gpsStream;
|
||||||
extern EnvironmentSensorManager sensors;
|
extern EnvironmentSensorManager sensors;
|
||||||
#else
|
#else
|
||||||
extern SensorManager sensors;
|
extern SensorManager sensors;
|
||||||
|
|||||||
Reference in New Issue
Block a user