mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-11-04 09:47:16 +00:00
Fix Cardputer delivery, live settings and power management
Keep selected chats bound to full contact keys and route keyboard channel sends through the active transport. Deliver accepted plain, room and Meshtext messages to local history and notifications once; disarm rejected sends and retries. Use shared live radio settings, GPS acquisition and power-saving policy, screen timers and red night palette. Report save failures, preserve verified custom preferences, and guard display controls and shutdown. Add executable sanitizer regressions for routing, retries, selection, storage faults, UI lifecycle and GPS. Both Companion builds and the shared T-Deck regression pass; no physical Cardputer testing performed.
This commit is contained in:
1 parent
e5d5981e5e
commit
9a2211e85a
27 files changed
+1950
-346
No files matched your search
@@ -1,110 +1,156 @@
|
||||
#include "CardputerSensorManager.h"
|
||||
|
||||
namespace {
|
||||
const uint32_t STANDBY_RENEW_MS = 60UL * 60UL * 1000UL;
|
||||
bool due(uint32_t now, uint32_t deadline) {
|
||||
return static_cast<int32_t>(now - deadline) >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CardputerSensorManager::armGpsPowerSavingCycle() {
|
||||
if (!powersaving_enabled || !_location->getGPSPowerSaving()) return;
|
||||
_location->syncTimeForPowerSavingCycle();
|
||||
_location->setNextGPSOn(0);
|
||||
_location->setNextSleep();
|
||||
// The provider uses zero as "no deadline", including at millis rollover.
|
||||
if (static_cast<uint32_t>(_location->getNextGPSOff()) == 0) _location->setNextGPSOff(1);
|
||||
}
|
||||
|
||||
void CardputerSensorManager::start_gps() {
|
||||
if (!gps_active) {
|
||||
gps_standby(false);
|
||||
delay(1000);
|
||||
gps_active = true;
|
||||
}
|
||||
if (!gps_serial_open || gps_active) return;
|
||||
gps_standby(false);
|
||||
gps_active = true;
|
||||
// Do not turn a retained pre-sleep fix into a new telemetry observation.
|
||||
// Receiver startup is asynchronous; the normal bounded parser polls it.
|
||||
_location->clearPositionFix();
|
||||
_location->begin();
|
||||
next_gps_update = millis();
|
||||
if (!_location->getGPSPowerSaving()) _location->syncTimeForPowerSavingCycle();
|
||||
armGpsPowerSavingCycle();
|
||||
}
|
||||
|
||||
void CardputerSensorManager::stop_gps() {
|
||||
if (gps_active) {
|
||||
gps_standby(true);
|
||||
gps_active = false;
|
||||
}
|
||||
if (!gps_active) return;
|
||||
gps_standby(true);
|
||||
gps_active = false;
|
||||
_location->stopTimeSync();
|
||||
_location->setNextGPSOff(0);
|
||||
if (powersaving_enabled && _location->getGPSPowerSaving()
|
||||
&& isGpsTelemetryUserEnabled()) _location->setNextWake();
|
||||
else _location->setNextGPSOn(0);
|
||||
_location->stop();
|
||||
}
|
||||
|
||||
void CardputerSensorManager::gps_standby(bool active) {
|
||||
if (active) {
|
||||
// Cap Lora-1262 does not have GPS switch pin
|
||||
// Instead we send a command to the GPS module to put it into standby for maximum possible value
|
||||
// (65535 seconds)
|
||||
Serial1.print("$PCAS12,65535*1E\r\n");
|
||||
} else {
|
||||
// Hot reboot to exit standby mode
|
||||
Serial1.print("$PCAS10,0*1C\r\n");
|
||||
}
|
||||
// CASIC protocol sections CAS10/CAS12: hot start and standby for at most
|
||||
// 65535 seconds. The provider's MicroNMEA sender generates the checksum.
|
||||
_location->sendSentence(active ? "$PCAS12,65535" : "$PCAS10,0");
|
||||
standby_renew_at = static_cast<uint32_t>(millis()) + STANDBY_RENEW_MS;
|
||||
}
|
||||
|
||||
bool CardputerSensorManager::begin() {
|
||||
if (gps_serial_open) return true;
|
||||
// Existing board macros name the cap's RX/TX nets; setPins takes MCU RX/TX.
|
||||
Serial1.setPins(PIN_GPS_TX, PIN_GPS_RX);
|
||||
Serial1.begin(GPS_BAUD_RATE);
|
||||
|
||||
delay(1000);
|
||||
|
||||
// Check data available to determine if GPS active
|
||||
if (Serial1.available() > 0) {
|
||||
start_gps();
|
||||
}
|
||||
|
||||
gps_serial_open = true;
|
||||
gps_active = false;
|
||||
_location->setGPSPowerSaving(false);
|
||||
_location->stopTimeSync();
|
||||
_location->stop();
|
||||
// A fixed cap powers GNSS at boot even when the saved preference is off.
|
||||
// Silence it explicitly; UART traffic during startup is not a user choice.
|
||||
gps_standby(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CardputerSensorManager::querySensors(uint8_t requester_permissions, CayenneLPP &telemetry) {
|
||||
if (requester_permissions & TELEM_PERM_LOCATION) { // does requester have permission?
|
||||
telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude);
|
||||
}
|
||||
// Shared cache, permissions, bounded acquisition and manual-off cancellation.
|
||||
queryGpsTelemetry(requester_permissions, telemetry);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CardputerSensorManager::loop() {
|
||||
static long next_gps_update = 0;
|
||||
static long next_gps_force_shutdown = 0;
|
||||
|
||||
// periodically send standby command because standby has maximum of 18 hours
|
||||
if (millis() > next_gps_force_shutdown) {
|
||||
if (!gps_active) {
|
||||
gps_standby(true);
|
||||
if (!gps_serial_open) return;
|
||||
const uint32_t now = millis();
|
||||
loopGpsTelemetry(now);
|
||||
if (powersaving_enabled && isGpsTelemetryUserEnabled()
|
||||
&& !isTrackerGpsModeEnabled() && _location->getGPSPowerSaving()) {
|
||||
const uint32_t next_off = _location->getNextGPSOff();
|
||||
const uint32_t next_on = _location->getNextGPSOn();
|
||||
if (gps_active && !gpsTelemetryReceiverRequired(now)
|
||||
&& ((next_off != 0 && due(now, next_off)) || !_location->waitingTimeSync())) {
|
||||
stop_gps();
|
||||
} else if (!gps_active && ((next_on != 0 && due(now, next_on))
|
||||
|| _location->waitingTimeSync())) {
|
||||
start_gps();
|
||||
}
|
||||
next_gps_force_shutdown = millis() + 1000 * 60 * 60; // every hour
|
||||
}
|
||||
|
||||
if (!gps_active) {
|
||||
// CAS12 eventually wakes on its own. Renew off/standby without changing
|
||||
// saved intent or creating a GPS telemetry/clock acquisition.
|
||||
if (due(now, standby_renew_at)) gps_standby(true);
|
||||
return;
|
||||
}
|
||||
|
||||
_location->loop();
|
||||
|
||||
if (millis() > next_gps_update) {
|
||||
if (due(now, next_gps_update)) {
|
||||
if (_location->isValid()) {
|
||||
node_lat = ((double)_location->getLatitude()) / 1000000.;
|
||||
node_lon = ((double)_location->getLongitude()) / 1000000.;
|
||||
node_altitude = ((double)_location->getAltitude()) / 1000.0;
|
||||
MESH_DEBUG_PRINTLN("VALID location: lat %f lon %f", node_lat, node_lon);
|
||||
} else {
|
||||
MESH_DEBUG_PRINTLN("INVALID location, waiting for fix");
|
||||
node_lat = static_cast<double>(_location->getLatitude()) / 1000000.0;
|
||||
node_lon = static_cast<double>(_location->getLongitude()) / 1000000.0;
|
||||
node_altitude = static_cast<double>(_location->getAltitude()) / 1000.0;
|
||||
processGpsTelemetryFix(node_lat, node_lon, node_altitude, now);
|
||||
}
|
||||
MESH_DEBUG_PRINTLN("GPS satellites: %d", _location->satellitesCount());
|
||||
next_gps_update = millis() + 1000;
|
||||
next_gps_update = now + getGpsUpdateIntervalMillis();
|
||||
}
|
||||
}
|
||||
|
||||
int CardputerSensorManager::getNumSettings() const {
|
||||
return 1;
|
||||
}
|
||||
int CardputerSensorManager::getNumSettings() const { return 1; }
|
||||
|
||||
const char *CardputerSensorManager::getSettingName(int i) const {
|
||||
return i == 0 ? "gps" : NULL;
|
||||
return i == 0 ? "gps" : nullptr;
|
||||
}
|
||||
|
||||
const char *CardputerSensorManager::getSettingValue(int i) const {
|
||||
if (i == 0) {
|
||||
return gps_active ? "1" : "0";
|
||||
}
|
||||
return NULL;
|
||||
return i == 0 ? (isGpsTelemetryUserEnabled() ? "1" : "0") : nullptr;
|
||||
}
|
||||
|
||||
bool CardputerSensorManager::setSettingValue(const char *name, const char *value) {
|
||||
if (!name || !value) return false;
|
||||
if (strcmp(name, "gps") == 0) {
|
||||
if (strcmp(value, "0") == 0) {
|
||||
stop_gps();
|
||||
} else {
|
||||
start_gps();
|
||||
const bool enabled = strcmp(value, "0") != 0;
|
||||
const bool was_active = gps_active;
|
||||
_location->setGPSPowerSaving(enabled && powersaving_enabled);
|
||||
setGpsTelemetryUserEnabled(enabled);
|
||||
if (enabled && powersaving_enabled && was_active) armGpsPowerSavingCycle();
|
||||
if (!enabled) {
|
||||
_location->stopTimeSync();
|
||||
_location->setNextGPSOn(0);
|
||||
_location->setNextGPSOff(0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false; // not supported
|
||||
const bool accepted = SensorManager::setSettingValue(name, value);
|
||||
if (accepted && strcmp(name, "gps_interval") == 0) next_gps_update = millis();
|
||||
return accepted;
|
||||
}
|
||||
|
||||
void CardputerSensorManager::setPowerSavingEnabled(bool enabled) {
|
||||
if (powersaving_enabled == enabled) return;
|
||||
powersaving_enabled = enabled;
|
||||
if (isTrackerGpsModeEnabled()) {
|
||||
_location->setGPSPowerSaving(false);
|
||||
return;
|
||||
}
|
||||
const bool user_enabled = isGpsTelemetryUserEnabled();
|
||||
_location->setGPSPowerSaving(enabled && user_enabled);
|
||||
if (!user_enabled) return;
|
||||
if (!gps_active) start_gps();
|
||||
else if (enabled) armGpsPowerSavingCycle();
|
||||
}
|
||||
|
||||
void CardputerSensorManager::shutdownGpsTransportForDeepSleep() {
|
||||
if (!gps_serial_open) return;
|
||||
stop_gps();
|
||||
Serial1.end();
|
||||
gps_serial_open = false;
|
||||
}
|
||||
Reference in new issue
Block a user