mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-10-15 08:50:15 +00:00
peripherals & power: GPS fixes, poweroff/pwrmgt, upstream SensorManager telemetry
- gps: servers honour gps_enabled at boot (was lost on reboot; zc.gps_set upgrades older prefs to on once); sleep lock only while acquiring; gps off works on boards without a power line (UART sleep); UI toggles and fixes handled on the main thread; configure only after a good init; observer parks its GPS; one interval clamp (clampGpsInterval); one nav-mode knob (room servers on Air530Z now stationary); GNSS UARTE from DT - gps: split into ZephyrGPSManager / gps_power / gps_module_cfg (+gps_internal.h); dead driver branches, gps_get_diag, sats_* removed - clock: ZephyrRTCClock writes the hardware RTC itself (clkreboot now resets it); MESH_EVENT_RTC_SAVE gone - power: one power-off path (zephcore_power_off, ZephyrBoard::powerOff), poweroff/shutdown CLI, get pwrmgt.*, crash record kept across reboot, battery reading cached 10 s, fuel gauge on the current API - telemetry: upstream SensorManager + CayenneLPP (rounded to nearest), one builder for all roles; per-sensor channels, MCU temp always, guest gating and inverse mask on servers; sensor list/get/set work; BME280/BMP280 also probed at 0x77 - CLI: gps in upstream's form, get gps detail, gps sync
This commit is contained in:
+27
-138
@@ -8,8 +8,7 @@
|
||||
#include <helpers/TxtDataHelpers.h>
|
||||
#include <helpers/MeshcoreJson.h>
|
||||
#include <adapters/radio/LoRaRadio.h>
|
||||
#include <adapters/sensors/SimpleLPP.h>
|
||||
#include <adapters/sensors/ZephyrEnvSensors.h>
|
||||
#include <adapters/sensors/ZephyrSensorManager.h>
|
||||
#include <adapters/gps/ZephyrGPSManager.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
@@ -413,86 +412,33 @@ int RepeaterMesh::handleRequest(ClientInfo* sender, uint32_t sender_timestamp, u
|
||||
}
|
||||
|
||||
if (payload[0] == REQ_TYPE_GET_TELEMETRY_DATA) {
|
||||
/* CayenneLPP telemetry response using SimpleLPP encoder */
|
||||
SimpleLPP lpp(&reply_data[4], sizeof(reply_data) - 4);
|
||||
uint8_t perm_mask = ~(payload[1]); // NEW: first reserved byte (of 4), is now inverse mask to apply to permissions
|
||||
|
||||
/* Battery voltage — channel 1 = TELEM_CHANNEL_SELF (matches Arduino) */
|
||||
const uint8_t CH_SELF = 1;
|
||||
uint16_t batt_mv = _board.getBattMilliVolts();
|
||||
lpp.addVoltage(CH_SELF, batt_mv / 1000.0f);
|
||||
telemetry.reset();
|
||||
telemetry.addVoltage(TELEM_CHANNEL_SELF, (float)_board.getBattMilliVolts() / 1000.0f);
|
||||
|
||||
/* Environment sensors — prefer external, fallback to MCU die temp */
|
||||
struct env_data env;
|
||||
if (env_sensors_read(&env) == 0) {
|
||||
if (env.has_temperature) {
|
||||
lpp.addTemperature(CH_SELF, env.temperature_c);
|
||||
} else if (env.has_mcu_temperature) {
|
||||
lpp.addTemperature(CH_SELF, env.mcu_temperature_c);
|
||||
} else {
|
||||
/* Last resort: MCU temp from board API */
|
||||
float mcu_temp = _board.getMCUTemperature();
|
||||
if (!isnan(mcu_temp)) {
|
||||
lpp.addTemperature(CH_SELF, mcu_temp);
|
||||
}
|
||||
}
|
||||
if (env.has_humidity) {
|
||||
lpp.addRelativeHumidity(CH_SELF, env.humidity_pct);
|
||||
}
|
||||
if (env.has_pressure) {
|
||||
lpp.addBarometricPressure(CH_SELF, env.pressure_hpa);
|
||||
}
|
||||
if (env.has_luminosity) {
|
||||
lpp.addLuminosity(CH_SELF, env.luminosity);
|
||||
}
|
||||
} else {
|
||||
/* No env sensors at all — try MCU temp directly */
|
||||
float mcu_temp = _board.getMCUTemperature();
|
||||
if (!isnan(mcu_temp)) {
|
||||
lpp.addTemperature(CH_SELF, mcu_temp);
|
||||
}
|
||||
// query other sensors -- target specific
|
||||
if ((sender->permissions & PERM_ACL_ROLE_MASK) == PERM_ACL_GUEST) {
|
||||
perm_mask = 0x00; // just base telemetry allowed
|
||||
}
|
||||
sensors.querySensors(perm_mask, telemetry);
|
||||
|
||||
// This default temperature will be overridden by external sensors (if any)
|
||||
float temperature = _board.getMCUTemperature();
|
||||
if(!isnan(temperature)) { // Supported boards with built-in temperature sensor. ESP32-C3 may return NAN
|
||||
telemetry.addTemperature(TELEM_CHANNEL_SELF, temperature); // Built-in MCU Temperature
|
||||
}
|
||||
|
||||
/* Power monitors (INA219/INA3221/ina2xx) */
|
||||
if (power_sensors_available()) {
|
||||
struct power_data pwr;
|
||||
if (power_sensors_read(&pwr) == 0) {
|
||||
uint8_t ch = CH_SELF + 1;
|
||||
for (int j = 0; j < pwr.num_channels; j++) {
|
||||
if (pwr.channels[j].valid) {
|
||||
lpp.addVoltage(ch, pwr.channels[j].voltage_v);
|
||||
lpp.addCurrent(ch, pwr.channels[j].current_a);
|
||||
lpp.addPower(ch, pwr.channels[j].power_w);
|
||||
ch++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// ZEPHCORE: wake the GPS (or extend its acquire window) so the next poll
|
||||
// has a fresher fix. A server's GPS is normally asleep between its 48 h
|
||||
// time syncs. Gated like the position, so a guest cannot keep it awake.
|
||||
if ((perm_mask & TELEM_PERM_LOCATION) && gps_is_enabled()) {
|
||||
gps_request_fresh_fix();
|
||||
}
|
||||
|
||||
/* GPS precise position — admin-only, and only via telemetry, never
|
||||
* adverts. Guests get the rest of the LPP payload but no position:
|
||||
* adverts already publish the operator-set prefs coordinates, so
|
||||
* there is no reason to hand a guest login the live fix as well. */
|
||||
if (sender->isAdmin()) {
|
||||
struct gps_position gpos;
|
||||
if (gps_get_last_known_position(&gpos)) {
|
||||
lpp.addGPS(CH_SELF,
|
||||
(float)(gpos.latitude_ndeg / 1e9),
|
||||
(float)(gpos.longitude_ndeg / 1e9),
|
||||
gpos.altitude_mm / 1000.0f);
|
||||
}
|
||||
|
||||
/* Wake GPS / extend acquire window so the next telemetry poll has
|
||||
* a fresher fix. In repeater mode GPS is normally off between the
|
||||
* 48h time-sync cycles — this opportunistically rearms acquire
|
||||
* when someone actually cares about our position. No-op if GPS
|
||||
* is disabled in prefs. Gated with the position itself so a guest
|
||||
* can't hold the GPS awake by polling. */
|
||||
if (gps_is_available() && gps_is_enabled()) {
|
||||
gps_request_fresh_fix();
|
||||
}
|
||||
}
|
||||
|
||||
return 4 + lpp.getSize();
|
||||
uint8_t tlen = telemetry.getSize();
|
||||
memcpy(&reply_data[4], telemetry.getBuffer(), tlen);
|
||||
return 4 + tlen; // reply_len
|
||||
}
|
||||
|
||||
if (payload[0] == REQ_TYPE_GET_ACCESS_LIST && sender->isAdmin()) {
|
||||
@@ -1082,7 +1028,7 @@ RepeaterMesh::RepeaterMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Mil
|
||||
mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables)
|
||||
: mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(), tables),
|
||||
_board(board),
|
||||
_cli(board, rtc, ®ion_map, &acl, &_prefs, this),
|
||||
_cli(board, rtc, sensors, ®ion_map, &acl, &_prefs, this),
|
||||
region_map(key_store), temp_map(key_store),
|
||||
discover_limiter(4, 120),
|
||||
anon_limiter(4, 180),
|
||||
@@ -1091,7 +1037,8 @@ RepeaterMesh::RepeaterMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Mil
|
||||
* attempts hit the cap quickly and trip the LOG_WRN below. Global rate
|
||||
* (not per-sender) — trade-off documented in CRYPTO_AUDIT_INDEX.md
|
||||
* Phase 4 (mitigation for upstream MeshCore#2556). */
|
||||
login_fail_limiter(4, 180) {
|
||||
login_fail_limiter(4, 180),
|
||||
telemetry(MAX_PACKET_PAYLOAD - 4) {
|
||||
|
||||
_store = nullptr;
|
||||
last_millis = 0;
|
||||
@@ -1118,6 +1065,8 @@ RepeaterMesh::RepeaterMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Mil
|
||||
_prefs.advert_loc_policy = ADVERT_LOC_PREFS; // Repeaters always advertise prefs coordinates
|
||||
_prefs.loop_detect = LOOP_DETECT_MODERATE;
|
||||
_prefs.gps_interval = CONFIG_ZEPHCORE_REPEATER_GPS_INTERVAL_SEC; // ZEPHCORE: 48 h, a fix for time sync
|
||||
_prefs.gps_enabled = 1; // ZEPHCORE: GPS on for time sync (upstream: 0)
|
||||
_prefs.gps_enabled_set = 1;
|
||||
/* path_hash_mode = 1 moved into initNodePrefs() -- it is the default for
|
||||
* every role, and a default listed only at one call site is invisible to
|
||||
* the others (the companion never got it). */
|
||||
@@ -1546,66 +1495,6 @@ void RepeaterMesh::loop() {
|
||||
|
||||
/* ---- ZEPHCORE: methods upstream does not have ---- */
|
||||
|
||||
double RepeaterMesh::getNodeLat() const {
|
||||
struct gps_position pos;
|
||||
if (gps_get_last_known_position(&pos)) {
|
||||
return pos.latitude_ndeg / 1e9;
|
||||
}
|
||||
return _prefs.node_lat;
|
||||
}
|
||||
|
||||
double RepeaterMesh::getNodeLon() const {
|
||||
struct gps_position pos;
|
||||
if (gps_get_last_known_position(&pos)) {
|
||||
return pos.longitude_ndeg / 1e9;
|
||||
}
|
||||
return _prefs.node_lon;
|
||||
}
|
||||
|
||||
bool RepeaterMesh::setGpsEnabled(bool enabled) {
|
||||
if (!gps_is_available()) return false;
|
||||
gps_enable(enabled);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RepeaterMesh::isGpsEnabled() const {
|
||||
return gps_is_enabled();
|
||||
}
|
||||
|
||||
void RepeaterMesh::formatGpsStatsReply(char* reply) {
|
||||
if (!gps_is_enabled()) {
|
||||
strcpy(reply, "off");
|
||||
return;
|
||||
}
|
||||
|
||||
struct gps_state_info gsi;
|
||||
gps_get_state_info(&gsi);
|
||||
|
||||
static const char* const state_str[] = { "off", "standby", "acquiring" };
|
||||
const char* state = gsi.state < 3 ? state_str[gsi.state] : "unknown";
|
||||
|
||||
struct gps_position pos;
|
||||
bool has_pos = gps_get_last_known_position(&pos);
|
||||
|
||||
if (has_pos) {
|
||||
snprintf(reply, CLI_REPLY_SIZE,
|
||||
"on state=%s sats=%u fix=%us ago lat=%.6f lon=%.6f",
|
||||
state, gsi.satellites, gsi.last_fix_age_s,
|
||||
pos.latitude_ndeg / 1e9, pos.longitude_ndeg / 1e9);
|
||||
} else if (gsi.next_search_s > 0) {
|
||||
snprintf(reply, CLI_REPLY_SIZE,
|
||||
"on state=%s sats=%u no fix next=%us",
|
||||
state, gsi.satellites, gsi.next_search_s);
|
||||
} else {
|
||||
snprintf(reply, CLI_REPLY_SIZE,
|
||||
"on state=%s sats=%u no fix",
|
||||
state, gsi.satellites);
|
||||
}
|
||||
|
||||
/* Per-constellation tally is deliberately NOT appended here — "get gps"
|
||||
* has to fit a LoRa reply. It lives in "get gps diag" instead. */
|
||||
}
|
||||
|
||||
void RepeaterMesh::savePrefs() {
|
||||
if (_store) {
|
||||
_store->savePrefs(_prefs);
|
||||
|
||||
Reference in New Issue
Block a user