2022-03-27 14:55:35 +00:00
|
|
|
#include "EnvironmentTelemetry.h"
|
2023-01-18 14:56:47 +00:00
|
|
|
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
2022-03-27 14:55:35 +00:00
|
|
|
#include "MeshService.h"
|
|
|
|
#include "NodeDB.h"
|
2022-05-07 10:31:21 +00:00
|
|
|
#include "PowerFSM.h"
|
2022-03-27 14:55:35 +00:00
|
|
|
#include "RTC.h"
|
|
|
|
#include "Router.h"
|
|
|
|
#include "configuration.h"
|
|
|
|
#include "main.h"
|
|
|
|
#include <OLEDDisplay.h>
|
|
|
|
#include <OLEDDisplayUi.h>
|
|
|
|
|
|
|
|
// Sensors
|
|
|
|
#include "Sensor/BME280Sensor.h"
|
|
|
|
#include "Sensor/BME680Sensor.h"
|
2023-01-18 20:51:48 +00:00
|
|
|
#include "Sensor/BMP280Sensor.h"
|
2022-06-11 21:44:56 +00:00
|
|
|
#include "Sensor/INA219Sensor.h"
|
2023-01-18 20:51:48 +00:00
|
|
|
#include "Sensor/INA260Sensor.h"
|
2022-10-15 19:55:57 +00:00
|
|
|
#include "Sensor/LPS22HBSensor.h"
|
2023-01-18 20:51:48 +00:00
|
|
|
#include "Sensor/MCP9808Sensor.h"
|
2022-12-28 14:30:23 +00:00
|
|
|
#include "Sensor/SHT31Sensor.h"
|
2023-01-18 20:51:48 +00:00
|
|
|
#include "Sensor/SHTC3Sensor.h"
|
2022-06-11 21:44:56 +00:00
|
|
|
|
2022-07-31 13:52:47 +00:00
|
|
|
BMP280Sensor bmp280Sensor;
|
2022-03-27 14:55:35 +00:00
|
|
|
BME280Sensor bme280Sensor;
|
|
|
|
BME680Sensor bme680Sensor;
|
|
|
|
MCP9808Sensor mcp9808Sensor;
|
2022-06-11 21:44:56 +00:00
|
|
|
INA260Sensor ina260Sensor;
|
|
|
|
INA219Sensor ina219Sensor;
|
2022-10-15 14:11:05 +00:00
|
|
|
SHTC3Sensor shtc3Sensor;
|
2022-10-15 19:55:57 +00:00
|
|
|
LPS22HBSensor lps22hbSensor;
|
2022-12-28 14:30:23 +00:00
|
|
|
SHT31Sensor sht31Sensor;
|
2022-03-27 14:55:35 +00:00
|
|
|
|
|
|
|
#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
|
|
|
|
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
|
|
|
|
|
2022-07-31 12:11:47 +00:00
|
|
|
#ifdef USE_EINK
|
2022-03-27 14:55:35 +00:00
|
|
|
// The screen is bigger so use bigger fonts
|
|
|
|
#define FONT_SMALL ArialMT_Plain_16
|
|
|
|
#define FONT_MEDIUM ArialMT_Plain_24
|
|
|
|
#define FONT_LARGE ArialMT_Plain_24
|
|
|
|
#else
|
|
|
|
#define FONT_SMALL ArialMT_Plain_10
|
|
|
|
#define FONT_MEDIUM ArialMT_Plain_16
|
|
|
|
#define FONT_LARGE ArialMT_Plain_24
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define fontHeight(font) ((font)[1] + 1) // height is position 1
|
|
|
|
|
|
|
|
#define FONT_HEIGHT_SMALL fontHeight(FONT_SMALL)
|
|
|
|
#define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM)
|
|
|
|
|
|
|
|
int32_t EnvironmentTelemetryModule::runOnce()
|
|
|
|
{
|
2022-07-31 12:11:47 +00:00
|
|
|
#ifndef ARCH_PORTDUINO
|
2022-06-05 14:50:06 +00:00
|
|
|
int32_t result = INT32_MAX;
|
2022-03-27 14:55:35 +00:00
|
|
|
/*
|
|
|
|
Uncomment the preferences below if you want to use the module
|
|
|
|
without having to configure it from the PythonAPI or WebUI.
|
|
|
|
*/
|
2023-01-18 20:51:48 +00:00
|
|
|
|
2022-07-29 17:39:46 +00:00
|
|
|
// moduleConfig.telemetry.environment_measurement_enabled = 1;
|
|
|
|
// moduleConfig.telemetry.environment_screen_enabled = 1;
|
|
|
|
// moduleConfig.telemetry.environment_update_interval = 45;
|
2022-05-07 10:31:21 +00:00
|
|
|
|
2023-01-18 20:51:48 +00:00
|
|
|
if (!(moduleConfig.telemetry.environment_measurement_enabled || moduleConfig.telemetry.environment_screen_enabled)) {
|
2022-03-27 14:55:35 +00:00
|
|
|
// If this module is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it
|
2022-12-30 00:48:33 +00:00
|
|
|
return disable();
|
2022-03-27 14:55:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (firstTime) {
|
|
|
|
// This is the first time the OSThread library has called this function, so do some setup
|
|
|
|
firstTime = 0;
|
|
|
|
|
2022-05-22 11:27:56 +00:00
|
|
|
if (moduleConfig.telemetry.environment_measurement_enabled) {
|
2022-12-30 16:27:07 +00:00
|
|
|
LOG_INFO("Environment Telemetry: Initializing\n");
|
2022-03-27 14:55:35 +00:00
|
|
|
// it's possible to have this module enabled, only for displaying values on the screen.
|
|
|
|
// therefore, we should only enable the sensor loop if measurement is also enabled
|
2023-01-18 20:51:48 +00:00
|
|
|
if (bmp280Sensor.hasSensor())
|
2022-07-31 13:52:47 +00:00
|
|
|
result = bmp280Sensor.runOnce();
|
2023-01-18 20:51:48 +00:00
|
|
|
if (bme280Sensor.hasSensor())
|
2022-06-05 14:50:06 +00:00
|
|
|
result = bme280Sensor.runOnce();
|
2023-01-18 20:51:48 +00:00
|
|
|
if (bme680Sensor.hasSensor())
|
2022-07-31 13:52:47 +00:00
|
|
|
result = bme680Sensor.runOnce();
|
2023-01-18 20:51:48 +00:00
|
|
|
if (mcp9808Sensor.hasSensor())
|
2022-06-05 14:50:06 +00:00
|
|
|
result = mcp9808Sensor.runOnce();
|
2023-01-18 20:51:48 +00:00
|
|
|
if (ina260Sensor.hasSensor())
|
2022-06-11 21:44:56 +00:00
|
|
|
result = ina260Sensor.runOnce();
|
|
|
|
if (ina219Sensor.hasSensor())
|
|
|
|
result = ina219Sensor.runOnce();
|
2022-10-15 14:11:05 +00:00
|
|
|
if (shtc3Sensor.hasSensor())
|
|
|
|
result = shtc3Sensor.runOnce();
|
2022-10-15 19:55:57 +00:00
|
|
|
if (lps22hbSensor.hasSensor()) {
|
|
|
|
result = lps22hbSensor.runOnce();
|
|
|
|
}
|
2022-12-28 14:30:23 +00:00
|
|
|
if (sht31Sensor.hasSensor())
|
|
|
|
result = sht31Sensor.runOnce();
|
2022-03-27 14:55:35 +00:00
|
|
|
}
|
2022-06-05 14:50:06 +00:00
|
|
|
return result;
|
2022-03-27 14:55:35 +00:00
|
|
|
} else {
|
|
|
|
// if we somehow got to a second run of this module with measurement disabled, then just wait forever
|
2022-05-22 11:27:56 +00:00
|
|
|
if (!moduleConfig.telemetry.environment_measurement_enabled)
|
2022-06-05 14:50:06 +00:00
|
|
|
return result;
|
2022-10-16 16:36:38 +00:00
|
|
|
|
|
|
|
uint32_t now = millis();
|
2023-01-18 20:51:48 +00:00
|
|
|
if ((lastSentToMesh == 0 ||
|
|
|
|
(now - lastSentToMesh) >= getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval)) &&
|
2023-01-11 20:52:19 +00:00
|
|
|
airTime->isTxAllowedAirUtil()) {
|
2022-10-16 16:36:38 +00:00
|
|
|
sendTelemetry();
|
|
|
|
lastSentToMesh = now;
|
|
|
|
} else if (service.isToPhoneQueueEmpty()) {
|
|
|
|
// Just send to phone when it's not our time to send to mesh yet
|
|
|
|
// Only send while queue is empty (phone assumed connected)
|
|
|
|
sendTelemetry(NODENUM_BROADCAST, true);
|
2022-03-27 14:55:35 +00:00
|
|
|
}
|
|
|
|
}
|
2022-10-16 16:36:38 +00:00
|
|
|
return sendToPhoneIntervalMs;
|
2022-03-27 14:55:35 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t GetTimeSinceMeshPacket(const MeshPacket *mp)
|
|
|
|
{
|
|
|
|
uint32_t now = getTime();
|
|
|
|
|
|
|
|
uint32_t last_seen = mp->rx_time;
|
|
|
|
int delta = (int)(now - last_seen);
|
|
|
|
if (delta < 0) // our clock must be slightly off still - not set from GPS yet
|
|
|
|
delta = 0;
|
|
|
|
|
|
|
|
return delta;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool EnvironmentTelemetryModule::wantUIFrame()
|
|
|
|
{
|
2022-05-22 11:27:56 +00:00
|
|
|
return moduleConfig.telemetry.environment_screen_enabled;
|
2022-03-27 14:55:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
float EnvironmentTelemetryModule::CelsiusToFahrenheit(float c)
|
|
|
|
{
|
|
|
|
return (c * 9) / 5 + 32;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
|
|
|
{
|
|
|
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
|
|
|
display->setFont(FONT_MEDIUM);
|
|
|
|
display->drawString(x, y, "Environment");
|
|
|
|
if (lastMeasurementPacket == nullptr) {
|
|
|
|
display->setFont(FONT_SMALL);
|
|
|
|
display->drawString(x, y += fontHeight(FONT_MEDIUM), "No measurement");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Telemetry lastMeasurement;
|
|
|
|
|
|
|
|
uint32_t agoSecs = GetTimeSinceMeshPacket(lastMeasurementPacket);
|
2022-04-24 21:12:25 +00:00
|
|
|
const char *lastSender = getSenderShortName(*lastMeasurementPacket);
|
2022-03-27 14:55:35 +00:00
|
|
|
|
|
|
|
auto &p = lastMeasurementPacket->decoded;
|
2022-12-23 09:00:34 +00:00
|
|
|
if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &Telemetry_msg, &lastMeasurement)) {
|
2022-03-27 14:55:35 +00:00
|
|
|
display->setFont(FONT_SMALL);
|
|
|
|
display->drawString(x, y += fontHeight(FONT_MEDIUM), "Measurement Error");
|
2022-12-30 16:27:07 +00:00
|
|
|
LOG_ERROR("Unable to decode last packet");
|
2022-03-27 14:55:35 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
display->setFont(FONT_SMALL);
|
|
|
|
String last_temp = String(lastMeasurement.variant.environment_metrics.temperature, 0) + "°C";
|
2022-05-22 11:27:56 +00:00
|
|
|
if (moduleConfig.telemetry.environment_display_fahrenheit) {
|
2022-03-27 14:55:35 +00:00
|
|
|
last_temp = String(CelsiusToFahrenheit(lastMeasurement.variant.environment_metrics.temperature), 0) + "°F";
|
|
|
|
}
|
2022-04-24 21:12:25 +00:00
|
|
|
display->drawString(x, y += fontHeight(FONT_MEDIUM) - 2, "From: " + String(lastSender) + "(" + String(agoSecs) + "s)");
|
2022-05-07 10:31:21 +00:00
|
|
|
display->drawString(x, y += fontHeight(FONT_SMALL) - 2,
|
2023-01-18 20:51:48 +00:00
|
|
|
"Temp/Hum: " + last_temp + " / " +
|
|
|
|
String(lastMeasurement.variant.environment_metrics.relative_humidity, 0) + "%");
|
2022-05-07 10:31:21 +00:00
|
|
|
if (lastMeasurement.variant.environment_metrics.barometric_pressure != 0)
|
|
|
|
display->drawString(x, y += fontHeight(FONT_SMALL),
|
2023-01-18 20:51:48 +00:00
|
|
|
"Press: " + String(lastMeasurement.variant.environment_metrics.barometric_pressure, 0) + "hPA");
|
2022-07-03 16:10:41 +00:00
|
|
|
if (lastMeasurement.variant.environment_metrics.voltage != 0)
|
|
|
|
display->drawString(x, y += fontHeight(FONT_SMALL),
|
2023-01-18 20:51:48 +00:00
|
|
|
"Volt/Cur: " + String(lastMeasurement.variant.environment_metrics.voltage, 0) + "V / " +
|
|
|
|
String(lastMeasurement.variant.environment_metrics.current, 0) + "mA");
|
2022-03-27 14:55:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool EnvironmentTelemetryModule::handleReceivedProtobuf(const MeshPacket &mp, Telemetry *t)
|
|
|
|
{
|
|
|
|
if (t->which_variant == Telemetry_environment_metrics_tag) {
|
2022-04-24 21:12:25 +00:00
|
|
|
const char *sender = getSenderShortName(mp);
|
2022-03-27 14:55:35 +00:00
|
|
|
|
2023-01-18 20:51:48 +00:00
|
|
|
LOG_INFO("(Received from %s): barometric_pressure=%f, current=%f, gas_resistance=%f, relative_humidity=%f, "
|
|
|
|
"temperature=%f, voltage=%f\n",
|
|
|
|
sender, t->variant.environment_metrics.barometric_pressure, t->variant.environment_metrics.current,
|
|
|
|
t->variant.environment_metrics.gas_resistance, t->variant.environment_metrics.relative_humidity,
|
|
|
|
t->variant.environment_metrics.temperature, t->variant.environment_metrics.voltage);
|
2022-03-27 14:55:35 +00:00
|
|
|
|
|
|
|
lastMeasurementPacket = packetPool.allocCopy(mp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false; // Let others look at this message also if they want
|
|
|
|
}
|
|
|
|
|
2022-10-16 16:36:38 +00:00
|
|
|
bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
2022-03-27 14:55:35 +00:00
|
|
|
{
|
|
|
|
Telemetry m;
|
|
|
|
m.time = getTime();
|
|
|
|
m.which_variant = Telemetry_environment_metrics_tag;
|
|
|
|
|
|
|
|
m.variant.environment_metrics.barometric_pressure = 0;
|
|
|
|
m.variant.environment_metrics.current = 0;
|
|
|
|
m.variant.environment_metrics.gas_resistance = 0;
|
|
|
|
m.variant.environment_metrics.relative_humidity = 0;
|
|
|
|
m.variant.environment_metrics.temperature = 0;
|
|
|
|
m.variant.environment_metrics.voltage = 0;
|
2022-05-07 10:31:21 +00:00
|
|
|
|
2022-12-28 14:30:23 +00:00
|
|
|
if (sht31Sensor.hasSensor())
|
|
|
|
sht31Sensor.getMetrics(&m);
|
2022-10-15 19:55:57 +00:00
|
|
|
if (lps22hbSensor.hasSensor())
|
|
|
|
lps22hbSensor.getMetrics(&m);
|
|
|
|
if (shtc3Sensor.hasSensor())
|
|
|
|
shtc3Sensor.getMetrics(&m);
|
2022-07-31 13:52:47 +00:00
|
|
|
if (bmp280Sensor.hasSensor())
|
|
|
|
bmp280Sensor.getMetrics(&m);
|
2022-06-10 17:04:04 +00:00
|
|
|
if (bme280Sensor.hasSensor())
|
|
|
|
bme280Sensor.getMetrics(&m);
|
|
|
|
if (bme680Sensor.hasSensor())
|
|
|
|
bme680Sensor.getMetrics(&m);
|
|
|
|
if (mcp9808Sensor.hasSensor())
|
|
|
|
mcp9808Sensor.getMetrics(&m);
|
2022-06-11 21:44:56 +00:00
|
|
|
if (ina219Sensor.hasSensor())
|
|
|
|
ina219Sensor.getMetrics(&m);
|
|
|
|
if (ina260Sensor.hasSensor())
|
|
|
|
ina260Sensor.getMetrics(&m);
|
2022-03-27 14:55:35 +00:00
|
|
|
|
2023-01-18 20:51:48 +00:00
|
|
|
LOG_INFO(
|
|
|
|
"(Sending): barometric_pressure=%f, current=%f, gas_resistance=%f, relative_humidity=%f, temperature=%f, voltage=%f\n",
|
|
|
|
m.variant.environment_metrics.barometric_pressure, m.variant.environment_metrics.current,
|
|
|
|
m.variant.environment_metrics.gas_resistance, m.variant.environment_metrics.relative_humidity,
|
|
|
|
m.variant.environment_metrics.temperature, m.variant.environment_metrics.voltage);
|
2022-03-27 14:55:35 +00:00
|
|
|
|
|
|
|
sensor_read_error_count = 0;
|
|
|
|
|
|
|
|
MeshPacket *p = allocDataProtobuf(m);
|
|
|
|
p->to = dest;
|
2022-10-16 16:36:38 +00:00
|
|
|
p->decoded.want_response = false;
|
2022-10-16 20:59:59 +00:00
|
|
|
p->priority = MeshPacket_Priority_MIN;
|
2022-03-27 14:55:35 +00:00
|
|
|
|
|
|
|
lastMeasurementPacket = packetPool.allocCopy(*p);
|
2022-10-16 16:36:38 +00:00
|
|
|
if (phoneOnly) {
|
2022-12-30 16:27:07 +00:00
|
|
|
LOG_INFO("Sending packet to phone\n");
|
2022-10-16 16:36:38 +00:00
|
|
|
service.sendToPhone(p);
|
|
|
|
} else {
|
2022-12-30 16:27:07 +00:00
|
|
|
LOG_INFO("Sending packet to mesh\n");
|
2022-10-16 16:36:38 +00:00
|
|
|
service.sendToMesh(p, RX_SRC_LOCAL, true);
|
|
|
|
}
|
2022-03-27 14:55:35 +00:00
|
|
|
return true;
|
|
|
|
}
|