2022-03-27 14:55:35 +00:00
|
|
|
#include "DeviceTelemetry.h"
|
2023-01-18 08:56:47 -06:00
|
|
|
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
2024-03-17 08:18:30 -05:00
|
|
|
#include "Default.h"
|
2022-03-27 14:55:35 +00:00
|
|
|
#include "MeshService.h"
|
|
|
|
|
#include "NodeDB.h"
|
2022-05-07 20:31:21 +10:00
|
|
|
#include "PowerFSM.h"
|
2022-03-27 14:55:35 +00:00
|
|
|
#include "RTC.h"
|
2024-08-16 17:15:51 -05:00
|
|
|
#include "RadioLibInterface.h"
|
2022-03-27 14:55:35 +00:00
|
|
|
#include "Router.h"
|
|
|
|
|
#include "configuration.h"
|
|
|
|
|
#include "main.h"
|
2025-05-25 11:08:56 -05:00
|
|
|
#include "memGet.h"
|
2022-03-27 14:55:35 +00:00
|
|
|
#include <OLEDDisplay.h>
|
|
|
|
|
#include <OLEDDisplayUi.h>
|
2024-09-25 13:50:00 -05:00
|
|
|
#include <meshUtils.h>
|
2022-03-27 14:55:35 +00:00
|
|
|
|
2023-03-29 13:04:02 -05:00
|
|
|
#define MAGIC_USB_BATTERY_LEVEL 101
|
2023-03-10 19:50:08 -06:00
|
|
|
|
2022-03-27 14:55:35 +00:00
|
|
|
int32_t DeviceTelemetryModule::runOnce()
|
|
|
|
|
{
|
2024-04-14 10:27:01 -05:00
|
|
|
refreshUptime();
|
2024-09-25 13:50:00 -05:00
|
|
|
bool isImpoliteRole =
|
|
|
|
|
IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_SENSOR, meshtastic_Config_DeviceConfig_Role_ROUTER);
|
2023-02-02 09:57:46 +01:00
|
|
|
if (((lastSentToMesh == 0) ||
|
2024-07-13 05:59:19 -05:00
|
|
|
((uptimeLastMs - lastSentToMesh) >=
|
|
|
|
|
Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.device_update_interval,
|
|
|
|
|
default_telemetry_broadcast_interval_secs, numOnlineNodes))) &&
|
2024-08-15 08:47:49 -05:00
|
|
|
airTime->isTxAllowedChannelUtil(!isImpoliteRole) && airTime->isTxAllowedAirUtil() &&
|
2025-10-12 06:30:17 -05:00
|
|
|
config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN &&
|
|
|
|
|
moduleConfig.telemetry.device_telemetry_enabled) {
|
2022-10-11 10:21:30 -05:00
|
|
|
sendTelemetry();
|
2024-04-14 10:27:01 -05:00
|
|
|
lastSentToMesh = uptimeLastMs;
|
2024-08-01 19:29:49 -05:00
|
|
|
} else if (service->isToPhoneQueueEmpty()) {
|
2022-10-11 10:21:30 -05:00
|
|
|
// Just send to phone when it's not our time to send to mesh yet
|
2022-10-16 09:58:58 -05:00
|
|
|
// Only send while queue is empty (phone assumed connected)
|
2022-10-11 10:21:30 -05:00
|
|
|
sendTelemetry(NODENUM_BROADCAST, true);
|
2024-08-16 17:15:51 -05:00
|
|
|
if (lastSentStatsToPhone == 0 || (uptimeLastMs - lastSentStatsToPhone) >= sendStatsToPhoneIntervalMs) {
|
|
|
|
|
sendLocalStatsToPhone();
|
|
|
|
|
lastSentStatsToPhone = uptimeLastMs;
|
|
|
|
|
}
|
2022-03-27 14:55:35 +00:00
|
|
|
}
|
2022-10-11 10:21:30 -05:00
|
|
|
return sendToPhoneIntervalMs;
|
2022-03-27 14:55:35 +00:00
|
|
|
}
|
|
|
|
|
|
2023-01-21 18:22:19 +01:00
|
|
|
bool DeviceTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t)
|
2022-03-27 14:55:35 +00:00
|
|
|
{
|
2023-01-21 18:22:19 +01:00
|
|
|
if (t->which_variant == meshtastic_Telemetry_device_metrics_tag) {
|
2025-07-28 23:51:38 +01:00
|
|
|
#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE)
|
2022-04-24 16:12:25 -05:00
|
|
|
const char *sender = getSenderShortName(mp);
|
2023-01-18 14:51:48 -06:00
|
|
|
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_INFO("(Received from %s): air_util_tx=%f, channel_utilization=%f, battery_level=%i, voltage=%f", sender,
|
2023-01-18 14:51:48 -06:00
|
|
|
t->variant.device_metrics.air_util_tx, t->variant.device_metrics.channel_utilization,
|
|
|
|
|
t->variant.device_metrics.battery_level, t->variant.device_metrics.voltage);
|
2023-05-13 13:33:14 +03:00
|
|
|
#endif
|
2024-03-21 09:06:37 -05:00
|
|
|
nodeDB->updateTelemetry(getFrom(&mp), *t, RX_SRC_RADIO);
|
2022-03-27 14:55:35 +00:00
|
|
|
}
|
|
|
|
|
return false; // Let others look at this message also if they want
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-01 16:18:10 -05:00
|
|
|
meshtastic_MeshPacket *DeviceTelemetryModule::allocReply()
|
|
|
|
|
{
|
2024-06-23 14:13:59 +02:00
|
|
|
if (currentRequest) {
|
|
|
|
|
auto req = *currentRequest;
|
|
|
|
|
const auto &p = req.decoded;
|
|
|
|
|
meshtastic_Telemetry scratch;
|
|
|
|
|
meshtastic_Telemetry *decoded = NULL;
|
|
|
|
|
memset(&scratch, 0, sizeof(scratch));
|
|
|
|
|
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
|
|
|
|
|
decoded = &scratch;
|
|
|
|
|
} else {
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_ERROR("Error decoding DeviceTelemetry module!");
|
2024-06-23 14:13:59 +02:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
// Check for a request for device metrics
|
|
|
|
|
if (decoded->which_variant == meshtastic_Telemetry_device_metrics_tag) {
|
2024-11-04 19:15:59 -06:00
|
|
|
LOG_INFO("Device telemetry reply to request");
|
2024-11-21 15:11:19 -06:00
|
|
|
return allocDataProtobuf(getDeviceTelemetry());
|
|
|
|
|
} else if (decoded->which_variant == meshtastic_Telemetry_local_stats_tag) {
|
|
|
|
|
LOG_INFO("Device telemetry reply w/ LocalStats to request");
|
|
|
|
|
return allocDataProtobuf(getLocalStatsTelemetry());
|
2024-06-23 14:13:59 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
2023-08-01 16:18:10 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry()
|
2022-03-27 14:55:35 +00:00
|
|
|
{
|
2024-06-04 15:14:27 +02:00
|
|
|
meshtastic_Telemetry t = meshtastic_Telemetry_init_zero;
|
2023-01-21 18:22:19 +01:00
|
|
|
t.which_variant = meshtastic_Telemetry_device_metrics_tag;
|
2024-08-14 16:24:28 -05:00
|
|
|
t.time = getTime();
|
2024-08-16 17:15:51 -05:00
|
|
|
t.variant.device_metrics = meshtastic_DeviceMetrics_init_zero;
|
|
|
|
|
t.variant.device_metrics.has_air_util_tx = true;
|
|
|
|
|
t.variant.device_metrics.has_battery_level = true;
|
|
|
|
|
t.variant.device_metrics.has_channel_utilization = true;
|
|
|
|
|
t.variant.device_metrics.has_voltage = true;
|
|
|
|
|
t.variant.device_metrics.has_uptime_seconds = true;
|
|
|
|
|
|
2023-06-08 08:07:32 -05:00
|
|
|
t.variant.device_metrics.air_util_tx = airTime->utilizationTXPercent();
|
2024-09-06 17:19:53 -07:00
|
|
|
t.variant.device_metrics.battery_level = (!powerStatus->getHasBattery() || powerStatus->getIsCharging())
|
|
|
|
|
? MAGIC_USB_BATTERY_LEVEL
|
|
|
|
|
: powerStatus->getBatteryChargePercent();
|
2023-06-08 08:07:32 -05:00
|
|
|
t.variant.device_metrics.channel_utilization = airTime->channelUtilizationPercent();
|
2022-03-28 17:13:22 +00:00
|
|
|
t.variant.device_metrics.voltage = powerStatus->getBatteryVoltageMv() / 1000.0;
|
2024-04-14 10:27:01 -05:00
|
|
|
t.variant.device_metrics.uptime_seconds = getUptimeSeconds();
|
2022-03-27 14:55:35 +00:00
|
|
|
|
2023-08-01 16:18:10 -05:00
|
|
|
return t;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-21 15:11:19 -06:00
|
|
|
meshtastic_Telemetry DeviceTelemetryModule::getLocalStatsTelemetry()
|
2024-08-16 17:15:51 -05:00
|
|
|
{
|
|
|
|
|
meshtastic_Telemetry telemetry = meshtastic_Telemetry_init_zero;
|
|
|
|
|
telemetry.which_variant = meshtastic_Telemetry_local_stats_tag;
|
|
|
|
|
telemetry.variant.local_stats = meshtastic_LocalStats_init_zero;
|
|
|
|
|
telemetry.time = getTime();
|
|
|
|
|
telemetry.variant.local_stats.uptime_seconds = getUptimeSeconds();
|
|
|
|
|
telemetry.variant.local_stats.channel_utilization = airTime->channelUtilizationPercent();
|
|
|
|
|
telemetry.variant.local_stats.air_util_tx = airTime->utilizationTXPercent();
|
|
|
|
|
telemetry.variant.local_stats.num_online_nodes = numOnlineNodes;
|
|
|
|
|
telemetry.variant.local_stats.num_total_nodes = nodeDB->getNumMeshNodes();
|
|
|
|
|
if (RadioLibInterface::instance) {
|
|
|
|
|
telemetry.variant.local_stats.num_packets_tx = RadioLibInterface::instance->txGood;
|
2024-09-15 00:53:27 +02:00
|
|
|
telemetry.variant.local_stats.num_packets_rx = RadioLibInterface::instance->rxGood + RadioLibInterface::instance->rxBad;
|
2024-08-16 17:15:51 -05:00
|
|
|
telemetry.variant.local_stats.num_packets_rx_bad = RadioLibInterface::instance->rxBad;
|
2024-10-04 13:28:51 +02:00
|
|
|
telemetry.variant.local_stats.num_tx_relay = RadioLibInterface::instance->txRelay;
|
2025-10-04 15:13:58 +02:00
|
|
|
telemetry.variant.local_stats.num_tx_dropped = RadioLibInterface::instance->txDrop;
|
2024-10-04 13:28:51 +02:00
|
|
|
}
|
2024-12-03 13:21:24 +01:00
|
|
|
#ifdef ARCH_PORTDUINO
|
|
|
|
|
if (SimRadio::instance) {
|
|
|
|
|
telemetry.variant.local_stats.num_packets_tx = SimRadio::instance->txGood;
|
|
|
|
|
telemetry.variant.local_stats.num_packets_rx = SimRadio::instance->rxGood + SimRadio::instance->rxBad;
|
|
|
|
|
telemetry.variant.local_stats.num_packets_rx_bad = SimRadio::instance->rxBad;
|
|
|
|
|
telemetry.variant.local_stats.num_tx_relay = SimRadio::instance->txRelay;
|
2025-10-04 15:13:58 +02:00
|
|
|
telemetry.variant.local_stats.num_tx_dropped = SimRadio::instance->txDrop;
|
2024-12-03 13:21:24 +01:00
|
|
|
}
|
2025-05-25 11:08:56 -05:00
|
|
|
#else
|
|
|
|
|
telemetry.variant.local_stats.heap_total_bytes = memGet.getHeapSize();
|
|
|
|
|
telemetry.variant.local_stats.heap_free_bytes = memGet.getFreeHeap();
|
2024-12-03 13:21:24 +01:00
|
|
|
#endif
|
2024-10-04 13:28:51 +02:00
|
|
|
if (router) {
|
|
|
|
|
telemetry.variant.local_stats.num_rx_dupe = router->rxDupe;
|
|
|
|
|
telemetry.variant.local_stats.num_tx_relay_canceled = router->txRelayCanceled;
|
2024-08-16 17:15:51 -05:00
|
|
|
}
|
|
|
|
|
|
2024-11-04 19:15:59 -06:00
|
|
|
LOG_INFO("Sending local stats: uptime=%i, channel_utilization=%f, air_util_tx=%f, num_online_nodes=%i, num_total_nodes=%i",
|
2024-10-14 06:11:43 +02:00
|
|
|
telemetry.variant.local_stats.uptime_seconds, telemetry.variant.local_stats.channel_utilization,
|
|
|
|
|
telemetry.variant.local_stats.air_util_tx, telemetry.variant.local_stats.num_online_nodes,
|
|
|
|
|
telemetry.variant.local_stats.num_total_nodes);
|
2024-08-16 17:15:51 -05:00
|
|
|
|
2024-10-14 06:11:43 +02:00
|
|
|
LOG_INFO("num_packets_tx=%i, num_packets_rx=%i, num_packets_rx_bad=%i", telemetry.variant.local_stats.num_packets_tx,
|
2024-08-16 17:15:51 -05:00
|
|
|
telemetry.variant.local_stats.num_packets_rx, telemetry.variant.local_stats.num_packets_rx_bad);
|
|
|
|
|
|
2024-11-21 15:11:19 -06:00
|
|
|
return telemetry;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DeviceTelemetryModule::sendLocalStatsToPhone()
|
|
|
|
|
{
|
|
|
|
|
meshtastic_MeshPacket *p = allocDataProtobuf(getLocalStatsTelemetry());
|
2024-08-16 17:15:51 -05:00
|
|
|
p->to = NODENUM_BROADCAST;
|
|
|
|
|
p->decoded.want_response = false;
|
|
|
|
|
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
|
|
|
|
|
|
|
|
|
|
service->sendToPhone(p);
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-01 16:18:10 -05:00
|
|
|
bool DeviceTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
|
|
|
|
{
|
|
|
|
|
meshtastic_Telemetry telemetry = getDeviceTelemetry();
|
2024-11-04 19:15:59 -06:00
|
|
|
LOG_INFO("Send: air_util_tx=%f, channel_utilization=%f, battery_level=%i, voltage=%f, uptime=%i",
|
2023-08-01 16:18:10 -05:00
|
|
|
telemetry.variant.device_metrics.air_util_tx, telemetry.variant.device_metrics.channel_utilization,
|
2024-04-14 10:27:01 -05:00
|
|
|
telemetry.variant.device_metrics.battery_level, telemetry.variant.device_metrics.voltage,
|
|
|
|
|
telemetry.variant.device_metrics.uptime_seconds);
|
2022-03-27 14:55:35 +00:00
|
|
|
|
2025-09-11 07:57:42 -05:00
|
|
|
DEBUG_HEAP_BEFORE;
|
2023-08-01 16:18:10 -05:00
|
|
|
meshtastic_MeshPacket *p = allocDataProtobuf(telemetry);
|
2025-09-11 07:57:42 -05:00
|
|
|
DEBUG_HEAP_AFTER("DeviceTelemetryModule::sendTelemetry", p);
|
2025-09-09 10:29:07 -05:00
|
|
|
|
2022-03-27 14:55:35 +00:00
|
|
|
p->to = dest;
|
2022-10-11 10:21:30 -05:00
|
|
|
p->decoded.want_response = false;
|
2024-03-12 18:21:09 +01:00
|
|
|
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
|
2022-03-27 14:55:35 +00:00
|
|
|
|
2024-03-21 09:06:37 -05:00
|
|
|
nodeDB->updateTelemetry(nodeDB->getNodeNum(), telemetry, RX_SRC_LOCAL);
|
2022-10-11 10:21:30 -05:00
|
|
|
if (phoneOnly) {
|
2024-11-04 12:16:25 -06:00
|
|
|
LOG_INFO("Send packet to phone");
|
2024-08-01 19:29:49 -05:00
|
|
|
service->sendToPhone(p);
|
2022-10-11 10:21:30 -05:00
|
|
|
} else {
|
2024-11-04 12:16:25 -06:00
|
|
|
LOG_INFO("Send packet to mesh");
|
2024-08-01 19:29:49 -05:00
|
|
|
service->sendToMesh(p, RX_SRC_LOCAL, true);
|
2022-10-11 10:21:30 -05:00
|
|
|
}
|
2022-03-27 14:55:35 +00:00
|
|
|
return true;
|
2024-06-23 14:13:59 +02:00
|
|
|
}
|