add a .clang-format file (#9154)

This commit is contained in:
Jorropo
2026-01-03 21:19:24 +01:00
committed by GitHub
parent abab6ce815
commit 0d11331d18
771 changed files with 77752 additions and 83184 deletions

View File

@@ -21,216 +21,206 @@
#define PMSA003I_WARMUP_MS 30000
#endif
int32_t AirQualityTelemetryModule::runOnce()
{
/*
Uncomment the preferences below if you want to use the module
without having to configure it from the PythonAPI or WebUI.
*/
int32_t AirQualityTelemetryModule::runOnce() {
/*
Uncomment the preferences below if you want to use the module
without having to configure it from the PythonAPI or WebUI.
*/
// moduleConfig.telemetry.air_quality_enabled = 1;
// moduleConfig.telemetry.air_quality_enabled = 1;
if (!(moduleConfig.telemetry.air_quality_enabled)) {
// If this module is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it
return disable();
}
if (!(moduleConfig.telemetry.air_quality_enabled)) {
// If this module is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it
return disable();
}
if (firstTime) {
// This is the first time the OSThread library has called this function, so do some setup
firstTime = false;
if (firstTime) {
// This is the first time the OSThread library has called this function, so do some setup
firstTime = false;
if (moduleConfig.telemetry.air_quality_enabled) {
LOG_INFO("Air quality Telemetry: init");
if (moduleConfig.telemetry.air_quality_enabled) {
LOG_INFO("Air quality Telemetry: init");
#ifdef PMSA003I_ENABLE_PIN
// put the sensor to sleep on startup
pinMode(PMSA003I_ENABLE_PIN, OUTPUT);
digitalWrite(PMSA003I_ENABLE_PIN, LOW);
// put the sensor to sleep on startup
pinMode(PMSA003I_ENABLE_PIN, OUTPUT);
digitalWrite(PMSA003I_ENABLE_PIN, LOW);
#endif /* PMSA003I_ENABLE_PIN */
if (!aqi.begin_I2C()) {
if (!aqi.begin_I2C()) {
#ifndef I2C_NO_RESCAN
LOG_WARN("Could not establish i2c connection to AQI sensor. Rescan");
// rescan for late arriving sensors. AQI Module starts about 10 seconds into the boot so this is plenty.
uint8_t i2caddr_scan[] = {PMSA0031_ADDR};
uint8_t i2caddr_asize = 1;
auto i2cScanner = std::unique_ptr<ScanI2CTwoWire>(new ScanI2CTwoWire());
LOG_WARN("Could not establish i2c connection to AQI sensor. Rescan");
// rescan for late arriving sensors. AQI Module starts about 10 seconds into the boot so this is plenty.
uint8_t i2caddr_scan[] = {PMSA0031_ADDR};
uint8_t i2caddr_asize = 1;
auto i2cScanner = std::unique_ptr<ScanI2CTwoWire>(new ScanI2CTwoWire());
#if defined(I2C_SDA1)
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1, i2caddr_scan, i2caddr_asize);
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1, i2caddr_scan, i2caddr_asize);
#endif
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE, i2caddr_scan, i2caddr_asize);
auto found = i2cScanner->find(ScanI2C::DeviceType::PMSA0031);
if (found.type != ScanI2C::DeviceType::NONE) {
nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].first = found.address.address;
nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].second =
i2cScanner->fetchI2CBus(found.address);
return setStartDelay();
}
#endif
return disable();
}
return setStartDelay();
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE, i2caddr_scan, i2caddr_asize);
auto found = i2cScanner->find(ScanI2C::DeviceType::PMSA0031);
if (found.type != ScanI2C::DeviceType::NONE) {
nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].first = found.address.address;
nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].second = i2cScanner->fetchI2CBus(found.address);
return setStartDelay();
}
#endif
return disable();
} else {
// if we somehow got to a second run of this module with measurement disabled, then just wait forever
if (!moduleConfig.telemetry.air_quality_enabled)
return disable();
switch (state) {
#ifdef PMSA003I_ENABLE_PIN
case State::IDLE:
// sensor is in standby; fire it up and sleep
LOG_DEBUG("runOnce(): state = idle");
digitalWrite(PMSA003I_ENABLE_PIN, HIGH);
state = State::ACTIVE;
return PMSA003I_WARMUP_MS;
#endif /* PMSA003I_ENABLE_PIN */
case State::ACTIVE:
// sensor is already warmed up; grab telemetry and send it
LOG_DEBUG("runOnce(): state = active");
if (((lastSentToMesh == 0) ||
!Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled(
moduleConfig.telemetry.air_quality_interval,
default_telemetry_broadcast_interval_secs, numOnlineNodes))) &&
airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
airTime->isTxAllowedAirUtil()) {
sendTelemetry();
lastSentToMesh = millis();
} 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);
}
#ifdef PMSA003I_ENABLE_PIN
// put sensor back to sleep
digitalWrite(PMSA003I_ENABLE_PIN, LOW);
state = State::IDLE;
#endif /* PMSA003I_ENABLE_PIN */
return sendToPhoneIntervalMs;
default:
return disable();
}
}
return setStartDelay();
}
return disable();
} else {
// if we somehow got to a second run of this module with measurement disabled, then just wait forever
if (!moduleConfig.telemetry.air_quality_enabled)
return disable();
switch (state) {
#ifdef PMSA003I_ENABLE_PIN
case State::IDLE:
// sensor is in standby; fire it up and sleep
LOG_DEBUG("runOnce(): state = idle");
digitalWrite(PMSA003I_ENABLE_PIN, HIGH);
state = State::ACTIVE;
return PMSA003I_WARMUP_MS;
#endif /* PMSA003I_ENABLE_PIN */
case State::ACTIVE:
// sensor is already warmed up; grab telemetry and send it
LOG_DEBUG("runOnce(): state = active");
if (((lastSentToMesh == 0) || !Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled(
moduleConfig.telemetry.air_quality_interval,
default_telemetry_broadcast_interval_secs, numOnlineNodes))) &&
airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) && airTime->isTxAllowedAirUtil()) {
sendTelemetry();
lastSentToMesh = millis();
} 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);
}
#ifdef PMSA003I_ENABLE_PIN
// put sensor back to sleep
digitalWrite(PMSA003I_ENABLE_PIN, LOW);
state = State::IDLE;
#endif /* PMSA003I_ENABLE_PIN */
return sendToPhoneIntervalMs;
default:
return disable();
}
}
}
bool AirQualityTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t)
{
if (t->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) {
bool AirQualityTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t) {
if (t->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) {
#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE)
const char *sender = getSenderShortName(mp);
const char *sender = getSenderShortName(mp);
LOG_INFO("(Received from %s): pm10_standard=%i, pm25_standard=%i, pm100_standard=%i", sender,
t->variant.air_quality_metrics.pm10_standard, t->variant.air_quality_metrics.pm25_standard,
t->variant.air_quality_metrics.pm100_standard);
LOG_INFO("(Received from %s): pm10_standard=%i, pm25_standard=%i, pm100_standard=%i", sender, t->variant.air_quality_metrics.pm10_standard,
t->variant.air_quality_metrics.pm25_standard, t->variant.air_quality_metrics.pm100_standard);
LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i",
t->variant.air_quality_metrics.pm10_environmental, t->variant.air_quality_metrics.pm25_environmental,
t->variant.air_quality_metrics.pm100_environmental);
LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i",
t->variant.air_quality_metrics.pm10_environmental, t->variant.air_quality_metrics.pm25_environmental,
t->variant.air_quality_metrics.pm100_environmental);
#endif
// release previous packet before occupying a new spot
if (lastMeasurementPacket != nullptr)
packetPool.release(lastMeasurementPacket);
// release previous packet before occupying a new spot
if (lastMeasurementPacket != nullptr)
packetPool.release(lastMeasurementPacket);
lastMeasurementPacket = packetPool.allocCopy(mp);
}
lastMeasurementPacket = packetPool.allocCopy(mp);
}
return false; // Let others look at this message also if they want
return false; // Let others look at this message also if they want
}
bool AirQualityTelemetryModule::getAirQualityTelemetry(meshtastic_Telemetry *m)
{
if (!aqi.read(&data)) {
LOG_WARN("Skip send measurements. Could not read AQIn");
return false;
}
m->time = getTime();
m->which_variant = meshtastic_Telemetry_air_quality_metrics_tag;
m->variant.air_quality_metrics.has_pm10_standard = true;
m->variant.air_quality_metrics.pm10_standard = data.pm10_standard;
m->variant.air_quality_metrics.has_pm25_standard = true;
m->variant.air_quality_metrics.pm25_standard = data.pm25_standard;
m->variant.air_quality_metrics.has_pm100_standard = true;
m->variant.air_quality_metrics.pm100_standard = data.pm100_standard;
m->variant.air_quality_metrics.has_pm10_environmental = true;
m->variant.air_quality_metrics.pm10_environmental = data.pm10_env;
m->variant.air_quality_metrics.has_pm25_environmental = true;
m->variant.air_quality_metrics.pm25_environmental = data.pm25_env;
m->variant.air_quality_metrics.has_pm100_environmental = true;
m->variant.air_quality_metrics.pm100_environmental = data.pm100_env;
LOG_INFO("Send: PM1.0(Standard)=%i, PM2.5(Standard)=%i, PM10.0(Standard)=%i", m->variant.air_quality_metrics.pm10_standard,
m->variant.air_quality_metrics.pm25_standard, m->variant.air_quality_metrics.pm100_standard);
LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i",
m->variant.air_quality_metrics.pm10_environmental, m->variant.air_quality_metrics.pm25_environmental,
m->variant.air_quality_metrics.pm100_environmental);
return true;
}
meshtastic_MeshPacket *AirQualityTelemetryModule::allocReply()
{
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 {
LOG_ERROR("Error decoding AirQualityTelemetry module!");
return NULL;
}
// Check for a request for air quality metrics
if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) {
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
if (getAirQualityTelemetry(&m)) {
LOG_INFO("Air quality telemetry reply to request");
return allocDataProtobuf(m);
} else {
return NULL;
}
}
}
return NULL;
}
bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
{
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
if (getAirQualityTelemetry(&m)) {
meshtastic_MeshPacket *p = allocDataProtobuf(m);
p->to = dest;
p->decoded.want_response = false;
if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR)
p->priority = meshtastic_MeshPacket_Priority_RELIABLE;
else
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
// release previous packet before occupying a new spot
if (lastMeasurementPacket != nullptr)
packetPool.release(lastMeasurementPacket);
lastMeasurementPacket = packetPool.allocCopy(*p);
if (phoneOnly) {
LOG_INFO("Send packet to phone");
service->sendToPhone(p);
} else {
LOG_INFO("Send packet to mesh");
service->sendToMesh(p, RX_SRC_LOCAL, true);
}
return true;
}
bool AirQualityTelemetryModule::getAirQualityTelemetry(meshtastic_Telemetry *m) {
if (!aqi.read(&data)) {
LOG_WARN("Skip send measurements. Could not read AQIn");
return false;
}
m->time = getTime();
m->which_variant = meshtastic_Telemetry_air_quality_metrics_tag;
m->variant.air_quality_metrics.has_pm10_standard = true;
m->variant.air_quality_metrics.pm10_standard = data.pm10_standard;
m->variant.air_quality_metrics.has_pm25_standard = true;
m->variant.air_quality_metrics.pm25_standard = data.pm25_standard;
m->variant.air_quality_metrics.has_pm100_standard = true;
m->variant.air_quality_metrics.pm100_standard = data.pm100_standard;
m->variant.air_quality_metrics.has_pm10_environmental = true;
m->variant.air_quality_metrics.pm10_environmental = data.pm10_env;
m->variant.air_quality_metrics.has_pm25_environmental = true;
m->variant.air_quality_metrics.pm25_environmental = data.pm25_env;
m->variant.air_quality_metrics.has_pm100_environmental = true;
m->variant.air_quality_metrics.pm100_environmental = data.pm100_env;
LOG_INFO("Send: PM1.0(Standard)=%i, PM2.5(Standard)=%i, PM10.0(Standard)=%i", m->variant.air_quality_metrics.pm10_standard,
m->variant.air_quality_metrics.pm25_standard, m->variant.air_quality_metrics.pm100_standard);
LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i", m->variant.air_quality_metrics.pm10_environmental,
m->variant.air_quality_metrics.pm25_environmental, m->variant.air_quality_metrics.pm100_environmental);
return true;
}
meshtastic_MeshPacket *AirQualityTelemetryModule::allocReply() {
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 {
LOG_ERROR("Error decoding AirQualityTelemetry module!");
return NULL;
}
// Check for a request for air quality metrics
if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) {
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
if (getAirQualityTelemetry(&m)) {
LOG_INFO("Air quality telemetry reply to request");
return allocDataProtobuf(m);
} else {
return NULL;
}
}
}
return NULL;
}
bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) {
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
if (getAirQualityTelemetry(&m)) {
meshtastic_MeshPacket *p = allocDataProtobuf(m);
p->to = dest;
p->decoded.want_response = false;
if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR)
p->priority = meshtastic_MeshPacket_Priority_RELIABLE;
else
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
// release previous packet before occupying a new spot
if (lastMeasurementPacket != nullptr)
packetPool.release(lastMeasurementPacket);
lastMeasurementPacket = packetPool.allocCopy(*p);
if (phoneOnly) {
LOG_INFO("Send packet to phone");
service->sendToPhone(p);
} else {
LOG_INFO("Send packet to mesh");
service->sendToMesh(p, RX_SRC_LOCAL, true);
}
return true;
}
return false;
}
#endif

View File

@@ -8,60 +8,57 @@
#include "NodeDB.h"
#include "ProtobufModule.h"
class AirQualityTelemetryModule : private concurrency::OSThread, public ProtobufModule<meshtastic_Telemetry>
{
CallbackObserver<AirQualityTelemetryModule, const meshtastic::Status *> nodeStatusObserver =
CallbackObserver<AirQualityTelemetryModule, const meshtastic::Status *>(this,
&AirQualityTelemetryModule::handleStatusUpdate);
class AirQualityTelemetryModule : private concurrency::OSThread, public ProtobufModule<meshtastic_Telemetry> {
CallbackObserver<AirQualityTelemetryModule, const meshtastic::Status *> nodeStatusObserver =
CallbackObserver<AirQualityTelemetryModule, const meshtastic::Status *>(this, &AirQualityTelemetryModule::handleStatusUpdate);
public:
AirQualityTelemetryModule()
: concurrency::OSThread("AirQualityTelemetry"),
ProtobufModule("AirQualityTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg)
{
lastMeasurementPacket = nullptr;
setIntervalFromNow(10 * 1000);
aqi = Adafruit_PM25AQI();
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
public:
AirQualityTelemetryModule()
: concurrency::OSThread("AirQualityTelemetry"),
ProtobufModule("AirQualityTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg) {
lastMeasurementPacket = nullptr;
setIntervalFromNow(10 * 1000);
aqi = Adafruit_PM25AQI();
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
#ifdef PMSA003I_ENABLE_PIN
// the PMSA003I sensor uses about 300mW on its own; support powering it off when it's not actively taking
// a reading
state = State::IDLE;
// the PMSA003I sensor uses about 300mW on its own; support powering it off when it's not actively taking
// a reading
state = State::IDLE;
#else
state = State::ACTIVE;
state = State::ACTIVE;
#endif
}
}
protected:
/** Called to handle a particular incoming message
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
virtual int32_t runOnce() override;
/** Called to get current Air Quality data
@return true if it contains valid data
*/
bool getAirQualityTelemetry(meshtastic_Telemetry *m);
virtual meshtastic_MeshPacket *allocReply() override;
/**
* Send our Telemetry into the mesh
*/
bool sendTelemetry(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
protected:
/** Called to handle a particular incoming message
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
virtual int32_t runOnce() override;
/** Called to get current Air Quality data
@return true if it contains valid data
*/
bool getAirQualityTelemetry(meshtastic_Telemetry *m);
virtual meshtastic_MeshPacket *allocReply() override;
/**
* Send our Telemetry into the mesh
*/
bool sendTelemetry(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
private:
enum State {
IDLE = 0,
ACTIVE = 1,
};
private:
enum State {
IDLE = 0,
ACTIVE = 1,
};
State state;
Adafruit_PM25AQI aqi;
PM25_AQI_Data data = {0};
bool firstTime = true;
meshtastic_MeshPacket *lastMeasurementPacket;
uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute
uint32_t lastSentToMesh = 0;
State state;
Adafruit_PM25AQI aqi;
PM25_AQI_Data data = {0};
bool firstTime = true;
meshtastic_MeshPacket *lastMeasurementPacket;
uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute
uint32_t lastSentToMesh = 0;
};
#endif

View File

@@ -16,175 +16,162 @@
#define MAGIC_USB_BATTERY_LEVEL 101
int32_t DeviceTelemetryModule::runOnce()
{
refreshUptime();
bool isImpoliteRole =
IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_SENSOR, meshtastic_Config_DeviceConfig_Role_ROUTER);
if (((lastSentToMesh == 0) ||
((uptimeLastMs - lastSentToMesh) >=
Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.device_update_interval,
default_telemetry_broadcast_interval_secs, numOnlineNodes))) &&
airTime->isTxAllowedChannelUtil(!isImpoliteRole) && airTime->isTxAllowedAirUtil() &&
config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN &&
moduleConfig.telemetry.device_telemetry_enabled) {
sendTelemetry();
lastSentToMesh = uptimeLastMs;
} 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);
if (lastSentStatsToPhone == 0 || (uptimeLastMs - lastSentStatsToPhone) >= sendStatsToPhoneIntervalMs) {
sendLocalStatsToPhone();
lastSentStatsToPhone = uptimeLastMs;
}
int32_t DeviceTelemetryModule::runOnce() {
refreshUptime();
bool isImpoliteRole = IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_SENSOR, meshtastic_Config_DeviceConfig_Role_ROUTER);
if (((lastSentToMesh == 0) ||
((uptimeLastMs - lastSentToMesh) >= Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.device_update_interval,
default_telemetry_broadcast_interval_secs, numOnlineNodes))) &&
airTime->isTxAllowedChannelUtil(!isImpoliteRole) && airTime->isTxAllowedAirUtil() &&
config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN && moduleConfig.telemetry.device_telemetry_enabled) {
sendTelemetry();
lastSentToMesh = uptimeLastMs;
} 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);
if (lastSentStatsToPhone == 0 || (uptimeLastMs - lastSentStatsToPhone) >= sendStatsToPhoneIntervalMs) {
sendLocalStatsToPhone();
lastSentStatsToPhone = uptimeLastMs;
}
return sendToPhoneIntervalMs;
}
return sendToPhoneIntervalMs;
}
bool DeviceTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t)
{
if (t->which_variant == meshtastic_Telemetry_device_metrics_tag) {
bool DeviceTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t) {
if (t->which_variant == meshtastic_Telemetry_device_metrics_tag) {
#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE)
const char *sender = getSenderShortName(mp);
const char *sender = getSenderShortName(mp);
LOG_INFO("(Received from %s): air_util_tx=%f, channel_utilization=%f, battery_level=%i, voltage=%f", sender,
t->variant.device_metrics.air_util_tx, t->variant.device_metrics.channel_utilization,
t->variant.device_metrics.battery_level, t->variant.device_metrics.voltage);
LOG_INFO("(Received from %s): air_util_tx=%f, channel_utilization=%f, battery_level=%i, voltage=%f", sender,
t->variant.device_metrics.air_util_tx, t->variant.device_metrics.channel_utilization, t->variant.device_metrics.battery_level,
t->variant.device_metrics.voltage);
#endif
nodeDB->updateTelemetry(getFrom(&mp), *t, RX_SRC_RADIO);
}
return false; // Let others look at this message also if they want
nodeDB->updateTelemetry(getFrom(&mp), *t, RX_SRC_RADIO);
}
return false; // Let others look at this message also if they want
}
meshtastic_MeshPacket *DeviceTelemetryModule::allocReply()
{
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 {
LOG_ERROR("Error decoding DeviceTelemetry module!");
return NULL;
}
// Check for a request for device metrics
if (decoded->which_variant == meshtastic_Telemetry_device_metrics_tag) {
LOG_INFO("Device telemetry reply to request");
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());
}
}
return NULL;
}
meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry()
{
meshtastic_Telemetry t = meshtastic_Telemetry_init_zero;
t.which_variant = meshtastic_Telemetry_device_metrics_tag;
t.time = getTime();
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;
t.variant.device_metrics.air_util_tx = airTime->utilizationTXPercent();
t.variant.device_metrics.battery_level = (!powerStatus->getHasBattery() || powerStatus->getIsCharging())
? MAGIC_USB_BATTERY_LEVEL
: powerStatus->getBatteryChargePercent();
t.variant.device_metrics.channel_utilization = airTime->channelUtilizationPercent();
t.variant.device_metrics.voltage = powerStatus->getBatteryVoltageMv() / 1000.0;
t.variant.device_metrics.uptime_seconds = getUptimeSeconds();
return t;
}
meshtastic_Telemetry DeviceTelemetryModule::getLocalStatsTelemetry()
{
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;
telemetry.variant.local_stats.num_packets_rx = RadioLibInterface::instance->rxGood + RadioLibInterface::instance->rxBad;
telemetry.variant.local_stats.num_packets_rx_bad = RadioLibInterface::instance->rxBad;
telemetry.variant.local_stats.num_tx_relay = RadioLibInterface::instance->txRelay;
telemetry.variant.local_stats.num_tx_dropped = RadioLibInterface::instance->txDrop;
}
#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;
telemetry.variant.local_stats.num_tx_dropped = SimRadio::instance->txDrop;
}
#else
telemetry.variant.local_stats.heap_total_bytes = memGet.getHeapSize();
telemetry.variant.local_stats.heap_free_bytes = memGet.getFreeHeap();
#endif
if (router) {
telemetry.variant.local_stats.num_rx_dupe = router->rxDupe;
telemetry.variant.local_stats.num_tx_relay_canceled = router->txRelayCanceled;
}
LOG_INFO("Sending local stats: uptime=%i, channel_utilization=%f, air_util_tx=%f, num_online_nodes=%i, num_total_nodes=%i",
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);
LOG_INFO("num_packets_tx=%i, num_packets_rx=%i, num_packets_rx_bad=%i", telemetry.variant.local_stats.num_packets_tx,
telemetry.variant.local_stats.num_packets_rx, telemetry.variant.local_stats.num_packets_rx_bad);
return telemetry;
}
void DeviceTelemetryModule::sendLocalStatsToPhone()
{
meshtastic_MeshPacket *p = allocDataProtobuf(getLocalStatsTelemetry());
p->to = NODENUM_BROADCAST;
p->decoded.want_response = false;
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
service->sendToPhone(p);
}
bool DeviceTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
{
meshtastic_Telemetry telemetry = getDeviceTelemetry();
LOG_INFO("Send: air_util_tx=%f, channel_utilization=%f, battery_level=%i, voltage=%f, uptime=%i",
telemetry.variant.device_metrics.air_util_tx, telemetry.variant.device_metrics.channel_utilization,
telemetry.variant.device_metrics.battery_level, telemetry.variant.device_metrics.voltage,
telemetry.variant.device_metrics.uptime_seconds);
DEBUG_HEAP_BEFORE;
meshtastic_MeshPacket *p = allocDataProtobuf(telemetry);
DEBUG_HEAP_AFTER("DeviceTelemetryModule::sendTelemetry", p);
p->to = dest;
p->decoded.want_response = false;
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
nodeDB->updateTelemetry(nodeDB->getNodeNum(), telemetry, RX_SRC_LOCAL);
if (phoneOnly) {
LOG_INFO("Send packet to phone");
service->sendToPhone(p);
meshtastic_MeshPacket *DeviceTelemetryModule::allocReply() {
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 {
LOG_INFO("Send packet to mesh");
service->sendToMesh(p, RX_SRC_LOCAL, true);
LOG_ERROR("Error decoding DeviceTelemetry module!");
return NULL;
}
return true;
// Check for a request for device metrics
if (decoded->which_variant == meshtastic_Telemetry_device_metrics_tag) {
LOG_INFO("Device telemetry reply to request");
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());
}
}
return NULL;
}
meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry() {
meshtastic_Telemetry t = meshtastic_Telemetry_init_zero;
t.which_variant = meshtastic_Telemetry_device_metrics_tag;
t.time = getTime();
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;
t.variant.device_metrics.air_util_tx = airTime->utilizationTXPercent();
t.variant.device_metrics.battery_level =
(!powerStatus->getHasBattery() || powerStatus->getIsCharging()) ? MAGIC_USB_BATTERY_LEVEL : powerStatus->getBatteryChargePercent();
t.variant.device_metrics.channel_utilization = airTime->channelUtilizationPercent();
t.variant.device_metrics.voltage = powerStatus->getBatteryVoltageMv() / 1000.0;
t.variant.device_metrics.uptime_seconds = getUptimeSeconds();
return t;
}
meshtastic_Telemetry DeviceTelemetryModule::getLocalStatsTelemetry() {
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;
telemetry.variant.local_stats.num_packets_rx = RadioLibInterface::instance->rxGood + RadioLibInterface::instance->rxBad;
telemetry.variant.local_stats.num_packets_rx_bad = RadioLibInterface::instance->rxBad;
telemetry.variant.local_stats.num_tx_relay = RadioLibInterface::instance->txRelay;
telemetry.variant.local_stats.num_tx_dropped = RadioLibInterface::instance->txDrop;
}
#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;
telemetry.variant.local_stats.num_tx_dropped = SimRadio::instance->txDrop;
}
#else
telemetry.variant.local_stats.heap_total_bytes = memGet.getHeapSize();
telemetry.variant.local_stats.heap_free_bytes = memGet.getFreeHeap();
#endif
if (router) {
telemetry.variant.local_stats.num_rx_dupe = router->rxDupe;
telemetry.variant.local_stats.num_tx_relay_canceled = router->txRelayCanceled;
}
LOG_INFO("Sending local stats: uptime=%i, channel_utilization=%f, air_util_tx=%f, num_online_nodes=%i, num_total_nodes=%i",
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);
LOG_INFO("num_packets_tx=%i, num_packets_rx=%i, num_packets_rx_bad=%i", telemetry.variant.local_stats.num_packets_tx,
telemetry.variant.local_stats.num_packets_rx, telemetry.variant.local_stats.num_packets_rx_bad);
return telemetry;
}
void DeviceTelemetryModule::sendLocalStatsToPhone() {
meshtastic_MeshPacket *p = allocDataProtobuf(getLocalStatsTelemetry());
p->to = NODENUM_BROADCAST;
p->decoded.want_response = false;
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
service->sendToPhone(p);
}
bool DeviceTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) {
meshtastic_Telemetry telemetry = getDeviceTelemetry();
LOG_INFO("Send: air_util_tx=%f, channel_utilization=%f, battery_level=%i, voltage=%f, uptime=%i", telemetry.variant.device_metrics.air_util_tx,
telemetry.variant.device_metrics.channel_utilization, telemetry.variant.device_metrics.battery_level,
telemetry.variant.device_metrics.voltage, telemetry.variant.device_metrics.uptime_seconds);
DEBUG_HEAP_BEFORE;
meshtastic_MeshPacket *p = allocDataProtobuf(telemetry);
DEBUG_HEAP_AFTER("DeviceTelemetryModule::sendTelemetry", p);
p->to = dest;
p->decoded.want_response = false;
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
nodeDB->updateTelemetry(nodeDB->getNodeNum(), telemetry, RX_SRC_LOCAL);
if (phoneOnly) {
LOG_INFO("Send packet to phone");
service->sendToPhone(p);
} else {
LOG_INFO("Send packet to mesh");
service->sendToMesh(p, RX_SRC_LOCAL, true);
}
return true;
}

View File

@@ -5,61 +5,57 @@
#include <OLEDDisplay.h>
#include <OLEDDisplayUi.h>
class DeviceTelemetryModule : private concurrency::OSThread, public ProtobufModule<meshtastic_Telemetry>
{
CallbackObserver<DeviceTelemetryModule, const meshtastic::Status *> nodeStatusObserver =
CallbackObserver<DeviceTelemetryModule, const meshtastic::Status *>(this, &DeviceTelemetryModule::handleStatusUpdate);
class DeviceTelemetryModule : private concurrency::OSThread, public ProtobufModule<meshtastic_Telemetry> {
CallbackObserver<DeviceTelemetryModule, const meshtastic::Status *> nodeStatusObserver =
CallbackObserver<DeviceTelemetryModule, const meshtastic::Status *>(this, &DeviceTelemetryModule::handleStatusUpdate);
public:
DeviceTelemetryModule()
: concurrency::OSThread("DeviceTelemetry"),
ProtobufModule("DeviceTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg)
{
uptimeWrapCount = 0;
uptimeLastMs = millis();
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
setIntervalFromNow(setStartDelay()); // Wait until NodeInfo is sent
}
virtual bool wantUIFrame() { return false; }
public:
DeviceTelemetryModule()
: concurrency::OSThread("DeviceTelemetry"), ProtobufModule("DeviceTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg) {
uptimeWrapCount = 0;
uptimeLastMs = millis();
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
setIntervalFromNow(setStartDelay()); // Wait until NodeInfo is sent
}
virtual bool wantUIFrame() { return false; }
protected:
/** Called to handle a particular incoming message
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
virtual meshtastic_MeshPacket *allocReply() override;
virtual int32_t runOnce() override;
/**
* Send our Telemetry into the mesh
*/
bool sendTelemetry(NodeNum dest = NODENUM_BROADCAST, bool phoneOnly = false);
protected:
/** Called to handle a particular incoming message
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
virtual meshtastic_MeshPacket *allocReply() override;
virtual int32_t runOnce() override;
/**
* Send our Telemetry into the mesh
*/
bool sendTelemetry(NodeNum dest = NODENUM_BROADCAST, bool phoneOnly = false);
/**
* Get the uptime in seconds
* Loses some accuracy after 49 days, but that's fine
*/
uint32_t getUptimeSeconds() { return (0xFFFFFFFF / 1000) * uptimeWrapCount + (uptimeLastMs / 1000); }
/**
* Get the uptime in seconds
* Loses some accuracy after 49 days, but that's fine
*/
uint32_t getUptimeSeconds() { return (0xFFFFFFFF / 1000) * uptimeWrapCount + (uptimeLastMs / 1000); }
private:
meshtastic_Telemetry getDeviceTelemetry();
meshtastic_Telemetry getLocalStatsTelemetry();
private:
meshtastic_Telemetry getDeviceTelemetry();
meshtastic_Telemetry getLocalStatsTelemetry();
void sendLocalStatsToPhone();
uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute
uint32_t sendStatsToPhoneIntervalMs = 15 * SECONDS_IN_MINUTE * 1000; // Send stats to phone every 15 minutes
uint32_t lastSentStatsToPhone = 0;
uint32_t lastSentToMesh = 0;
void sendLocalStatsToPhone();
uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute
uint32_t sendStatsToPhoneIntervalMs = 15 * SECONDS_IN_MINUTE * 1000; // Send stats to phone every 15 minutes
uint32_t lastSentStatsToPhone = 0;
uint32_t lastSentToMesh = 0;
void refreshUptime()
{
auto now = millis();
// If we wrapped around (~49 days), increment the wrap count
if (now < uptimeLastMs)
uptimeWrapCount++;
void refreshUptime() {
auto now = millis();
// If we wrapped around (~49 days), increment the wrap count
if (now < uptimeLastMs)
uptimeWrapCount++;
uptimeLastMs = now;
}
uptimeLastMs = now;
}
uint32_t uptimeWrapCount;
uint32_t uptimeLastMs;
uint32_t uptimeWrapCount;
uint32_t uptimeLastMs;
};

File diff suppressed because it is too large Load Diff

View File

@@ -15,59 +15,53 @@
#include <OLEDDisplay.h>
#include <OLEDDisplayUi.h>
class EnvironmentTelemetryModule : private concurrency::OSThread,
public ScanI2CConsumer,
public ProtobufModule<meshtastic_Telemetry>
{
CallbackObserver<EnvironmentTelemetryModule, const meshtastic::Status *> nodeStatusObserver =
CallbackObserver<EnvironmentTelemetryModule, const meshtastic::Status *>(this,
&EnvironmentTelemetryModule::handleStatusUpdate);
class EnvironmentTelemetryModule : private concurrency::OSThread, public ScanI2CConsumer, public ProtobufModule<meshtastic_Telemetry> {
CallbackObserver<EnvironmentTelemetryModule, const meshtastic::Status *> nodeStatusObserver =
CallbackObserver<EnvironmentTelemetryModule, const meshtastic::Status *>(this, &EnvironmentTelemetryModule::handleStatusUpdate);
public:
EnvironmentTelemetryModule()
: concurrency::OSThread("EnvironmentTelemetry"), ScanI2CConsumer(),
ProtobufModule("EnvironmentTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg)
{
lastMeasurementPacket = nullptr;
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
setIntervalFromNow(10 * 1000);
}
virtual bool wantUIFrame() override;
public:
EnvironmentTelemetryModule()
: concurrency::OSThread("EnvironmentTelemetry"), ScanI2CConsumer(),
ProtobufModule("EnvironmentTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg) {
lastMeasurementPacket = nullptr;
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
setIntervalFromNow(10 * 1000);
}
virtual bool wantUIFrame() override;
#if !HAS_SCREEN
void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
#else
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override;
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override;
#endif
protected:
/** Called to handle a particular incoming message
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
virtual int32_t runOnce() override;
/** Called to get current Environment telemetry data
@return true if it contains valid data
*/
bool getEnvironmentTelemetry(meshtastic_Telemetry *m);
virtual meshtastic_MeshPacket *allocReply() override;
/**
* Send our Telemetry into the mesh
*/
bool sendTelemetry(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
protected:
/** Called to handle a particular incoming message
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
virtual int32_t runOnce() override;
/** Called to get current Environment telemetry data
@return true if it contains valid data
*/
bool getEnvironmentTelemetry(meshtastic_Telemetry *m);
virtual meshtastic_MeshPacket *allocReply() override;
/**
* Send our Telemetry into the mesh
*/
bool sendTelemetry(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
virtual AdminMessageHandleResult handleAdminMessageForModule(const meshtastic_MeshPacket &mp,
meshtastic_AdminMessage *request,
meshtastic_AdminMessage *response) override;
virtual AdminMessageHandleResult handleAdminMessageForModule(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request,
meshtastic_AdminMessage *response) override;
void i2cScanFinished(ScanI2C *i2cScanner);
void i2cScanFinished(ScanI2C *i2cScanner);
private:
bool firstTime = 1;
meshtastic_MeshPacket *lastMeasurementPacket;
uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute
uint32_t lastSentToMesh = 0;
uint32_t lastSentToPhone = 0;
uint32_t sensor_read_error_count = 0;
private:
bool firstTime = 1;
meshtastic_MeshPacket *lastMeasurementPacket;
uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute
uint32_t lastSentToMesh = 0;
uint32_t lastSentToPhone = 0;
uint32_t sensor_read_error_count = 0;
};
#endif

View File

@@ -33,226 +33,214 @@ MLX90614Sensor mlx90614Sensor;
#endif
#include <Throttle.h>
int32_t HealthTelemetryModule::runOnce()
{
if (sleepOnNextExecution == true) {
sleepOnNextExecution = false;
uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.health_update_interval,
default_telemetry_broadcast_interval_secs);
LOG_DEBUG("Sleep for %ims, then awake to send metrics again", nightyNightMs);
doDeepSleep(nightyNightMs, true, false);
int32_t HealthTelemetryModule::runOnce() {
if (sleepOnNextExecution == true) {
sleepOnNextExecution = false;
uint32_t nightyNightMs =
Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.health_update_interval, default_telemetry_broadcast_interval_secs);
LOG_DEBUG("Sleep for %ims, then awake to send metrics again", nightyNightMs);
doDeepSleep(nightyNightMs, true, false);
}
uint32_t result = UINT32_MAX;
if (!(moduleConfig.telemetry.health_measurement_enabled || moduleConfig.telemetry.health_screen_enabled)) {
// If this module is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it
return disable();
}
if (firstTime) {
// This is the first time the OSThread library has called this function, so do some setup
firstTime = false;
if (moduleConfig.telemetry.health_measurement_enabled) {
LOG_INFO("Health Telemetry: init");
// Initialize sensors
if (mlx90614Sensor.hasSensor())
result = mlx90614Sensor.runOnce();
if (max30102Sensor.hasSensor())
result = max30102Sensor.runOnce();
}
return result == UINT32_MAX ? disable() : setStartDelay();
} else {
// if we somehow got to a second run of this module with measurement disabled, then just wait forever
if (!moduleConfig.telemetry.health_measurement_enabled) {
return disable();
}
uint32_t result = UINT32_MAX;
if (!(moduleConfig.telemetry.health_measurement_enabled || moduleConfig.telemetry.health_screen_enabled)) {
// If this module is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it
return disable();
if (((lastSentToMesh == 0) || !Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled(
moduleConfig.telemetry.health_update_interval,
default_telemetry_broadcast_interval_secs, numOnlineNodes))) &&
airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) && airTime->isTxAllowedAirUtil()) {
sendTelemetry();
lastSentToMesh = millis();
} else if (((lastSentToPhone == 0) || !Throttle::isWithinTimespanMs(lastSentToPhone, sendToPhoneIntervalMs)) &&
(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);
lastSentToPhone = millis();
}
if (firstTime) {
// This is the first time the OSThread library has called this function, so do some setup
firstTime = false;
if (moduleConfig.telemetry.health_measurement_enabled) {
LOG_INFO("Health Telemetry: init");
// Initialize sensors
if (mlx90614Sensor.hasSensor())
result = mlx90614Sensor.runOnce();
if (max30102Sensor.hasSensor())
result = max30102Sensor.runOnce();
}
return result == UINT32_MAX ? disable() : setStartDelay();
} else {
// if we somehow got to a second run of this module with measurement disabled, then just wait forever
if (!moduleConfig.telemetry.health_measurement_enabled) {
return disable();
}
if (((lastSentToMesh == 0) ||
!Throttle::isWithinTimespanMs(lastSentToMesh, Default::getConfiguredOrDefaultMsScaled(
moduleConfig.telemetry.health_update_interval,
default_telemetry_broadcast_interval_secs, numOnlineNodes))) &&
airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
airTime->isTxAllowedAirUtil()) {
sendTelemetry();
lastSentToMesh = millis();
} else if (((lastSentToPhone == 0) || !Throttle::isWithinTimespanMs(lastSentToPhone, sendToPhoneIntervalMs)) &&
(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);
lastSentToPhone = millis();
}
}
return min(sendToPhoneIntervalMs, result);
}
return min(sendToPhoneIntervalMs, result);
}
bool HealthTelemetryModule::wantUIFrame()
{
return moduleConfig.telemetry.health_screen_enabled;
bool HealthTelemetryModule::wantUIFrame() { return moduleConfig.telemetry.health_screen_enabled; }
void HealthTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) {
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_SMALL);
if (lastMeasurementPacket == nullptr) {
// If there's no valid packet, display "Health"
display->drawString(x, y, "Health");
display->drawString(x, y += _fontHeight(FONT_SMALL), "No measurement");
return;
}
// Decode the last measurement packet
meshtastic_Telemetry lastMeasurement;
uint32_t agoSecs = service->GetTimeSinceMeshPacket(lastMeasurementPacket);
const char *lastSender = getSenderShortName(*lastMeasurementPacket);
const meshtastic_Data &p = lastMeasurementPacket->decoded;
if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &lastMeasurement)) {
display->drawString(x, y, "Measurement Error");
LOG_ERROR("Unable to decode last packet");
return;
}
// Display "Health From: ..." on its own
char headerStr[64];
snprintf(headerStr, sizeof(headerStr), "Health From: %s(%ds)", lastSender, (int)agoSecs);
display->drawString(x, y, headerStr);
char last_temp[16];
if (moduleConfig.telemetry.environment_display_fahrenheit) {
snprintf(last_temp, sizeof(last_temp), "%.0f°F", UnitConversions::CelsiusToFahrenheit(lastMeasurement.variant.health_metrics.temperature));
} else {
snprintf(last_temp, sizeof(last_temp), "%.0f°C", lastMeasurement.variant.health_metrics.temperature);
}
// Continue with the remaining details
char tempStr[32];
snprintf(tempStr, sizeof(tempStr), "Temp: %s", last_temp);
display->drawString(x, y += _fontHeight(FONT_SMALL), tempStr);
if (lastMeasurement.variant.health_metrics.has_heart_bpm) {
char heartStr[32];
snprintf(heartStr, sizeof(heartStr), "Heart Rate: %.0f bpm", lastMeasurement.variant.health_metrics.heart_bpm);
display->drawString(x, y += _fontHeight(FONT_SMALL), heartStr);
}
if (lastMeasurement.variant.health_metrics.has_spO2) {
char spo2Str[32];
snprintf(spo2Str, sizeof(spo2Str), "spO2: %.0f %%", lastMeasurement.variant.health_metrics.spO2);
display->drawString(x, y += _fontHeight(FONT_SMALL), spo2Str);
}
}
void HealthTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_SMALL);
if (lastMeasurementPacket == nullptr) {
// If there's no valid packet, display "Health"
display->drawString(x, y, "Health");
display->drawString(x, y += _fontHeight(FONT_SMALL), "No measurement");
return;
}
// Decode the last measurement packet
meshtastic_Telemetry lastMeasurement;
uint32_t agoSecs = service->GetTimeSinceMeshPacket(lastMeasurementPacket);
const char *lastSender = getSenderShortName(*lastMeasurementPacket);
const meshtastic_Data &p = lastMeasurementPacket->decoded;
if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &lastMeasurement)) {
display->drawString(x, y, "Measurement Error");
LOG_ERROR("Unable to decode last packet");
return;
}
// Display "Health From: ..." on its own
char headerStr[64];
snprintf(headerStr, sizeof(headerStr), "Health From: %s(%ds)", lastSender, (int)agoSecs);
display->drawString(x, y, headerStr);
char last_temp[16];
if (moduleConfig.telemetry.environment_display_fahrenheit) {
snprintf(last_temp, sizeof(last_temp), "%.0f°F",
UnitConversions::CelsiusToFahrenheit(lastMeasurement.variant.health_metrics.temperature));
} else {
snprintf(last_temp, sizeof(last_temp), "%.0f°C", lastMeasurement.variant.health_metrics.temperature);
}
// Continue with the remaining details
char tempStr[32];
snprintf(tempStr, sizeof(tempStr), "Temp: %s", last_temp);
display->drawString(x, y += _fontHeight(FONT_SMALL), tempStr);
if (lastMeasurement.variant.health_metrics.has_heart_bpm) {
char heartStr[32];
snprintf(heartStr, sizeof(heartStr), "Heart Rate: %.0f bpm", lastMeasurement.variant.health_metrics.heart_bpm);
display->drawString(x, y += _fontHeight(FONT_SMALL), heartStr);
}
if (lastMeasurement.variant.health_metrics.has_spO2) {
char spo2Str[32];
snprintf(spo2Str, sizeof(spo2Str), "spO2: %.0f %%", lastMeasurement.variant.health_metrics.spO2);
display->drawString(x, y += _fontHeight(FONT_SMALL), spo2Str);
}
}
bool HealthTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t)
{
if (t->which_variant == meshtastic_Telemetry_health_metrics_tag) {
bool HealthTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t) {
if (t->which_variant == meshtastic_Telemetry_health_metrics_tag) {
#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE)
const char *sender = getSenderShortName(mp);
const char *sender = getSenderShortName(mp);
LOG_INFO("(Received from %s): temperature=%f, heart_bpm=%d, spO2=%d,", sender, t->variant.health_metrics.temperature,
t->variant.health_metrics.heart_bpm, t->variant.health_metrics.spO2);
LOG_INFO("(Received from %s): temperature=%f, heart_bpm=%d, spO2=%d,", sender, t->variant.health_metrics.temperature,
t->variant.health_metrics.heart_bpm, t->variant.health_metrics.spO2);
#endif
// release previous packet before occupying a new spot
if (lastMeasurementPacket != nullptr)
packetPool.release(lastMeasurementPacket);
// release previous packet before occupying a new spot
if (lastMeasurementPacket != nullptr)
packetPool.release(lastMeasurementPacket);
lastMeasurementPacket = packetPool.allocCopy(mp);
}
lastMeasurementPacket = packetPool.allocCopy(mp);
}
return false; // Let others look at this message also if they want
return false; // Let others look at this message also if they want
}
bool HealthTelemetryModule::getHealthTelemetry(meshtastic_Telemetry *m)
{
bool valid = true;
bool hasSensor = false;
m->time = getTime();
m->which_variant = meshtastic_Telemetry_health_metrics_tag;
m->variant.health_metrics = meshtastic_HealthMetrics_init_zero;
bool HealthTelemetryModule::getHealthTelemetry(meshtastic_Telemetry *m) {
bool valid = true;
bool hasSensor = false;
m->time = getTime();
m->which_variant = meshtastic_Telemetry_health_metrics_tag;
m->variant.health_metrics = meshtastic_HealthMetrics_init_zero;
if (max30102Sensor.hasSensor()) {
valid = valid && max30102Sensor.getMetrics(m);
hasSensor = true;
}
if (mlx90614Sensor.hasSensor()) {
valid = valid && mlx90614Sensor.getMetrics(m);
hasSensor = true;
}
if (max30102Sensor.hasSensor()) {
valid = valid && max30102Sensor.getMetrics(m);
hasSensor = true;
}
if (mlx90614Sensor.hasSensor()) {
valid = valid && mlx90614Sensor.getMetrics(m);
hasSensor = true;
}
return valid && hasSensor;
return valid && hasSensor;
}
meshtastic_MeshPacket *HealthTelemetryModule::allocReply()
{
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 {
LOG_ERROR("Error decoding HealthTelemetry module!");
return NULL;
}
// Check for a request for health metrics
if (decoded->which_variant == meshtastic_Telemetry_health_metrics_tag) {
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
if (getHealthTelemetry(&m)) {
LOG_INFO("Health telemetry reply to request");
return allocDataProtobuf(m);
} else {
return NULL;
}
}
meshtastic_MeshPacket *HealthTelemetryModule::allocReply() {
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 {
LOG_ERROR("Error decoding HealthTelemetry module!");
return NULL;
}
return NULL;
// Check for a request for health metrics
if (decoded->which_variant == meshtastic_Telemetry_health_metrics_tag) {
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
if (getHealthTelemetry(&m)) {
LOG_INFO("Health telemetry reply to request");
return allocDataProtobuf(m);
} else {
return NULL;
}
}
}
return NULL;
}
bool HealthTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
{
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
m.which_variant = meshtastic_Telemetry_health_metrics_tag;
m.time = getTime();
if (getHealthTelemetry(&m)) {
LOG_INFO("Send: temperature=%f, heart_bpm=%d, spO2=%d", m.variant.health_metrics.temperature,
m.variant.health_metrics.heart_bpm, m.variant.health_metrics.spO2);
bool HealthTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) {
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
m.which_variant = meshtastic_Telemetry_health_metrics_tag;
m.time = getTime();
if (getHealthTelemetry(&m)) {
LOG_INFO("Send: temperature=%f, heart_bpm=%d, spO2=%d", m.variant.health_metrics.temperature, m.variant.health_metrics.heart_bpm,
m.variant.health_metrics.spO2);
sensor_read_error_count = 0;
sensor_read_error_count = 0;
meshtastic_MeshPacket *p = allocDataProtobuf(m);
p->to = dest;
p->decoded.want_response = false;
if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR)
p->priority = meshtastic_MeshPacket_Priority_RELIABLE;
else
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
// release previous packet before occupying a new spot
if (lastMeasurementPacket != nullptr)
packetPool.release(lastMeasurementPacket);
meshtastic_MeshPacket *p = allocDataProtobuf(m);
p->to = dest;
p->decoded.want_response = false;
if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR)
p->priority = meshtastic_MeshPacket_Priority_RELIABLE;
else
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
// release previous packet before occupying a new spot
if (lastMeasurementPacket != nullptr)
packetPool.release(lastMeasurementPacket);
lastMeasurementPacket = packetPool.allocCopy(*p);
if (phoneOnly) {
LOG_INFO("Send packet to phone");
service->sendToPhone(p);
} else {
LOG_INFO("Send packet to mesh");
service->sendToMesh(p, RX_SRC_LOCAL, true);
lastMeasurementPacket = packetPool.allocCopy(*p);
if (phoneOnly) {
LOG_INFO("Send packet to phone");
service->sendToPhone(p);
} else {
LOG_INFO("Send packet to mesh");
service->sendToMesh(p, RX_SRC_LOCAL, true);
if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR && config.power.is_power_saving) {
LOG_DEBUG("Start next execution in 5s, then sleep");
sleepOnNextExecution = true;
setIntervalFromNow(5000);
}
}
return true;
if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR && config.power.is_power_saving) {
LOG_DEBUG("Start next execution in 5s, then sleep");
sleepOnNextExecution = true;
setIntervalFromNow(5000);
}
}
return false;
return true;
}
return false;
}
#endif

View File

@@ -9,52 +9,49 @@
#include <OLEDDisplay.h>
#include <OLEDDisplayUi.h>
class HealthTelemetryModule : private concurrency::OSThread, public ProtobufModule<meshtastic_Telemetry>
{
CallbackObserver<HealthTelemetryModule, const meshtastic::Status *> nodeStatusObserver =
CallbackObserver<HealthTelemetryModule, const meshtastic::Status *>(this, &HealthTelemetryModule::handleStatusUpdate);
class HealthTelemetryModule : private concurrency::OSThread, public ProtobufModule<meshtastic_Telemetry> {
CallbackObserver<HealthTelemetryModule, const meshtastic::Status *> nodeStatusObserver =
CallbackObserver<HealthTelemetryModule, const meshtastic::Status *>(this, &HealthTelemetryModule::handleStatusUpdate);
public:
HealthTelemetryModule()
: concurrency::OSThread("HealthTelemetry"),
ProtobufModule("HealthTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg)
{
lastMeasurementPacket = nullptr;
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
setIntervalFromNow(10 * 1000);
}
public:
HealthTelemetryModule()
: concurrency::OSThread("HealthTelemetry"), ProtobufModule("HealthTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg) {
lastMeasurementPacket = nullptr;
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
setIntervalFromNow(10 * 1000);
}
#if !HAS_SCREEN
void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
#else
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override;
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override;
#endif
virtual bool wantUIFrame() override;
virtual bool wantUIFrame() override;
protected:
/** Called to handle a particular incoming message
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
virtual int32_t runOnce() override;
/** Called to get current Health telemetry data
@return true if it contains valid data
*/
bool getHealthTelemetry(meshtastic_Telemetry *m);
virtual meshtastic_MeshPacket *allocReply() override;
/**
* Send our Telemetry into the mesh
*/
bool sendTelemetry(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
protected:
/** Called to handle a particular incoming message
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
virtual int32_t runOnce() override;
/** Called to get current Health telemetry data
@return true if it contains valid data
*/
bool getHealthTelemetry(meshtastic_Telemetry *m);
virtual meshtastic_MeshPacket *allocReply() override;
/**
* Send our Telemetry into the mesh
*/
bool sendTelemetry(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
private:
bool firstTime = 1;
meshtastic_MeshPacket *lastMeasurementPacket;
uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute
uint32_t lastSentToMesh = 0;
uint32_t lastSentToPhone = 0;
uint32_t sensor_read_error_count = 0;
private:
bool firstTime = 1;
meshtastic_MeshPacket *lastMeasurementPacket;
uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute
uint32_t lastSentToMesh = 0;
uint32_t lastSentToPhone = 0;
uint32_t sensor_read_error_count = 0;
};
#endif

View File

@@ -6,37 +6,34 @@
#include <filesystem>
#endif
int32_t HostMetricsModule::runOnce()
{
int32_t HostMetricsModule::runOnce() {
#if ARCH_PORTDUINO
if (portduino_config.hostMetrics_interval == 0) {
return disable();
} else {
sendMetrics();
return 60 * 1000 * portduino_config.hostMetrics_interval;
}
#else
if (portduino_config.hostMetrics_interval == 0) {
return disable();
} else {
sendMetrics();
return 60 * 1000 * portduino_config.hostMetrics_interval;
}
#else
return disable();
#endif
}
bool HostMetricsModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t)
{
if (t->which_variant == meshtastic_Telemetry_host_metrics_tag) {
bool HostMetricsModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t) {
if (t->which_variant == meshtastic_Telemetry_host_metrics_tag) {
#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE)
const char *sender = getSenderShortName(mp);
if (t->variant.host_metrics.has_user_string)
t->variant.host_metrics.user_string[sizeof(t->variant.host_metrics.user_string) - 1] = '\0';
const char *sender = getSenderShortName(mp);
if (t->variant.host_metrics.has_user_string)
t->variant.host_metrics.user_string[sizeof(t->variant.host_metrics.user_string) - 1] = '\0';
LOG_INFO("(Received Host Metrics from %s): uptime=%u, diskfree=%lu, memory free=%lu, load=%04.2f, %04.2f, %04.2f", sender,
t->variant.host_metrics.uptime_seconds, t->variant.host_metrics.diskfree1_bytes,
t->variant.host_metrics.freemem_bytes, static_cast<float>(t->variant.host_metrics.load1) / 100,
static_cast<float>(t->variant.host_metrics.load5) / 100,
static_cast<float>(t->variant.host_metrics.load15) / 100);
// t->variant.host_metrics.has_user_string ? t->variant.host_metrics.user_string : "");
LOG_INFO("(Received Host Metrics from %s): uptime=%u, diskfree=%lu, memory free=%lu, load=%04.2f, %04.2f, %04.2f", sender,
t->variant.host_metrics.uptime_seconds, t->variant.host_metrics.diskfree1_bytes, t->variant.host_metrics.freemem_bytes,
static_cast<float>(t->variant.host_metrics.load1) / 100, static_cast<float>(t->variant.host_metrics.load5) / 100,
static_cast<float>(t->variant.host_metrics.load15) / 100);
// t->variant.host_metrics.has_user_string ? t->variant.host_metrics.user_string : "");
#endif
}
return false; // Let others look at this message also if they want
}
return false; // Let others look at this message also if they want
}
/*
@@ -65,75 +62,72 @@ meshtastic_MeshPacket *HostMetricsModule::allocReply()
*/
#if ARCH_PORTDUINO
meshtastic_Telemetry HostMetricsModule::getHostMetrics()
{
std::string file_line;
meshtastic_Telemetry t = meshtastic_Telemetry_init_zero;
t.which_variant = meshtastic_Telemetry_host_metrics_tag;
t.variant.host_metrics = meshtastic_HostMetrics_init_zero;
meshtastic_Telemetry HostMetricsModule::getHostMetrics() {
std::string file_line;
meshtastic_Telemetry t = meshtastic_Telemetry_init_zero;
t.which_variant = meshtastic_Telemetry_host_metrics_tag;
t.variant.host_metrics = meshtastic_HostMetrics_init_zero;
if (access("/proc/uptime", R_OK) == 0) {
std::ifstream proc_uptime("/proc/uptime");
if (proc_uptime.is_open()) {
std::getline(proc_uptime, file_line, ' ');
proc_uptime.close();
t.variant.host_metrics.uptime_seconds = stoul(file_line);
}
if (access("/proc/uptime", R_OK) == 0) {
std::ifstream proc_uptime("/proc/uptime");
if (proc_uptime.is_open()) {
std::getline(proc_uptime, file_line, ' ');
proc_uptime.close();
t.variant.host_metrics.uptime_seconds = stoul(file_line);
}
}
std::filesystem::space_info root = std::filesystem::space("/");
t.variant.host_metrics.diskfree1_bytes = root.available;
std::filesystem::space_info root = std::filesystem::space("/");
t.variant.host_metrics.diskfree1_bytes = root.available;
if (access("/proc/meminfo", R_OK) == 0) {
std::ifstream proc_meminfo("/proc/meminfo");
if (proc_meminfo.is_open()) {
do {
std::getline(proc_meminfo, file_line);
} while (file_line.find("MemAvailable") == std::string::npos);
proc_meminfo.close();
t.variant.host_metrics.freemem_bytes = stoull(file_line.substr(file_line.find_first_of("0123456789"))) * 1024;
}
if (access("/proc/meminfo", R_OK) == 0) {
std::ifstream proc_meminfo("/proc/meminfo");
if (proc_meminfo.is_open()) {
do {
std::getline(proc_meminfo, file_line);
} while (file_line.find("MemAvailable") == std::string::npos);
proc_meminfo.close();
t.variant.host_metrics.freemem_bytes = stoull(file_line.substr(file_line.find_first_of("0123456789"))) * 1024;
}
if (access("/proc/loadavg", R_OK) == 0) {
std::ifstream proc_loadavg("/proc/loadavg");
if (proc_loadavg.is_open()) {
std::getline(proc_loadavg, file_line, ' ');
t.variant.host_metrics.load1 = stof(file_line) * 100;
std::getline(proc_loadavg, file_line, ' ');
t.variant.host_metrics.load5 = stof(file_line) * 100;
std::getline(proc_loadavg, file_line, ' ');
t.variant.host_metrics.load15 = stof(file_line) * 100;
proc_loadavg.close();
}
}
if (access("/proc/loadavg", R_OK) == 0) {
std::ifstream proc_loadavg("/proc/loadavg");
if (proc_loadavg.is_open()) {
std::getline(proc_loadavg, file_line, ' ');
t.variant.host_metrics.load1 = stof(file_line) * 100;
std::getline(proc_loadavg, file_line, ' ');
t.variant.host_metrics.load5 = stof(file_line) * 100;
std::getline(proc_loadavg, file_line, ' ');
t.variant.host_metrics.load15 = stof(file_line) * 100;
proc_loadavg.close();
}
if (portduino_config.hostMetrics_user_command != "") {
std::string userCommandResult = exec(portduino_config.hostMetrics_user_command.c_str());
if (userCommandResult.length() > 1) {
strncpy(t.variant.host_metrics.user_string, userCommandResult.c_str(), sizeof(t.variant.host_metrics.user_string));
t.variant.host_metrics.user_string[sizeof(t.variant.host_metrics.user_string) - 1] = '\0';
t.variant.host_metrics.has_user_string = true;
}
}
if (portduino_config.hostMetrics_user_command != "") {
std::string userCommandResult = exec(portduino_config.hostMetrics_user_command.c_str());
if (userCommandResult.length() > 1) {
strncpy(t.variant.host_metrics.user_string, userCommandResult.c_str(), sizeof(t.variant.host_metrics.user_string));
t.variant.host_metrics.user_string[sizeof(t.variant.host_metrics.user_string) - 1] = '\0';
t.variant.host_metrics.has_user_string = true;
}
return t;
}
return t;
}
bool HostMetricsModule::sendMetrics()
{
meshtastic_Telemetry telemetry = getHostMetrics();
LOG_INFO("Send: uptime=%u, diskfree=%lu, memory free=%lu, load=%04.2f, %04.2f, %04.2f",
telemetry.variant.host_metrics.uptime_seconds, telemetry.variant.host_metrics.diskfree1_bytes,
telemetry.variant.host_metrics.freemem_bytes, static_cast<float>(telemetry.variant.host_metrics.load1) / 100,
static_cast<float>(telemetry.variant.host_metrics.load5) / 100,
static_cast<float>(telemetry.variant.host_metrics.load15) / 100);
// telemetry.variant.host_metrics.has_user_string ? telemetry.variant.host_metrics.user_string : "");
bool HostMetricsModule::sendMetrics() {
meshtastic_Telemetry telemetry = getHostMetrics();
LOG_INFO("Send: uptime=%u, diskfree=%lu, memory free=%lu, load=%04.2f, %04.2f, %04.2f", telemetry.variant.host_metrics.uptime_seconds,
telemetry.variant.host_metrics.diskfree1_bytes, telemetry.variant.host_metrics.freemem_bytes,
static_cast<float>(telemetry.variant.host_metrics.load1) / 100, static_cast<float>(telemetry.variant.host_metrics.load5) / 100,
static_cast<float>(telemetry.variant.host_metrics.load15) / 100);
// telemetry.variant.host_metrics.has_user_string ? telemetry.variant.host_metrics.user_string : "");
meshtastic_MeshPacket *p = allocDataProtobuf(telemetry);
p->to = NODENUM_BROADCAST;
p->decoded.want_response = false;
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
p->channel = portduino_config.hostMetrics_channel;
LOG_INFO("Send packet to mesh");
service->sendToMesh(p, RX_SRC_LOCAL, true);
return true;
meshtastic_MeshPacket *p = allocDataProtobuf(telemetry);
p->to = NODENUM_BROADCAST;
p->decoded.want_response = false;
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
p->channel = portduino_config.hostMetrics_channel;
LOG_INFO("Send packet to mesh");
service->sendToMesh(p, RX_SRC_LOCAL, true);
return true;
}
#endif

View File

@@ -2,39 +2,36 @@
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "ProtobufModule.h"
class HostMetricsModule : private concurrency::OSThread, public ProtobufModule<meshtastic_Telemetry>
{
CallbackObserver<HostMetricsModule, const meshtastic::Status *> nodeStatusObserver =
CallbackObserver<HostMetricsModule, const meshtastic::Status *>(this, &HostMetricsModule::handleStatusUpdate);
class HostMetricsModule : private concurrency::OSThread, public ProtobufModule<meshtastic_Telemetry> {
CallbackObserver<HostMetricsModule, const meshtastic::Status *> nodeStatusObserver =
CallbackObserver<HostMetricsModule, const meshtastic::Status *>(this, &HostMetricsModule::handleStatusUpdate);
public:
HostMetricsModule()
: concurrency::OSThread("HostMetrics"),
ProtobufModule("HostMetrics", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg)
{
uptimeWrapCount = 0;
uptimeLastMs = millis();
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
setIntervalFromNow(setStartDelay()); // Wait until NodeInfo is sent
}
virtual bool wantUIFrame() { return false; }
public:
HostMetricsModule()
: concurrency::OSThread("HostMetrics"), ProtobufModule("HostMetrics", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg) {
uptimeWrapCount = 0;
uptimeLastMs = millis();
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
setIntervalFromNow(setStartDelay()); // Wait until NodeInfo is sent
}
virtual bool wantUIFrame() { return false; }
protected:
/** Called to handle a particular incoming message
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
// virtual meshtastic_MeshPacket *allocReply() override;
virtual int32_t runOnce() override;
/**
* Send our Telemetry into the mesh
*/
bool sendMetrics();
protected:
/** Called to handle a particular incoming message
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
// virtual meshtastic_MeshPacket *allocReply() override;
virtual int32_t runOnce() override;
/**
* Send our Telemetry into the mesh
*/
bool sendMetrics();
private:
meshtastic_Telemetry getHostMetrics();
private:
meshtastic_Telemetry getHostMetrics();
uint32_t lastSentToMesh = 0;
uint32_t uptimeWrapCount;
uint32_t uptimeLastMs;
uint32_t lastSentToMesh = 0;
uint32_t uptimeWrapCount;
uint32_t uptimeLastMs;
};

View File

@@ -22,268 +22,255 @@
#include "graphics/ScreenFonts.h"
#include <Throttle.h>
namespace graphics
{
extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr, bool force_no_invert,
bool show_date);
namespace graphics {
extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr, bool force_no_invert, bool show_date);
}
int32_t PowerTelemetryModule::runOnce()
{
if (sleepOnNextExecution == true) {
sleepOnNextExecution = false;
uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.power_update_interval,
default_telemetry_broadcast_interval_secs);
LOG_DEBUG("Sleep for %ims, then awake to send metrics again", nightyNightMs);
doDeepSleep(nightyNightMs, true, false);
}
int32_t PowerTelemetryModule::runOnce() {
if (sleepOnNextExecution == true) {
sleepOnNextExecution = false;
uint32_t nightyNightMs =
Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.power_update_interval, default_telemetry_broadcast_interval_secs);
LOG_DEBUG("Sleep for %ims, then awake to send metrics again", nightyNightMs);
doDeepSleep(nightyNightMs, true, false);
}
/*
Uncomment the preferences below if you want to use the module
without having to configure it from the PythonAPI or WebUI.
*/
/*
Uncomment the preferences below if you want to use the module
without having to configure it from the PythonAPI or WebUI.
*/
// moduleConfig.telemetry.power_measurement_enabled = 1;
// moduleConfig.telemetry.power_screen_enabled = 1;
// moduleConfig.telemetry.power_update_interval = 45;
// moduleConfig.telemetry.power_measurement_enabled = 1;
// moduleConfig.telemetry.power_screen_enabled = 1;
// moduleConfig.telemetry.power_update_interval = 45;
if (!(moduleConfig.telemetry.power_measurement_enabled)) {
// If this module is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it
return disable();
}
if (!(moduleConfig.telemetry.power_measurement_enabled)) {
// If this module is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it
return disable();
}
uint32_t sendToMeshIntervalMs = Default::getConfiguredOrDefaultMsScaled(
moduleConfig.telemetry.power_update_interval, default_telemetry_broadcast_interval_secs, numOnlineNodes);
uint32_t sendToMeshIntervalMs = Default::getConfiguredOrDefaultMsScaled(moduleConfig.telemetry.power_update_interval,
default_telemetry_broadcast_interval_secs, numOnlineNodes);
if (firstTime) {
// This is the first time the OSThread library has called this function, so do some setup
firstTime = 0;
uint32_t result = UINT32_MAX;
if (firstTime) {
// This is the first time the OSThread library has called this function, so do some setup
firstTime = 0;
uint32_t result = UINT32_MAX;
#if HAS_TELEMETRY
if (moduleConfig.telemetry.power_measurement_enabled) {
LOG_INFO("Power Telemetry: init");
// If sensor is already initialized by EnvironmentTelemetryModule, then we don't need to initialize it again,
// but we need to set the result to != UINT32_MAX to avoid it being disabled
if (ina219Sensor.hasSensor())
result = ina219Sensor.isInitialized() ? 0 : ina219Sensor.runOnce();
if (ina226Sensor.hasSensor())
result = ina226Sensor.isInitialized() ? 0 : ina226Sensor.runOnce();
if (ina260Sensor.hasSensor())
result = ina260Sensor.isInitialized() ? 0 : ina260Sensor.runOnce();
if (ina3221Sensor.hasSensor())
result = ina3221Sensor.isInitialized() ? 0 : ina3221Sensor.runOnce();
if (max17048Sensor.hasSensor())
result = max17048Sensor.isInitialized() ? 0 : max17048Sensor.runOnce();
}
// 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
return result == UINT32_MAX ? disable() : setStartDelay();
#else
return disable();
#endif
} else {
// if we somehow got to a second run of this module with measurement disabled, then just wait forever
if (!moduleConfig.telemetry.power_measurement_enabled)
return disable();
if (((lastSentToMesh == 0) || !Throttle::isWithinTimespanMs(lastSentToMesh, sendToMeshIntervalMs)) &&
airTime->isTxAllowedAirUtil()) {
sendTelemetry();
lastSentToMesh = millis();
} else if (((lastSentToPhone == 0) || !Throttle::isWithinTimespanMs(lastSentToPhone, sendToPhoneIntervalMs)) &&
(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);
lastSentToPhone = millis();
}
if (moduleConfig.telemetry.power_measurement_enabled) {
LOG_INFO("Power Telemetry: init");
// If sensor is already initialized by EnvironmentTelemetryModule, then we don't need to initialize it again,
// but we need to set the result to != UINT32_MAX to avoid it being disabled
if (ina219Sensor.hasSensor())
result = ina219Sensor.isInitialized() ? 0 : ina219Sensor.runOnce();
if (ina226Sensor.hasSensor())
result = ina226Sensor.isInitialized() ? 0 : ina226Sensor.runOnce();
if (ina260Sensor.hasSensor())
result = ina260Sensor.isInitialized() ? 0 : ina260Sensor.runOnce();
if (ina3221Sensor.hasSensor())
result = ina3221Sensor.isInitialized() ? 0 : ina3221Sensor.runOnce();
if (max17048Sensor.hasSensor())
result = max17048Sensor.isInitialized() ? 0 : max17048Sensor.runOnce();
}
return min(sendToPhoneIntervalMs, sendToMeshIntervalMs);
// 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
return result == UINT32_MAX ? disable() : setStartDelay();
#else
return disable();
#endif
} else {
// if we somehow got to a second run of this module with measurement disabled, then just wait forever
if (!moduleConfig.telemetry.power_measurement_enabled)
return disable();
if (((lastSentToMesh == 0) || !Throttle::isWithinTimespanMs(lastSentToMesh, sendToMeshIntervalMs)) && airTime->isTxAllowedAirUtil()) {
sendTelemetry();
lastSentToMesh = millis();
} else if (((lastSentToPhone == 0) || !Throttle::isWithinTimespanMs(lastSentToPhone, sendToPhoneIntervalMs)) &&
(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);
lastSentToPhone = millis();
}
}
return min(sendToPhoneIntervalMs, sendToMeshIntervalMs);
}
bool PowerTelemetryModule::wantUIFrame()
{
return moduleConfig.telemetry.power_screen_enabled;
}
bool PowerTelemetryModule::wantUIFrame() { return moduleConfig.telemetry.power_screen_enabled; }
#if HAS_SCREEN
void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
display->clear();
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_SMALL);
int line = 1;
void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) {
display->clear();
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_SMALL);
int line = 1;
// === Set Title
const char *titleStr = (graphics::currentResolution == graphics::ScreenResolution::High) ? "Power Telem." : "Power";
// === Set Title
const char *titleStr = (graphics::currentResolution == graphics::ScreenResolution::High) ? "Power Telem." : "Power";
// === Header ===
graphics::drawCommonHeader(display, x, y, titleStr);
// === Header ===
graphics::drawCommonHeader(display, x, y, titleStr);
if (lastMeasurementPacket == nullptr) {
// In case of no valid packet, display "Power Telemetry", "No measurement"
display->drawString(x, graphics::getTextPositions(display)[line++], "No measurement");
return;
}
if (lastMeasurementPacket == nullptr) {
// In case of no valid packet, display "Power Telemetry", "No measurement"
display->drawString(x, graphics::getTextPositions(display)[line++], "No measurement");
return;
}
// Decode the last power packet
meshtastic_Telemetry lastMeasurement;
uint32_t agoSecs = service->GetTimeSinceMeshPacket(lastMeasurementPacket);
const char *lastSender = getSenderShortName(*lastMeasurementPacket);
// Decode the last power packet
meshtastic_Telemetry lastMeasurement;
uint32_t agoSecs = service->GetTimeSinceMeshPacket(lastMeasurementPacket);
const char *lastSender = getSenderShortName(*lastMeasurementPacket);
const meshtastic_Data &p = lastMeasurementPacket->decoded;
if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &lastMeasurement)) {
display->drawString(x, graphics::getTextPositions(display)[line++], "Measurement Error");
LOG_ERROR("Unable to decode last packet");
return;
}
const meshtastic_Data &p = lastMeasurementPacket->decoded;
if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &lastMeasurement)) {
display->drawString(x, graphics::getTextPositions(display)[line++], "Measurement Error");
LOG_ERROR("Unable to decode last packet");
return;
}
// Display "Pow. From: ..."
char fromStr[64];
snprintf(fromStr, sizeof(fromStr), "Pow. From: %s (%us)", lastSender, agoSecs);
display->drawString(x, graphics::getTextPositions(display)[line++], fromStr);
// Display "Pow. From: ..."
char fromStr[64];
snprintf(fromStr, sizeof(fromStr), "Pow. From: %s (%us)", lastSender, agoSecs);
display->drawString(x, graphics::getTextPositions(display)[line++], fromStr);
// Display current and voltage based on ...power_metrics.has_[channel/voltage/current]... flags
const auto &m = lastMeasurement.variant.power_metrics;
int lineY = textSecondLine;
// Display current and voltage based on ...power_metrics.has_[channel/voltage/current]... flags
const auto &m = lastMeasurement.variant.power_metrics;
int lineY = textSecondLine;
auto drawLine = [&](const char *label, float voltage, float current) {
char lineStr[64];
snprintf(lineStr, sizeof(lineStr), "%s: %.2fV %.0fmA", label, voltage, current);
display->drawString(x, lineY, lineStr);
lineY += _fontHeight(FONT_SMALL);
};
auto drawLine = [&](const char *label, float voltage, float current) {
char lineStr[64];
snprintf(lineStr, sizeof(lineStr), "%s: %.2fV %.0fmA", label, voltage, current);
display->drawString(x, lineY, lineStr);
lineY += _fontHeight(FONT_SMALL);
};
if (m.has_ch1_voltage || m.has_ch1_current) {
drawLine("Ch1", m.ch1_voltage, m.ch1_current);
}
if (m.has_ch2_voltage || m.has_ch2_current) {
drawLine("Ch2", m.ch2_voltage, m.ch2_current);
}
if (m.has_ch3_voltage || m.has_ch3_current) {
drawLine("Ch3", m.ch3_voltage, m.ch3_current);
}
graphics::drawCommonFooter(display, x, y);
if (m.has_ch1_voltage || m.has_ch1_current) {
drawLine("Ch1", m.ch1_voltage, m.ch1_current);
}
if (m.has_ch2_voltage || m.has_ch2_current) {
drawLine("Ch2", m.ch2_voltage, m.ch2_current);
}
if (m.has_ch3_voltage || m.has_ch3_current) {
drawLine("Ch3", m.ch3_voltage, m.ch3_current);
}
graphics::drawCommonFooter(display, x, y);
}
#endif
bool PowerTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t)
{
if (t->which_variant == meshtastic_Telemetry_power_metrics_tag) {
bool PowerTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t) {
if (t->which_variant == meshtastic_Telemetry_power_metrics_tag) {
#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE)
const char *sender = getSenderShortName(mp);
const char *sender = getSenderShortName(mp);
LOG_INFO("(Received from %s): ch1_voltage=%.1f, ch1_current=%.1f, ch2_voltage=%.1f, ch2_current=%.1f, "
"ch3_voltage=%.1f, ch3_current=%.1f",
sender, t->variant.power_metrics.ch1_voltage, t->variant.power_metrics.ch1_current,
t->variant.power_metrics.ch2_voltage, t->variant.power_metrics.ch2_current, t->variant.power_metrics.ch3_voltage,
t->variant.power_metrics.ch3_current);
LOG_INFO("(Received from %s): ch1_voltage=%.1f, ch1_current=%.1f, ch2_voltage=%.1f, ch2_current=%.1f, "
"ch3_voltage=%.1f, ch3_current=%.1f",
sender, t->variant.power_metrics.ch1_voltage, t->variant.power_metrics.ch1_current, t->variant.power_metrics.ch2_voltage,
t->variant.power_metrics.ch2_current, t->variant.power_metrics.ch3_voltage, t->variant.power_metrics.ch3_current);
#endif
// release previous packet before occupying a new spot
if (lastMeasurementPacket != nullptr)
packetPool.release(lastMeasurementPacket);
// release previous packet before occupying a new spot
if (lastMeasurementPacket != nullptr)
packetPool.release(lastMeasurementPacket);
lastMeasurementPacket = packetPool.allocCopy(mp);
}
lastMeasurementPacket = packetPool.allocCopy(mp);
}
return false; // Let others look at this message also if they want
return false; // Let others look at this message also if they want
}
bool PowerTelemetryModule::getPowerTelemetry(meshtastic_Telemetry *m)
{
bool valid = false;
m->time = getTime();
m->which_variant = meshtastic_Telemetry_power_metrics_tag;
bool PowerTelemetryModule::getPowerTelemetry(meshtastic_Telemetry *m) {
bool valid = false;
m->time = getTime();
m->which_variant = meshtastic_Telemetry_power_metrics_tag;
m->variant.power_metrics = meshtastic_PowerMetrics_init_zero;
m->variant.power_metrics = meshtastic_PowerMetrics_init_zero;
#if HAS_TELEMETRY
if (ina219Sensor.hasSensor())
valid = ina219Sensor.getMetrics(m);
if (ina226Sensor.hasSensor())
valid = ina226Sensor.getMetrics(m);
if (ina260Sensor.hasSensor())
valid = ina260Sensor.getMetrics(m);
if (ina3221Sensor.hasSensor())
valid = ina3221Sensor.getMetrics(m);
if (max17048Sensor.hasSensor())
valid = max17048Sensor.getMetrics(m);
if (ina219Sensor.hasSensor())
valid = ina219Sensor.getMetrics(m);
if (ina226Sensor.hasSensor())
valid = ina226Sensor.getMetrics(m);
if (ina260Sensor.hasSensor())
valid = ina260Sensor.getMetrics(m);
if (ina3221Sensor.hasSensor())
valid = ina3221Sensor.getMetrics(m);
if (max17048Sensor.hasSensor())
valid = max17048Sensor.getMetrics(m);
#endif
return valid;
return valid;
}
meshtastic_MeshPacket *PowerTelemetryModule::allocReply()
{
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 {
LOG_ERROR("Error decoding PowerTelemetry module!");
return NULL;
}
// Check for a request for power metrics
if (decoded->which_variant == meshtastic_Telemetry_power_metrics_tag) {
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
if (getPowerTelemetry(&m)) {
LOG_INFO("Power telemetry reply to request");
return allocDataProtobuf(m);
} else {
return NULL;
}
}
meshtastic_MeshPacket *PowerTelemetryModule::allocReply() {
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 {
LOG_ERROR("Error decoding PowerTelemetry module!");
return NULL;
}
// Check for a request for power metrics
if (decoded->which_variant == meshtastic_Telemetry_power_metrics_tag) {
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
if (getPowerTelemetry(&m)) {
LOG_INFO("Power telemetry reply to request");
return allocDataProtobuf(m);
} else {
return NULL;
}
}
}
return NULL;
return NULL;
}
bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
{
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
m.which_variant = meshtastic_Telemetry_power_metrics_tag;
m.time = getTime();
if (getPowerTelemetry(&m)) {
LOG_INFO("Send: ch1_voltage=%f, ch1_current=%f, ch2_voltage=%f, ch2_current=%f, "
"ch3_voltage=%f, ch3_current=%f",
m.variant.power_metrics.ch1_voltage, m.variant.power_metrics.ch1_current, m.variant.power_metrics.ch2_voltage,
m.variant.power_metrics.ch2_current, m.variant.power_metrics.ch3_voltage, m.variant.power_metrics.ch3_current);
bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) {
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
m.which_variant = meshtastic_Telemetry_power_metrics_tag;
m.time = getTime();
if (getPowerTelemetry(&m)) {
LOG_INFO("Send: ch1_voltage=%f, ch1_current=%f, ch2_voltage=%f, ch2_current=%f, "
"ch3_voltage=%f, ch3_current=%f",
m.variant.power_metrics.ch1_voltage, m.variant.power_metrics.ch1_current, m.variant.power_metrics.ch2_voltage,
m.variant.power_metrics.ch2_current, m.variant.power_metrics.ch3_voltage, m.variant.power_metrics.ch3_current);
sensor_read_error_count = 0;
sensor_read_error_count = 0;
meshtastic_MeshPacket *p = allocDataProtobuf(m);
p->to = dest;
p->decoded.want_response = false;
if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR)
p->priority = meshtastic_MeshPacket_Priority_RELIABLE;
else
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
// release previous packet before occupying a new spot
if (lastMeasurementPacket != nullptr)
packetPool.release(lastMeasurementPacket);
meshtastic_MeshPacket *p = allocDataProtobuf(m);
p->to = dest;
p->decoded.want_response = false;
if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR)
p->priority = meshtastic_MeshPacket_Priority_RELIABLE;
else
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
// release previous packet before occupying a new spot
if (lastMeasurementPacket != nullptr)
packetPool.release(lastMeasurementPacket);
lastMeasurementPacket = packetPool.allocCopy(*p);
if (phoneOnly) {
LOG_INFO("Send packet to phone");
service->sendToPhone(p);
} else {
LOG_INFO("Send packet to mesh");
service->sendToMesh(p, RX_SRC_LOCAL, true);
lastMeasurementPacket = packetPool.allocCopy(*p);
if (phoneOnly) {
LOG_INFO("Send packet to phone");
service->sendToPhone(p);
} else {
LOG_INFO("Send packet to mesh");
service->sendToMesh(p, RX_SRC_LOCAL, true);
if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR && config.power.is_power_saving) {
LOG_DEBUG("Start next execution in 5s then sleep");
sleepOnNextExecution = true;
setIntervalFromNow(5000);
}
}
return true;
if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR && config.power.is_power_saving) {
LOG_DEBUG("Start next execution in 5s then sleep");
sleepOnNextExecution = true;
setIntervalFromNow(5000);
}
}
return false;
return true;
}
return false;
}
#endif

View File

@@ -10,50 +10,47 @@
#include <OLEDDisplay.h>
#include <OLEDDisplayUi.h>
class PowerTelemetryModule : private concurrency::OSThread, public ProtobufModule<meshtastic_Telemetry>
{
CallbackObserver<PowerTelemetryModule, const meshtastic::Status *> nodeStatusObserver =
CallbackObserver<PowerTelemetryModule, const meshtastic::Status *>(this, &PowerTelemetryModule::handleStatusUpdate);
class PowerTelemetryModule : private concurrency::OSThread, public ProtobufModule<meshtastic_Telemetry> {
CallbackObserver<PowerTelemetryModule, const meshtastic::Status *> nodeStatusObserver =
CallbackObserver<PowerTelemetryModule, const meshtastic::Status *>(this, &PowerTelemetryModule::handleStatusUpdate);
public:
PowerTelemetryModule()
: concurrency::OSThread("PowerTelemetry"),
ProtobufModule("PowerTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg)
{
lastMeasurementPacket = nullptr;
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
setIntervalFromNow(10 * 1000);
}
virtual bool wantUIFrame() override;
public:
PowerTelemetryModule()
: concurrency::OSThread("PowerTelemetry"), ProtobufModule("PowerTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg) {
lastMeasurementPacket = nullptr;
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
setIntervalFromNow(10 * 1000);
}
virtual bool wantUIFrame() override;
#if !HAS_SCREEN
void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
#else
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override;
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override;
#endif
protected:
/** Called to handle a particular incoming message
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
virtual int32_t runOnce() override;
/** Called to get current Power telemetry data
@return true if it contains valid data
*/
bool getPowerTelemetry(meshtastic_Telemetry *m);
virtual meshtastic_MeshPacket *allocReply() override;
/**
* Send our Telemetry into the mesh
*/
bool sendTelemetry(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
protected:
/** Called to handle a particular incoming message
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
virtual int32_t runOnce() override;
/** Called to get current Power telemetry data
@return true if it contains valid data
*/
bool getPowerTelemetry(meshtastic_Telemetry *m);
virtual meshtastic_MeshPacket *allocReply() override;
/**
* Send our Telemetry into the mesh
*/
bool sendTelemetry(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
private:
bool firstTime = 1;
meshtastic_MeshPacket *lastMeasurementPacket;
uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute
uint32_t lastSentToMesh = 0;
uint32_t lastSentToPhone = 0;
uint32_t sensor_read_error_count = 0;
private:
bool firstTime = 1;
meshtastic_MeshPacket *lastMeasurementPacket;
uint32_t sendToPhoneIntervalMs = SECONDS_IN_MINUTE * 1000; // Send to phone every minute
uint32_t lastSentToMesh = 0;
uint32_t lastSentToPhone = 0;
uint32_t sensor_read_error_count = 0;
};
#endif

View File

@@ -15,35 +15,33 @@
AHT10Sensor::AHT10Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_AHT10, "AHT10") {}
bool AHT10Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
aht10 = Adafruit_AHTX0();
status = aht10.begin(bus, 0, dev->address.address);
bool AHT10Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
aht10 = Adafruit_AHTX0();
status = aht10.begin(bus, 0, dev->address.address);
initI2CSensor();
return status;
initI2CSensor();
return status;
}
bool AHT10Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
LOG_DEBUG("AHT10 getMetrics");
bool AHT10Sensor::getMetrics(meshtastic_Telemetry *measurement) {
LOG_DEBUG("AHT10 getMetrics");
sensors_event_t humidity, temp;
aht10.getEvent(&humidity, &temp);
sensors_event_t humidity, temp;
aht10.getEvent(&humidity, &temp);
// prefer other sensors like bmp280, bmp3xx
if (!measurement->variant.environment_metrics.has_temperature) {
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.temperature = temp.temperature + AHT10_TEMP_OFFSET;
}
// prefer other sensors like bmp280, bmp3xx
if (!measurement->variant.environment_metrics.has_temperature) {
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.temperature = temp.temperature + AHT10_TEMP_OFFSET;
}
if (!measurement->variant.environment_metrics.has_relative_humidity) {
measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.environment_metrics.relative_humidity = humidity.relative_humidity;
}
if (!measurement->variant.environment_metrics.has_relative_humidity) {
measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.environment_metrics.relative_humidity = humidity.relative_humidity;
}
return true;
return true;
}
#endif

View File

@@ -14,15 +14,14 @@
#include "TelemetrySensor.h"
#include <Adafruit_AHTX0.h>
class AHT10Sensor : public TelemetrySensor
{
private:
Adafruit_AHTX0 aht10;
class AHT10Sensor : public TelemetrySensor {
private:
Adafruit_AHTX0 aht10;
public:
AHT10Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
AHT10Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -13,42 +13,40 @@
BH1750Sensor::BH1750Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BH1750, "BH1750") {}
bool BH1750Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s with mode %d", sensorName, BH1750_SENSOR_MODE);
bool BH1750Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s with mode %d", sensorName, BH1750_SENSOR_MODE);
bh1750 = BH1750_WE(bus, dev->address.address);
status = bh1750.init();
if (!status) {
return status;
}
bh1750.setMode(BH1750_SENSOR_MODE);
initI2CSensor();
bh1750 = BH1750_WE(bus, dev->address.address);
status = bh1750.init();
if (!status) {
return status;
}
bh1750.setMode(BH1750_SENSOR_MODE);
initI2CSensor();
return status;
}
bool BH1750Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
bool BH1750Sensor::getMetrics(meshtastic_Telemetry *measurement) {
/* An OTH and OTH_2 measurement takes ~120 ms. I suggest to wait
140 ms to be on the safe side.
An OTL measurement takes about 16 ms. I suggest to wait 20 ms
to be on the safe side. */
if (BH1750_SENSOR_MODE == BH1750Mode::OTH || BH1750_SENSOR_MODE == BH1750Mode::OTH_2) {
bh1750.setMode(BH1750_SENSOR_MODE);
delay(140); // wait for measurement to be completed
} else if (BH1750_SENSOR_MODE == BH1750Mode::OTL) {
bh1750.setMode(BH1750_SENSOR_MODE);
delay(20);
}
/* An OTH and OTH_2 measurement takes ~120 ms. I suggest to wait
140 ms to be on the safe side.
An OTL measurement takes about 16 ms. I suggest to wait 20 ms
to be on the safe side. */
if (BH1750_SENSOR_MODE == BH1750Mode::OTH || BH1750_SENSOR_MODE == BH1750Mode::OTH_2) {
bh1750.setMode(BH1750_SENSOR_MODE);
delay(140); // wait for measurement to be completed
} else if (BH1750_SENSOR_MODE == BH1750Mode::OTL) {
bh1750.setMode(BH1750_SENSOR_MODE);
delay(20);
}
measurement->variant.environment_metrics.has_lux = true;
float lightIntensity = bh1750.getLux();
measurement->variant.environment_metrics.has_lux = true;
float lightIntensity = bh1750.getLux();
measurement->variant.environment_metrics.lux = lightIntensity;
return true;
measurement->variant.environment_metrics.lux = lightIntensity;
return true;
}
#endif

View File

@@ -7,15 +7,14 @@
#include "TelemetrySensor.h"
#include <BH1750_WE.h>
class BH1750Sensor : public TelemetrySensor
{
private:
BH1750_WE bh1750;
class BH1750Sensor : public TelemetrySensor {
private:
BH1750_WE bh1750;
public:
BH1750Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
BH1750Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -10,36 +10,34 @@
BME280Sensor::BME280Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BME280, "BME280") {}
bool BME280Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
status = bme280.begin(dev->address.address, bus);
if (!status) {
return status;
}
bme280.setSampling(Adafruit_BME280::MODE_FORCED,
Adafruit_BME280::SAMPLING_X1, // Temp. oversampling
Adafruit_BME280::SAMPLING_X1, // Pressure oversampling
Adafruit_BME280::SAMPLING_X1, // Humidity oversampling
Adafruit_BME280::FILTER_OFF, Adafruit_BME280::STANDBY_MS_1000);
initI2CSensor();
bool BME280Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
status = bme280.begin(dev->address.address, bus);
if (!status) {
return status;
}
bme280.setSampling(Adafruit_BME280::MODE_FORCED,
Adafruit_BME280::SAMPLING_X1, // Temp. oversampling
Adafruit_BME280::SAMPLING_X1, // Pressure oversampling
Adafruit_BME280::SAMPLING_X1, // Humidity oversampling
Adafruit_BME280::FILTER_OFF, Adafruit_BME280::STANDBY_MS_1000);
initI2CSensor();
return status;
}
bool BME280Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
bool BME280Sensor::getMetrics(meshtastic_Telemetry *measurement) {
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
LOG_DEBUG("BME280 getMetrics");
bme280.takeForcedMeasurement();
measurement->variant.environment_metrics.temperature = bme280.readTemperature();
measurement->variant.environment_metrics.relative_humidity = bme280.readHumidity();
measurement->variant.environment_metrics.barometric_pressure = bme280.readPressure() / 100.0F;
LOG_DEBUG("BME280 getMetrics");
bme280.takeForcedMeasurement();
measurement->variant.environment_metrics.temperature = bme280.readTemperature();
measurement->variant.environment_metrics.relative_humidity = bme280.readHumidity();
measurement->variant.environment_metrics.barometric_pressure = bme280.readPressure() / 100.0F;
return true;
return true;
}
#endif

View File

@@ -6,15 +6,14 @@
#include "TelemetrySensor.h"
#include <Adafruit_BME280.h>
class BME280Sensor : public TelemetrySensor
{
private:
Adafruit_BME280 bme280;
class BME280Sensor : public TelemetrySensor {
private:
Adafruit_BME280 bme280;
public:
BME280Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
BME280Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -10,139 +10,132 @@
BME680Sensor::BME680Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BME680, "BME680") {}
int32_t BME680Sensor::runOnce()
{
if (!bme680.run()) {
checkStatus("runTrigger");
int32_t BME680Sensor::runOnce() {
if (!bme680.run()) {
checkStatus("runTrigger");
}
return 35;
}
bool BME680Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
status = 0;
if (!bme680.begin(dev->address.address, *bus))
checkStatus("begin");
if (bme680.status == BSEC_OK) {
status = 1;
if (!bme680.setConfig(bsec_config)) {
checkStatus("setConfig");
status = 0;
}
return 35;
}
bool BME680Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
status = 0;
if (!bme680.begin(dev->address.address, *bus))
checkStatus("begin");
if (bme680.status == BSEC_OK) {
status = 1;
if (!bme680.setConfig(bsec_config)) {
checkStatus("setConfig");
status = 0;
}
loadState();
if (!bme680.updateSubscription(sensorList, ARRAY_LEN(sensorList), BSEC_SAMPLE_RATE_LP)) {
checkStatus("updateSubscription");
status = 0;
}
LOG_INFO("Init sensor: %s with the BSEC Library version %d.%d.%d.%d ", sensorName, bme680.version.major,
bme680.version.minor, bme680.version.major_bugfix, bme680.version.minor_bugfix);
loadState();
if (!bme680.updateSubscription(sensorList, ARRAY_LEN(sensorList), BSEC_SAMPLE_RATE_LP)) {
checkStatus("updateSubscription");
status = 0;
}
LOG_INFO("Init sensor: %s with the BSEC Library version %d.%d.%d.%d ", sensorName, bme680.version.major, bme680.version.minor,
bme680.version.major_bugfix, bme680.version.minor_bugfix);
}
if (status == 0)
LOG_DEBUG("BME680Sensor::runOnce: bme680.status %d", bme680.status);
if (status == 0)
LOG_DEBUG("BME680Sensor::runOnce: bme680.status %d", bme680.status);
initI2CSensor();
return status;
initI2CSensor();
return status;
}
bool BME680Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
if (bme680.getData(BSEC_OUTPUT_RAW_PRESSURE).signal == 0)
return false;
bool BME680Sensor::getMetrics(meshtastic_Telemetry *measurement) {
if (bme680.getData(BSEC_OUTPUT_RAW_PRESSURE).signal == 0)
return false;
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
measurement->variant.environment_metrics.has_gas_resistance = true;
measurement->variant.environment_metrics.has_iaq = true;
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
measurement->variant.environment_metrics.has_gas_resistance = true;
measurement->variant.environment_metrics.has_iaq = true;
measurement->variant.environment_metrics.temperature = bme680.getData(BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE).signal;
measurement->variant.environment_metrics.relative_humidity =
bme680.getData(BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY).signal;
measurement->variant.environment_metrics.barometric_pressure = bme680.getData(BSEC_OUTPUT_RAW_PRESSURE).signal;
measurement->variant.environment_metrics.gas_resistance = bme680.getData(BSEC_OUTPUT_RAW_GAS).signal / 1000.0;
// Check if we need to save state to filesystem (every STATE_SAVE_PERIOD ms)
measurement->variant.environment_metrics.iaq = bme680.getData(BSEC_OUTPUT_IAQ).signal;
updateState();
return true;
measurement->variant.environment_metrics.temperature = bme680.getData(BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE).signal;
measurement->variant.environment_metrics.relative_humidity = bme680.getData(BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY).signal;
measurement->variant.environment_metrics.barometric_pressure = bme680.getData(BSEC_OUTPUT_RAW_PRESSURE).signal;
measurement->variant.environment_metrics.gas_resistance = bme680.getData(BSEC_OUTPUT_RAW_GAS).signal / 1000.0;
// Check if we need to save state to filesystem (every STATE_SAVE_PERIOD ms)
measurement->variant.environment_metrics.iaq = bme680.getData(BSEC_OUTPUT_IAQ).signal;
updateState();
return true;
}
void BME680Sensor::loadState()
{
void BME680Sensor::loadState() {
#ifdef FSCom
spiLock->lock();
auto file = FSCom.open(bsecConfigFileName, FILE_O_READ);
spiLock->lock();
auto file = FSCom.open(bsecConfigFileName, FILE_O_READ);
if (file) {
file.read((uint8_t *)&bsecState, BSEC_MAX_STATE_BLOB_SIZE);
file.close();
bme680.setState(bsecState);
LOG_INFO("%s state read from %s", sensorName, bsecConfigFileName);
} else {
LOG_INFO("No %s state found (File: %s)", sensorName, bsecConfigFileName);
}
spiLock->unlock();
#else
LOG_ERROR("ERROR: Filesystem not implemented");
#endif
}
void BME680Sensor::updateState() {
#ifdef FSCom
spiLock->lock();
bool update = false;
if (stateUpdateCounter == 0) {
/* First state update when IAQ accuracy is >= 3 */
accuracy = bme680.getData(BSEC_OUTPUT_IAQ).accuracy;
if (accuracy >= 2) {
LOG_DEBUG("%s state update IAQ accuracy %u >= 2", sensorName, accuracy);
update = true;
stateUpdateCounter++;
} else {
LOG_DEBUG("%s not updated, IAQ accuracy is %u < 2", sensorName, accuracy);
}
} else {
/* Update every STATE_SAVE_PERIOD minutes */
if ((stateUpdateCounter * STATE_SAVE_PERIOD) < millis()) {
LOG_DEBUG("%s state update every %d minutes", sensorName, STATE_SAVE_PERIOD / 60000);
update = true;
stateUpdateCounter++;
}
}
if (update) {
bme680.getState(bsecState);
if (FSCom.exists(bsecConfigFileName) && !FSCom.remove(bsecConfigFileName)) {
LOG_WARN("Can't remove old state file");
}
auto file = FSCom.open(bsecConfigFileName, FILE_O_WRITE);
if (file) {
file.read((uint8_t *)&bsecState, BSEC_MAX_STATE_BLOB_SIZE);
file.close();
bme680.setState(bsecState);
LOG_INFO("%s state read from %s", sensorName, bsecConfigFileName);
LOG_INFO("%s state write to %s", sensorName, bsecConfigFileName);
file.write((uint8_t *)&bsecState, BSEC_MAX_STATE_BLOB_SIZE);
file.flush();
file.close();
} else {
LOG_INFO("No %s state found (File: %s)", sensorName, bsecConfigFileName);
LOG_INFO("Can't write %s state (File: %s)", sensorName, bsecConfigFileName);
}
spiLock->unlock();
}
spiLock->unlock();
#else
LOG_ERROR("ERROR: Filesystem not implemented");
LOG_ERROR("ERROR: Filesystem not implemented");
#endif
}
void BME680Sensor::updateState()
{
#ifdef FSCom
spiLock->lock();
bool update = false;
if (stateUpdateCounter == 0) {
/* First state update when IAQ accuracy is >= 3 */
accuracy = bme680.getData(BSEC_OUTPUT_IAQ).accuracy;
if (accuracy >= 2) {
LOG_DEBUG("%s state update IAQ accuracy %u >= 2", sensorName, accuracy);
update = true;
stateUpdateCounter++;
} else {
LOG_DEBUG("%s not updated, IAQ accuracy is %u < 2", sensorName, accuracy);
}
} else {
/* Update every STATE_SAVE_PERIOD minutes */
if ((stateUpdateCounter * STATE_SAVE_PERIOD) < millis()) {
LOG_DEBUG("%s state update every %d minutes", sensorName, STATE_SAVE_PERIOD / 60000);
update = true;
stateUpdateCounter++;
}
}
void BME680Sensor::checkStatus(const char *functionName) {
if (bme680.status < BSEC_OK)
LOG_ERROR("%s BSEC2 code: %d", functionName, bme680.status);
else if (bme680.status > BSEC_OK)
LOG_WARN("%s BSEC2 code: %d", functionName, bme680.status);
if (update) {
bme680.getState(bsecState);
if (FSCom.exists(bsecConfigFileName) && !FSCom.remove(bsecConfigFileName)) {
LOG_WARN("Can't remove old state file");
}
auto file = FSCom.open(bsecConfigFileName, FILE_O_WRITE);
if (file) {
LOG_INFO("%s state write to %s", sensorName, bsecConfigFileName);
file.write((uint8_t *)&bsecState, BSEC_MAX_STATE_BLOB_SIZE);
file.flush();
file.close();
} else {
LOG_INFO("Can't write %s state (File: %s)", sensorName, bsecConfigFileName);
}
}
spiLock->unlock();
#else
LOG_ERROR("ERROR: Filesystem not implemented");
#endif
}
void BME680Sensor::checkStatus(const char *functionName)
{
if (bme680.status < BSEC_OK)
LOG_ERROR("%s BSEC2 code: %d", functionName, bme680.status);
else if (bme680.status > BSEC_OK)
LOG_WARN("%s BSEC2 code: %d", functionName, bme680.status);
if (bme680.sensor.status < BME68X_OK)
LOG_ERROR("%s BME68X code: %d", functionName, bme680.sensor.status);
else if (bme680.sensor.status > BME68X_OK)
LOG_WARN("%s BME68X code: %d", functionName, bme680.sensor.status);
if (bme680.sensor.status < BME68X_OK)
LOG_ERROR("%s BME68X code: %d", functionName, bme680.sensor.status);
else if (bme680.sensor.status > BME68X_OK)
LOG_WARN("%s BME68X code: %d", functionName, bme680.sensor.status);
}
#endif

View File

@@ -12,34 +12,33 @@ const uint8_t bsec_config[] = {
#include "config/bme680/bme680_iaq_33v_3s_4d/bsec_iaq.txt"
};
class BME680Sensor : public TelemetrySensor
{
private:
Bsec2 bme680;
class BME680Sensor : public TelemetrySensor {
private:
Bsec2 bme680;
protected:
const char *bsecConfigFileName = "/prefs/bsec.dat";
uint8_t bsecState[BSEC_MAX_STATE_BLOB_SIZE] = {0};
uint8_t accuracy = 0;
uint16_t stateUpdateCounter = 0;
bsecSensor sensorList[9] = {BSEC_OUTPUT_IAQ,
BSEC_OUTPUT_RAW_TEMPERATURE,
BSEC_OUTPUT_RAW_PRESSURE,
BSEC_OUTPUT_RAW_HUMIDITY,
BSEC_OUTPUT_RAW_GAS,
BSEC_OUTPUT_STABILIZATION_STATUS,
BSEC_OUTPUT_RUN_IN_STATUS,
BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY};
void loadState();
void updateState();
void checkStatus(const char *functionName);
protected:
const char *bsecConfigFileName = "/prefs/bsec.dat";
uint8_t bsecState[BSEC_MAX_STATE_BLOB_SIZE] = {0};
uint8_t accuracy = 0;
uint16_t stateUpdateCounter = 0;
bsecSensor sensorList[9] = {BSEC_OUTPUT_IAQ,
BSEC_OUTPUT_RAW_TEMPERATURE,
BSEC_OUTPUT_RAW_PRESSURE,
BSEC_OUTPUT_RAW_HUMIDITY,
BSEC_OUTPUT_RAW_GAS,
BSEC_OUTPUT_STABILIZATION_STATUS,
BSEC_OUTPUT_RUN_IN_STATUS,
BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY};
void loadState();
void updateState();
void checkStatus(const char *functionName);
public:
BME680Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
BME680Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -10,27 +10,25 @@
BMP085Sensor::BMP085Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BMP085, "BMP085") {}
bool BMP085Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
bool BMP085Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
bmp085 = Adafruit_BMP085();
status = bmp085.begin(dev->address.address, bus);
bmp085 = Adafruit_BMP085();
status = bmp085.begin(dev->address.address, bus);
initI2CSensor();
return status;
initI2CSensor();
return status;
}
bool BMP085Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
bool BMP085Sensor::getMetrics(meshtastic_Telemetry *measurement) {
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
LOG_DEBUG("BMP085 getMetrics");
measurement->variant.environment_metrics.temperature = bmp085.readTemperature();
measurement->variant.environment_metrics.barometric_pressure = bmp085.readPressure() / 100.0F;
LOG_DEBUG("BMP085 getMetrics");
measurement->variant.environment_metrics.temperature = bmp085.readTemperature();
measurement->variant.environment_metrics.barometric_pressure = bmp085.readPressure() / 100.0F;
return true;
return true;
}
#endif

View File

@@ -6,15 +6,14 @@
#include "TelemetrySensor.h"
#include <Adafruit_BMP085.h>
class BMP085Sensor : public TelemetrySensor
{
private:
Adafruit_BMP085 bmp085;
class BMP085Sensor : public TelemetrySensor {
private:
Adafruit_BMP085 bmp085;
public:
BMP085Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
BMP085Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -10,36 +10,34 @@
BMP280Sensor::BMP280Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BMP280, "BMP280") {}
bool BMP280Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
bool BMP280Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
bmp280 = Adafruit_BMP280(bus);
status = bmp280.begin(dev->address.address);
if (!status) {
return status;
}
bmp280.setSampling(Adafruit_BMP280::MODE_FORCED,
Adafruit_BMP280::SAMPLING_X1, // Temp. oversampling
Adafruit_BMP280::SAMPLING_X1, // Pressure oversampling
Adafruit_BMP280::FILTER_OFF, Adafruit_BMP280::STANDBY_MS_1000);
initI2CSensor();
bmp280 = Adafruit_BMP280(bus);
status = bmp280.begin(dev->address.address);
if (!status) {
return status;
}
bmp280.setSampling(Adafruit_BMP280::MODE_FORCED,
Adafruit_BMP280::SAMPLING_X1, // Temp. oversampling
Adafruit_BMP280::SAMPLING_X1, // Pressure oversampling
Adafruit_BMP280::FILTER_OFF, Adafruit_BMP280::STANDBY_MS_1000);
initI2CSensor();
return status;
}
bool BMP280Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
bool BMP280Sensor::getMetrics(meshtastic_Telemetry *measurement) {
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
LOG_DEBUG("BMP280 getMetrics");
bmp280.takeForcedMeasurement();
measurement->variant.environment_metrics.temperature = bmp280.readTemperature();
measurement->variant.environment_metrics.barometric_pressure = bmp280.readPressure() / 100.0F;
LOG_DEBUG("BMP280 getMetrics");
bmp280.takeForcedMeasurement();
measurement->variant.environment_metrics.temperature = bmp280.readTemperature();
measurement->variant.environment_metrics.barometric_pressure = bmp280.readPressure() / 100.0F;
return true;
return true;
}
#endif

View File

@@ -6,15 +6,14 @@
#include "TelemetrySensor.h"
#include <Adafruit_BMP280.h>
class BMP280Sensor : public TelemetrySensor
{
private:
Adafruit_BMP280 bmp280;
class BMP280Sensor : public TelemetrySensor {
private:
Adafruit_BMP280 bmp280;
public:
BMP280Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
BMP280Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -6,65 +6,61 @@
BMP3XXSensor::BMP3XXSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BMP3XX, "BMP3XX") {}
bool BMP3XXSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
bool BMP3XXSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
// Get a singleton instance and initialise the bmp3xx
if (bmp3xx == nullptr) {
bmp3xx = BMP3XXSingleton::GetInstance();
}
status = bmp3xx->begin_I2C(dev->address.address, bus);
if (!status) {
return status;
}
// set up oversampling and filter initialization
bmp3xx->setTemperatureOversampling(BMP3_OVERSAMPLING_4X);
bmp3xx->setPressureOversampling(BMP3_OVERSAMPLING_8X);
bmp3xx->setIIRFilterCoeff(BMP3_IIR_FILTER_COEFF_3);
bmp3xx->setOutputDataRate(BMP3_ODR_25_HZ);
// take a couple of initial readings to settle the sensor filters
for (int i = 0; i < 3; i++) {
bmp3xx->performReading();
}
initI2CSensor();
// Get a singleton instance and initialise the bmp3xx
if (bmp3xx == nullptr) {
bmp3xx = BMP3XXSingleton::GetInstance();
}
status = bmp3xx->begin_I2C(dev->address.address, bus);
if (!status) {
return status;
}
// set up oversampling and filter initialization
bmp3xx->setTemperatureOversampling(BMP3_OVERSAMPLING_4X);
bmp3xx->setPressureOversampling(BMP3_OVERSAMPLING_8X);
bmp3xx->setIIRFilterCoeff(BMP3_IIR_FILTER_COEFF_3);
bmp3xx->setOutputDataRate(BMP3_ODR_25_HZ);
// take a couple of initial readings to settle the sensor filters
for (int i = 0; i < 3; i++) {
bmp3xx->performReading();
}
initI2CSensor();
return status;
}
bool BMP3XXSensor::getMetrics(meshtastic_Telemetry *measurement)
{
if (bmp3xx == nullptr) {
bmp3xx = BMP3XXSingleton::GetInstance();
}
if ((int)measurement->which_variant == meshtastic_Telemetry_environment_metrics_tag) {
bmp3xx->performReading();
bool BMP3XXSensor::getMetrics(meshtastic_Telemetry *measurement) {
if (bmp3xx == nullptr) {
bmp3xx = BMP3XXSingleton::GetInstance();
}
if ((int)measurement->which_variant == meshtastic_Telemetry_environment_metrics_tag) {
bmp3xx->performReading();
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
measurement->variant.environment_metrics.has_relative_humidity = false;
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
measurement->variant.environment_metrics.has_relative_humidity = false;
measurement->variant.environment_metrics.temperature = static_cast<float>(bmp3xx->temperature);
measurement->variant.environment_metrics.barometric_pressure = static_cast<float>(bmp3xx->pressure) / 100.0F;
measurement->variant.environment_metrics.relative_humidity = 0.0f;
measurement->variant.environment_metrics.temperature = static_cast<float>(bmp3xx->temperature);
measurement->variant.environment_metrics.barometric_pressure = static_cast<float>(bmp3xx->pressure) / 100.0F;
measurement->variant.environment_metrics.relative_humidity = 0.0f;
LOG_DEBUG("BMP3XX getMetrics id: %i temp: %.1f press %.1f", measurement->which_variant,
measurement->variant.environment_metrics.temperature,
measurement->variant.environment_metrics.barometric_pressure);
} else {
LOG_DEBUG("BMP3XX getMetrics id: %i", measurement->which_variant);
}
return true;
LOG_DEBUG("BMP3XX getMetrics id: %i temp: %.1f press %.1f", measurement->which_variant, measurement->variant.environment_metrics.temperature,
measurement->variant.environment_metrics.barometric_pressure);
} else {
LOG_DEBUG("BMP3XX getMetrics id: %i", measurement->which_variant);
}
return true;
}
// Get a singleton wrapper for an Adafruit_bmp3xx
BMP3XXSingleton *BMP3XXSingleton::GetInstance()
{
if (pinstance == nullptr) {
pinstance = new BMP3XXSingleton();
}
return pinstance;
BMP3XXSingleton *BMP3XXSingleton::GetInstance() {
if (pinstance == nullptr) {
pinstance = new BMP3XXSingleton();
}
return pinstance;
}
BMP3XXSingleton::BMP3XXSingleton() {}
@@ -73,16 +69,15 @@ BMP3XXSingleton::~BMP3XXSingleton() {}
BMP3XXSingleton *BMP3XXSingleton::pinstance{nullptr};
bool BMP3XXSingleton::performReading()
{
bool result = Adafruit_BMP3XX::performReading();
if (result) {
double atmospheric = this->pressure / 100.0;
altitudeAmslMetres = 44330.0 * (1.0 - pow(atmospheric / SEAL_LEVEL_HPA, 0.1903));
} else {
altitudeAmslMetres = 0.0;
}
return result;
bool BMP3XXSingleton::performReading() {
bool result = Adafruit_BMP3XX::performReading();
if (result) {
double atmospheric = this->pressure / 100.0;
altitudeAmslMetres = 44330.0 * (1.0 - pow(atmospheric / SEAL_LEVEL_HPA, 0.1903));
} else {
altitudeAmslMetres = 0.0;
}
return result;
}
#endif

View File

@@ -12,42 +12,40 @@
#include <typeinfo>
// Singleton wrapper for the Adafruit_BMP3XX class
class BMP3XXSingleton : public Adafruit_BMP3XX
{
private:
static BMP3XXSingleton *pinstance;
class BMP3XXSingleton : public Adafruit_BMP3XX {
private:
static BMP3XXSingleton *pinstance;
protected:
BMP3XXSingleton();
~BMP3XXSingleton();
protected:
BMP3XXSingleton();
~BMP3XXSingleton();
public:
// Create a singleton instance (not thread safe)
static BMP3XXSingleton *GetInstance();
public:
// Create a singleton instance (not thread safe)
static BMP3XXSingleton *GetInstance();
// Singletons should not be cloneable.
BMP3XXSingleton(BMP3XXSingleton &other) = delete;
// Singletons should not be cloneable.
BMP3XXSingleton(BMP3XXSingleton &other) = delete;
// Singletons should not be assignable.
void operator=(const BMP3XXSingleton &) = delete;
// Singletons should not be assignable.
void operator=(const BMP3XXSingleton &) = delete;
// Performs a full reading of all sensors in the BMP3XX. Assigns
// the internal temperature, pressure and altitudeAmsl variables
bool performReading();
// Performs a full reading of all sensors in the BMP3XX. Assigns
// the internal temperature, pressure and altitudeAmsl variables
bool performReading();
// Altitude in metres above mean sea level, assigned after calling performReading()
double altitudeAmslMetres = 0.0f;
// Altitude in metres above mean sea level, assigned after calling performReading()
double altitudeAmslMetres = 0.0f;
};
class BMP3XXSensor : public TelemetrySensor
{
protected:
BMP3XXSingleton *bmp3xx = nullptr;
class BMP3XXSensor : public TelemetrySensor {
protected:
BMP3XXSingleton *bmp3xx = nullptr;
public:
BMP3XXSensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
BMP3XXSensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -14,55 +14,51 @@
CGRadSensSensor::CGRadSensSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_RADSENS, "RadSens") {}
bool CGRadSensSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
// Initialize the sensor following the same pattern as RCWL9620Sensor
LOG_INFO("Init sensor: %s", sensorName);
status = true;
begin(bus, dev->address.address);
initI2CSensor();
return status;
bool CGRadSensSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
// Initialize the sensor following the same pattern as RCWL9620Sensor
LOG_INFO("Init sensor: %s", sensorName);
status = true;
begin(bus, dev->address.address);
initI2CSensor();
return status;
}
void CGRadSensSensor::begin(TwoWire *wire, uint8_t addr)
{
// Store the Wire and address to the sensor following the same pattern as RCWL9620Sensor
_wire = wire;
_addr = addr;
_wire->begin();
void CGRadSensSensor::begin(TwoWire *wire, uint8_t addr) {
// Store the Wire and address to the sensor following the same pattern as RCWL9620Sensor
_wire = wire;
_addr = addr;
_wire->begin();
}
float CGRadSensSensor::getStaticRadiation()
{
// Read a register, following the same pattern as the RCWL9620Sensor
_wire->beginTransmission(_addr); // Transfer data to addr.
_wire->write(0x06); // Radiation intensity (static period T = 500 sec)
if (_wire->endTransmission() == 0) {
if (_wire->requestFrom(_addr, (uint8_t)3)) {
; // Request 3 bytes
uint32_t data = _wire->read();
data <<= 8;
data |= _wire->read();
data <<= 8;
data |= _wire->read();
float CGRadSensSensor::getStaticRadiation() {
// Read a register, following the same pattern as the RCWL9620Sensor
_wire->beginTransmission(_addr); // Transfer data to addr.
_wire->write(0x06); // Radiation intensity (static period T = 500 sec)
if (_wire->endTransmission() == 0) {
if (_wire->requestFrom(_addr, (uint8_t)3)) {
; // Request 3 bytes
uint32_t data = _wire->read();
data <<= 8;
data |= _wire->read();
data <<= 8;
data |= _wire->read();
// As per the data sheet for the RadSens
// Register 0x06 contains the reading in 0.1 * μR / h
float microRadPerHr = float(data) / 10.0;
return microRadPerHr;
}
// As per the data sheet for the RadSens
// Register 0x06 contains the reading in 0.1 * μR / h
float microRadPerHr = float(data) / 10.0;
return microRadPerHr;
}
return -1.0;
}
return -1.0;
}
bool CGRadSensSensor::getMetrics(meshtastic_Telemetry *measurement)
{
// Store the meansurement in the the appropriate fields of the protobuf
measurement->variant.environment_metrics.has_radiation = true;
bool CGRadSensSensor::getMetrics(meshtastic_Telemetry *measurement) {
// Store the meansurement in the the appropriate fields of the protobuf
measurement->variant.environment_metrics.has_radiation = true;
LOG_DEBUG("CGRADSENS getMetrics");
measurement->variant.environment_metrics.radiation = getStaticRadiation();
LOG_DEBUG("CGRADSENS getMetrics");
measurement->variant.environment_metrics.radiation = getStaticRadiation();
return true;
return true;
}
#endif

View File

@@ -10,20 +10,19 @@
#include "TelemetrySensor.h"
#include <Wire.h>
class CGRadSensSensor : public TelemetrySensor
{
private:
uint8_t _addr = 0x66;
TwoWire *_wire = &Wire;
class CGRadSensSensor : public TelemetrySensor {
private:
uint8_t _addr = 0x66;
TwoWire *_wire = &Wire;
protected:
void begin(TwoWire *wire = &Wire, uint8_t addr = 0x66);
float getStaticRadiation();
protected:
void begin(TwoWire *wire = &Wire, uint8_t addr = 0x66);
float getStaticRadiation();
public:
CGRadSensSensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
CGRadSensSensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -4,10 +4,9 @@
#pragma once
class CurrentSensor
{
public:
virtual int16_t getCurrentMa() = 0;
class CurrentSensor {
public:
virtual int16_t getCurrentMa() = 0;
};
#endif

View File

@@ -10,46 +10,43 @@
DFRobotGravitySensor::DFRobotGravitySensor() : TelemetrySensor(meshtastic_TelemetrySensorType_DFROBOT_RAIN, "DFROBOT_RAIN") {}
DFRobotGravitySensor::~DFRobotGravitySensor()
{
if (gravity) {
DFRobotGravitySensor::~DFRobotGravitySensor() {
if (gravity) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
delete gravity;
delete gravity;
#pragma GCC diagnostic pop
gravity = nullptr;
}
gravity = nullptr;
}
}
bool DFRobotGravitySensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
bool DFRobotGravitySensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
gravity = new DFRobot_RainfallSensor_I2C(bus);
status = gravity->begin();
gravity = new DFRobot_RainfallSensor_I2C(bus);
status = gravity->begin();
LOG_DEBUG("%s VID: %x, PID: %x, Version: %s", sensorName, gravity->vid, gravity->pid, gravity->getFirmwareVersion().c_str());
LOG_DEBUG("%s VID: %x, PID: %x, Version: %s", sensorName, gravity->vid, gravity->pid, gravity->getFirmwareVersion().c_str());
initI2CSensor();
return status;
initI2CSensor();
return status;
}
bool DFRobotGravitySensor::getMetrics(meshtastic_Telemetry *measurement)
{
if (!gravity) {
LOG_ERROR("DFRobotGravitySensor not initialized");
return false;
}
bool DFRobotGravitySensor::getMetrics(meshtastic_Telemetry *measurement) {
if (!gravity) {
LOG_ERROR("DFRobotGravitySensor not initialized");
return false;
}
measurement->variant.environment_metrics.has_rainfall_1h = true;
measurement->variant.environment_metrics.has_rainfall_24h = true;
measurement->variant.environment_metrics.has_rainfall_1h = true;
measurement->variant.environment_metrics.has_rainfall_24h = true;
measurement->variant.environment_metrics.rainfall_1h = gravity->getRainfall(1);
measurement->variant.environment_metrics.rainfall_24h = gravity->getRainfall(24);
measurement->variant.environment_metrics.rainfall_1h = gravity->getRainfall(1);
measurement->variant.environment_metrics.rainfall_24h = gravity->getRainfall(24);
LOG_INFO("Rain 1h: %f mm", measurement->variant.environment_metrics.rainfall_1h);
LOG_INFO("Rain 24h: %f mm", measurement->variant.environment_metrics.rainfall_24h);
return true;
LOG_INFO("Rain 1h: %f mm", measurement->variant.environment_metrics.rainfall_1h);
LOG_INFO("Rain 24h: %f mm", measurement->variant.environment_metrics.rainfall_24h);
return true;
}
#endif

View File

@@ -11,16 +11,15 @@
#include <DFRobot_RainfallSensor.h>
#include <string>
class DFRobotGravitySensor : public TelemetrySensor
{
private:
DFRobot_RainfallSensor_I2C *gravity = nullptr;
class DFRobotGravitySensor : public TelemetrySensor {
private:
DFRobot_RainfallSensor_I2C *gravity = nullptr;
public:
DFRobotGravitySensor();
~DFRobotGravitySensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
DFRobotGravitySensor();
~DFRobotGravitySensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -11,44 +11,42 @@
DFRobotLarkSensor::DFRobotLarkSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_DFROBOT_LARK, "DFROBOT_LARK") {}
bool DFRobotLarkSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
lark = DFRobot_LarkWeatherStation_I2C(dev->address.address, bus);
bool DFRobotLarkSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
lark = DFRobot_LarkWeatherStation_I2C(dev->address.address, bus);
if (lark.begin() == 0) // DFRobotLarkSensor init
{
LOG_DEBUG("DFRobotLarkSensor Init Succeed");
status = true;
} else {
LOG_ERROR("DFRobotLarkSensor Init Failed");
status = false;
}
initI2CSensor();
return status;
if (lark.begin() == 0) // DFRobotLarkSensor init
{
LOG_DEBUG("DFRobotLarkSensor Init Succeed");
status = true;
} else {
LOG_ERROR("DFRobotLarkSensor Init Failed");
status = false;
}
initI2CSensor();
return status;
}
bool DFRobotLarkSensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.environment_metrics.has_wind_speed = true;
measurement->variant.environment_metrics.has_wind_direction = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
bool DFRobotLarkSensor::getMetrics(meshtastic_Telemetry *measurement) {
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.environment_metrics.has_wind_speed = true;
measurement->variant.environment_metrics.has_wind_direction = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
measurement->variant.environment_metrics.temperature = lark.getValue("Temp").toFloat();
measurement->variant.environment_metrics.relative_humidity = lark.getValue("Humi").toFloat();
measurement->variant.environment_metrics.wind_speed = lark.getValue("Speed").toFloat();
measurement->variant.environment_metrics.wind_direction = GeoCoord::bearingToDegrees(lark.getValue("Dir").c_str());
measurement->variant.environment_metrics.barometric_pressure = lark.getValue("Pressure").toFloat();
measurement->variant.environment_metrics.temperature = lark.getValue("Temp").toFloat();
measurement->variant.environment_metrics.relative_humidity = lark.getValue("Humi").toFloat();
measurement->variant.environment_metrics.wind_speed = lark.getValue("Speed").toFloat();
measurement->variant.environment_metrics.wind_direction = GeoCoord::bearingToDegrees(lark.getValue("Dir").c_str());
measurement->variant.environment_metrics.barometric_pressure = lark.getValue("Pressure").toFloat();
LOG_INFO("Temperature: %f", measurement->variant.environment_metrics.temperature);
LOG_INFO("Humidity: %f", measurement->variant.environment_metrics.relative_humidity);
LOG_INFO("Wind Speed: %f", measurement->variant.environment_metrics.wind_speed);
LOG_INFO("Wind Direction: %d", measurement->variant.environment_metrics.wind_direction);
LOG_INFO("Barometric Pressure: %f", measurement->variant.environment_metrics.barometric_pressure);
LOG_INFO("Temperature: %f", measurement->variant.environment_metrics.temperature);
LOG_INFO("Humidity: %f", measurement->variant.environment_metrics.relative_humidity);
LOG_INFO("Wind Speed: %f", measurement->variant.environment_metrics.wind_speed);
LOG_INFO("Wind Direction: %d", measurement->variant.environment_metrics.wind_direction);
LOG_INFO("Barometric Pressure: %f", measurement->variant.environment_metrics.barometric_pressure);
return true;
return true;
}
#endif

View File

@@ -11,15 +11,14 @@
#include <DFRobot_LarkWeatherStation.h>
#include <string>
class DFRobotLarkSensor : public TelemetrySensor
{
private:
DFRobot_LarkWeatherStation_I2C lark = DFRobot_LarkWeatherStation_I2C();
class DFRobotLarkSensor : public TelemetrySensor {
private:
DFRobot_LarkWeatherStation_I2C lark = DFRobot_LarkWeatherStation_I2C();
public:
DFRobotLarkSensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
DFRobotLarkSensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -9,36 +9,34 @@
DPS310Sensor::DPS310Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_DPS310, "DPS310") {}
bool DPS310Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
status = dps310.begin_I2C(dev->address.address, bus);
if (!status) {
return status;
}
dps310.configurePressure(DPS310_1HZ, DPS310_4SAMPLES);
dps310.configureTemperature(DPS310_1HZ, DPS310_4SAMPLES);
dps310.setMode(DPS310_CONT_PRESTEMP);
initI2CSensor();
bool DPS310Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
status = dps310.begin_I2C(dev->address.address, bus);
if (!status) {
return status;
}
dps310.configurePressure(DPS310_1HZ, DPS310_4SAMPLES);
dps310.configureTemperature(DPS310_1HZ, DPS310_4SAMPLES);
dps310.setMode(DPS310_CONT_PRESTEMP);
initI2CSensor();
return status;
}
bool DPS310Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
sensors_event_t temp, press;
bool DPS310Sensor::getMetrics(meshtastic_Telemetry *measurement) {
sensors_event_t temp, press;
if (!dps310.getEvents(&temp, &press)) {
LOG_DEBUG("DPS310 getEvents no data");
return false;
}
if (!dps310.getEvents(&temp, &press)) {
LOG_DEBUG("DPS310 getEvents no data");
return false;
}
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
measurement->variant.environment_metrics.temperature = temp.temperature;
measurement->variant.environment_metrics.barometric_pressure = press.pressure;
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
measurement->variant.environment_metrics.temperature = temp.temperature;
measurement->variant.environment_metrics.barometric_pressure = press.pressure;
return true;
return true;
}
#endif

View File

@@ -6,15 +6,14 @@
#include "TelemetrySensor.h"
#include <Adafruit_DPS310.h>
class DPS310Sensor : public TelemetrySensor
{
private:
Adafruit_DPS310 dps310;
class DPS310Sensor : public TelemetrySensor {
private:
Adafruit_DPS310 dps310;
public:
DPS310Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
DPS310Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -13,41 +13,33 @@
INA219Sensor::INA219Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_INA219, "INA219") {}
int32_t INA219Sensor::runOnce()
{
LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
if (!ina219.success()) {
ina219 = Adafruit_INA219(nodeTelemetrySensorsMap[sensorType].first);
status = ina219.begin(nodeTelemetrySensorsMap[sensorType].second);
} else {
status = ina219.success();
}
return initI2CSensor();
int32_t INA219Sensor::runOnce() {
LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
if (!ina219.success()) {
ina219 = Adafruit_INA219(nodeTelemetrySensorsMap[sensorType].first);
status = ina219.begin(nodeTelemetrySensorsMap[sensorType].second);
} else {
status = ina219.success();
}
return initI2CSensor();
}
void INA219Sensor::setup() {}
bool INA219Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_voltage = true;
measurement->variant.environment_metrics.has_current = true;
bool INA219Sensor::getMetrics(meshtastic_Telemetry *measurement) {
measurement->variant.environment_metrics.has_voltage = true;
measurement->variant.environment_metrics.has_current = true;
measurement->variant.environment_metrics.voltage = ina219.getBusVoltage_V();
measurement->variant.environment_metrics.current = ina219.getCurrent_mA() * INA219_MULTIPLIER;
return true;
measurement->variant.environment_metrics.voltage = ina219.getBusVoltage_V();
measurement->variant.environment_metrics.current = ina219.getCurrent_mA() * INA219_MULTIPLIER;
return true;
}
uint16_t INA219Sensor::getBusVoltageMv()
{
return lround(ina219.getBusVoltage_V() * 1000);
}
uint16_t INA219Sensor::getBusVoltageMv() { return lround(ina219.getBusVoltage_V() * 1000); }
int16_t INA219Sensor::getCurrentMa()
{
return lround(ina219.getCurrent_mA());
}
int16_t INA219Sensor::getCurrentMa() { return lround(ina219.getCurrent_mA()); }
#endif

View File

@@ -8,20 +8,19 @@
#include "VoltageSensor.h"
#include <Adafruit_INA219.h>
class INA219Sensor : public TelemetrySensor, VoltageSensor, CurrentSensor
{
private:
Adafruit_INA219 ina219;
class INA219Sensor : public TelemetrySensor, VoltageSensor, CurrentSensor {
private:
Adafruit_INA219 ina219;
protected:
virtual void setup() override;
protected:
virtual void setup() override;
public:
INA219Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual uint16_t getBusVoltageMv() override;
virtual int16_t getCurrentMa() override;
public:
INA219Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual uint16_t getBusVoltageMv() override;
virtual int16_t getCurrentMa() override;
};
#endif

View File

@@ -9,76 +9,65 @@
INA226Sensor::INA226Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_INA226, "INA226") {}
int32_t INA226Sensor::runOnce()
{
LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
int32_t INA226Sensor::runOnce() {
LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
begin(nodeTelemetrySensorsMap[sensorType].second, nodeTelemetrySensorsMap[sensorType].first);
begin(nodeTelemetrySensorsMap[sensorType].second, nodeTelemetrySensorsMap[sensorType].first);
if (!status) {
status = ina226.begin();
}
return initI2CSensor();
if (!status) {
status = ina226.begin();
}
return initI2CSensor();
}
void INA226Sensor::setup() {}
void INA226Sensor::begin(TwoWire *wire, uint8_t addr)
{
_wire = wire;
_addr = addr;
ina226 = INA226(_addr, _wire);
_wire->begin();
ina226.setMaxCurrentShunt(0.8, 0.100);
void INA226Sensor::begin(TwoWire *wire, uint8_t addr) {
_wire = wire;
_addr = addr;
ina226 = INA226(_addr, _wire);
_wire->begin();
ina226.setMaxCurrentShunt(0.8, 0.100);
}
bool INA226Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
switch (measurement->which_variant) {
case meshtastic_Telemetry_environment_metrics_tag:
return getEnvironmentMetrics(measurement);
bool INA226Sensor::getMetrics(meshtastic_Telemetry *measurement) {
switch (measurement->which_variant) {
case meshtastic_Telemetry_environment_metrics_tag:
return getEnvironmentMetrics(measurement);
case meshtastic_Telemetry_power_metrics_tag:
return getPowerMetrics(measurement);
}
case meshtastic_Telemetry_power_metrics_tag:
return getPowerMetrics(measurement);
}
// unsupported metric
return false;
// unsupported metric
return false;
}
bool INA226Sensor::getEnvironmentMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_voltage = true;
measurement->variant.environment_metrics.has_current = true;
bool INA226Sensor::getEnvironmentMetrics(meshtastic_Telemetry *measurement) {
measurement->variant.environment_metrics.has_voltage = true;
measurement->variant.environment_metrics.has_current = true;
measurement->variant.environment_metrics.voltage = ina226.getBusVoltage();
measurement->variant.environment_metrics.current = ina226.getCurrent_mA();
measurement->variant.environment_metrics.voltage = ina226.getBusVoltage();
measurement->variant.environment_metrics.current = ina226.getCurrent_mA();
return true;
return true;
}
bool INA226Sensor::getPowerMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.power_metrics.has_ch1_voltage = true;
measurement->variant.power_metrics.has_ch1_current = true;
bool INA226Sensor::getPowerMetrics(meshtastic_Telemetry *measurement) {
measurement->variant.power_metrics.has_ch1_voltage = true;
measurement->variant.power_metrics.has_ch1_current = true;
measurement->variant.power_metrics.ch1_voltage = ina226.getBusVoltage();
measurement->variant.power_metrics.ch1_current = ina226.getCurrent_mA();
measurement->variant.power_metrics.ch1_voltage = ina226.getBusVoltage();
measurement->variant.power_metrics.ch1_current = ina226.getCurrent_mA();
return true;
return true;
}
uint16_t INA226Sensor::getBusVoltageMv()
{
return lround(ina226.getBusVoltage() * 1000);
}
uint16_t INA226Sensor::getBusVoltageMv() { return lround(ina226.getBusVoltage() * 1000); }
int16_t INA226Sensor::getCurrentMa()
{
return lround(ina226.getCurrent_mA());
}
int16_t INA226Sensor::getCurrentMa() { return lround(ina226.getCurrent_mA()); }
#endif

View File

@@ -8,26 +8,25 @@
#include "VoltageSensor.h"
#include <INA226.h>
class INA226Sensor : public TelemetrySensor, VoltageSensor, CurrentSensor
{
private:
uint8_t _addr = INA_ADDR;
TwoWire *_wire = &Wire;
INA226 ina226 = INA226(_addr, _wire);
class INA226Sensor : public TelemetrySensor, VoltageSensor, CurrentSensor {
private:
uint8_t _addr = INA_ADDR;
TwoWire *_wire = &Wire;
INA226 ina226 = INA226(_addr, _wire);
bool getEnvironmentMetrics(meshtastic_Telemetry *measurement);
bool getPowerMetrics(meshtastic_Telemetry *measurement);
bool getEnvironmentMetrics(meshtastic_Telemetry *measurement);
bool getPowerMetrics(meshtastic_Telemetry *measurement);
protected:
virtual void setup() override;
void begin(TwoWire *wire = &Wire, uint8_t addr = INA_ADDR);
protected:
virtual void setup() override;
void begin(TwoWire *wire = &Wire, uint8_t addr = INA_ADDR);
public:
INA226Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual uint16_t getBusVoltageMv() override;
virtual int16_t getCurrentMa() override;
public:
INA226Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual uint16_t getBusVoltageMv() override;
virtual int16_t getCurrentMa() override;
};
#endif

View File

@@ -9,35 +9,30 @@
INA260Sensor::INA260Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_INA260, "INA260") {}
int32_t INA260Sensor::runOnce()
{
LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
int32_t INA260Sensor::runOnce() {
LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
if (!status) {
status = ina260.begin(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second);
}
return initI2CSensor();
if (!status) {
status = ina260.begin(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second);
}
return initI2CSensor();
}
void INA260Sensor::setup() {}
bool INA260Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_voltage = true;
measurement->variant.environment_metrics.has_current = true;
bool INA260Sensor::getMetrics(meshtastic_Telemetry *measurement) {
measurement->variant.environment_metrics.has_voltage = true;
measurement->variant.environment_metrics.has_current = true;
// mV conversion to V
measurement->variant.environment_metrics.voltage = ina260.readBusVoltage() / 1000;
measurement->variant.environment_metrics.current = ina260.readCurrent();
return true;
// mV conversion to V
measurement->variant.environment_metrics.voltage = ina260.readBusVoltage() / 1000;
measurement->variant.environment_metrics.current = ina260.readCurrent();
return true;
}
uint16_t INA260Sensor::getBusVoltageMv()
{
return lround(ina260.readBusVoltage());
}
uint16_t INA260Sensor::getBusVoltageMv() { return lround(ina260.readBusVoltage()); }
#endif

View File

@@ -7,19 +7,18 @@
#include "VoltageSensor.h"
#include <Adafruit_INA260.h>
class INA260Sensor : public TelemetrySensor, VoltageSensor
{
private:
Adafruit_INA260 ina260 = Adafruit_INA260();
class INA260Sensor : public TelemetrySensor, VoltageSensor {
private:
Adafruit_INA260 ina260 = Adafruit_INA260();
protected:
virtual void setup() override;
protected:
virtual void setup() override;
public:
INA260Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual uint16_t getBusVoltageMv() override;
public:
INA260Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual uint16_t getBusVoltageMv() override;
};
#endif

View File

@@ -9,102 +9,90 @@
INA3221Sensor::INA3221Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_INA3221, "INA3221"){};
int32_t INA3221Sensor::runOnce()
{
LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
if (!status) {
ina3221.begin(nodeTelemetrySensorsMap[sensorType].second);
ina3221.setShuntRes(100, 100, 100); // 0.1 Ohm shunt resistors
status = true;
} else {
status = true;
}
return initI2CSensor();
int32_t INA3221Sensor::runOnce() {
LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
if (!status) {
ina3221.begin(nodeTelemetrySensorsMap[sensorType].second);
ina3221.setShuntRes(100, 100, 100); // 0.1 Ohm shunt resistors
status = true;
} else {
status = true;
}
return initI2CSensor();
};
void INA3221Sensor::setup() {}
struct _INA3221Measurement INA3221Sensor::getMeasurement(ina3221_ch_t ch)
{
struct _INA3221Measurement measurement;
struct _INA3221Measurement INA3221Sensor::getMeasurement(ina3221_ch_t ch) {
struct _INA3221Measurement measurement;
measurement.voltage = ina3221.getVoltage(ch);
measurement.current = ina3221.getCurrent(ch);
measurement.voltage = ina3221.getVoltage(ch);
measurement.current = ina3221.getCurrent(ch);
return measurement;
return measurement;
}
struct _INA3221Measurements INA3221Sensor::getMeasurements()
{
struct _INA3221Measurements measurements;
struct _INA3221Measurements INA3221Sensor::getMeasurements() {
struct _INA3221Measurements measurements;
// INA3221 has 3 channels starting from 0
for (int i = 0; i < 3; i++) {
measurements.measurements[i] = getMeasurement((ina3221_ch_t)i);
}
// INA3221 has 3 channels starting from 0
for (int i = 0; i < 3; i++) {
measurements.measurements[i] = getMeasurement((ina3221_ch_t)i);
}
return measurements;
return measurements;
}
bool INA3221Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
switch (measurement->which_variant) {
case meshtastic_Telemetry_environment_metrics_tag:
return getEnvironmentMetrics(measurement);
bool INA3221Sensor::getMetrics(meshtastic_Telemetry *measurement) {
switch (measurement->which_variant) {
case meshtastic_Telemetry_environment_metrics_tag:
return getEnvironmentMetrics(measurement);
case meshtastic_Telemetry_power_metrics_tag:
return getPowerMetrics(measurement);
}
case meshtastic_Telemetry_power_metrics_tag:
return getPowerMetrics(measurement);
}
// unsupported metric
return false;
// unsupported metric
return false;
}
bool INA3221Sensor::getEnvironmentMetrics(meshtastic_Telemetry *measurement)
{
struct _INA3221Measurement m = getMeasurement(ENV_CH);
bool INA3221Sensor::getEnvironmentMetrics(meshtastic_Telemetry *measurement) {
struct _INA3221Measurement m = getMeasurement(ENV_CH);
measurement->variant.environment_metrics.has_voltage = true;
measurement->variant.environment_metrics.has_current = true;
measurement->variant.environment_metrics.has_voltage = true;
measurement->variant.environment_metrics.has_current = true;
measurement->variant.environment_metrics.voltage = m.voltage;
measurement->variant.environment_metrics.current = m.current;
measurement->variant.environment_metrics.voltage = m.voltage;
measurement->variant.environment_metrics.current = m.current;
return true;
return true;
}
bool INA3221Sensor::getPowerMetrics(meshtastic_Telemetry *measurement)
{
struct _INA3221Measurements m = getMeasurements();
bool INA3221Sensor::getPowerMetrics(meshtastic_Telemetry *measurement) {
struct _INA3221Measurements m = getMeasurements();
measurement->variant.power_metrics.has_ch1_voltage = true;
measurement->variant.power_metrics.has_ch1_current = true;
measurement->variant.power_metrics.has_ch2_voltage = true;
measurement->variant.power_metrics.has_ch2_current = true;
measurement->variant.power_metrics.has_ch3_voltage = true;
measurement->variant.power_metrics.has_ch3_current = true;
measurement->variant.power_metrics.has_ch1_voltage = true;
measurement->variant.power_metrics.has_ch1_current = true;
measurement->variant.power_metrics.has_ch2_voltage = true;
measurement->variant.power_metrics.has_ch2_current = true;
measurement->variant.power_metrics.has_ch3_voltage = true;
measurement->variant.power_metrics.has_ch3_current = true;
measurement->variant.power_metrics.ch1_voltage = m.measurements[INA3221_CH1].voltage;
measurement->variant.power_metrics.ch1_current = m.measurements[INA3221_CH1].current;
measurement->variant.power_metrics.ch2_voltage = m.measurements[INA3221_CH2].voltage;
measurement->variant.power_metrics.ch2_current = m.measurements[INA3221_CH2].current;
measurement->variant.power_metrics.ch3_voltage = m.measurements[INA3221_CH3].voltage;
measurement->variant.power_metrics.ch3_current = m.measurements[INA3221_CH3].current;
measurement->variant.power_metrics.ch1_voltage = m.measurements[INA3221_CH1].voltage;
measurement->variant.power_metrics.ch1_current = m.measurements[INA3221_CH1].current;
measurement->variant.power_metrics.ch2_voltage = m.measurements[INA3221_CH2].voltage;
measurement->variant.power_metrics.ch2_current = m.measurements[INA3221_CH2].current;
measurement->variant.power_metrics.ch3_voltage = m.measurements[INA3221_CH3].voltage;
measurement->variant.power_metrics.ch3_current = m.measurements[INA3221_CH3].current;
return true;
return true;
}
uint16_t INA3221Sensor::getBusVoltageMv()
{
return lround(ina3221.getVoltage(BAT_CH) * 1000);
}
uint16_t INA3221Sensor::getBusVoltageMv() { return lround(ina3221.getVoltage(BAT_CH) * 1000); }
int16_t INA3221Sensor::getCurrentMa()
{
return lround(ina3221.getCurrent(BAT_CH));
}
int16_t INA3221Sensor::getCurrentMa() { return lround(ina3221.getCurrent(BAT_CH)); }
#endif

View File

@@ -16,45 +16,44 @@
#define INA3221_BAT_CH INA3221_CH1
#endif
class INA3221Sensor : public TelemetrySensor, VoltageSensor, CurrentSensor
{
private:
INA3221 ina3221 = INA3221(INA3221_ADDR42_SDA);
class INA3221Sensor : public TelemetrySensor, VoltageSensor, CurrentSensor {
private:
INA3221 ina3221 = INA3221(INA3221_ADDR42_SDA);
// channel to report voltage/current for environment metrics
static const ina3221_ch_t ENV_CH = INA3221_ENV_CH;
// channel to report voltage/current for environment metrics
static const ina3221_ch_t ENV_CH = INA3221_ENV_CH;
// channel to report battery voltage for device_battery_ina_address
static const ina3221_ch_t BAT_CH = INA3221_BAT_CH;
// channel to report battery voltage for device_battery_ina_address
static const ina3221_ch_t BAT_CH = INA3221_BAT_CH;
// get a single measurement for a channel
struct _INA3221Measurement getMeasurement(ina3221_ch_t ch);
// get a single measurement for a channel
struct _INA3221Measurement getMeasurement(ina3221_ch_t ch);
// get all measurements for all channels
struct _INA3221Measurements getMeasurements();
// get all measurements for all channels
struct _INA3221Measurements getMeasurements();
bool getEnvironmentMetrics(meshtastic_Telemetry *measurement);
bool getPowerMetrics(meshtastic_Telemetry *measurement);
bool getEnvironmentMetrics(meshtastic_Telemetry *measurement);
bool getPowerMetrics(meshtastic_Telemetry *measurement);
protected:
void setup() override;
protected:
void setup() override;
public:
INA3221Sensor();
int32_t runOnce() override;
bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual uint16_t getBusVoltageMv() override;
virtual int16_t getCurrentMa() override;
public:
INA3221Sensor();
int32_t runOnce() override;
bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual uint16_t getBusVoltageMv() override;
virtual int16_t getCurrentMa() override;
};
struct _INA3221Measurement {
float voltage;
float current;
float voltage;
float current;
};
struct _INA3221Measurements {
// INA3221 has 3 channels
struct _INA3221Measurement measurements[3];
// INA3221 has 3 channels
struct _INA3221Measurement measurements[3];
};
#endif

View File

@@ -19,148 +19,144 @@ uint8_t data[SENSOR_BUF_SIZE]; // decode
#define ACK_PKT_PARA "ACK"
enum sensor_pkt_type {
PKT_TYPE_ACK = 0x00, // uin32_t
PKT_TYPE_CMD_COLLECT_INTERVAL = 0xA0, // uin32_t
PKT_TYPE_CMD_BEEP_ON = 0xA1, // uin32_t ms: on time
PKT_TYPE_CMD_BEEP_OFF = 0xA2,
PKT_TYPE_CMD_SHUTDOWN = 0xA3, // uin32_t
PKT_TYPE_CMD_POWER_ON = 0xA4,
PKT_TYPE_SENSOR_SCD41_TEMP = 0xB0, // float
PKT_TYPE_SENSOR_SCD41_HUMIDITY = 0xB1, // float
PKT_TYPE_SENSOR_SCD41_CO2 = 0xB2, // float
PKT_TYPE_SENSOR_AHT20_TEMP = 0xB3, // float
PKT_TYPE_SENSOR_AHT20_HUMIDITY = 0xB4, // float
PKT_TYPE_SENSOR_TVOC_INDEX = 0xB5, // float
PKT_TYPE_ACK = 0x00, // uin32_t
PKT_TYPE_CMD_COLLECT_INTERVAL = 0xA0, // uin32_t
PKT_TYPE_CMD_BEEP_ON = 0xA1, // uin32_t ms: on time
PKT_TYPE_CMD_BEEP_OFF = 0xA2,
PKT_TYPE_CMD_SHUTDOWN = 0xA3, // uin32_t
PKT_TYPE_CMD_POWER_ON = 0xA4,
PKT_TYPE_SENSOR_SCD41_TEMP = 0xB0, // float
PKT_TYPE_SENSOR_SCD41_HUMIDITY = 0xB1, // float
PKT_TYPE_SENSOR_SCD41_CO2 = 0xB2, // float
PKT_TYPE_SENSOR_AHT20_TEMP = 0xB3, // float
PKT_TYPE_SENSOR_AHT20_HUMIDITY = 0xB4, // float
PKT_TYPE_SENSOR_TVOC_INDEX = 0xB5, // float
};
static int cmd_send(uint8_t cmd, const char *p_data, uint8_t len)
{
uint8_t send_buf[32] = {0};
uint8_t send_data[32] = {0};
if (len > 31) {
return -1;
}
uint8_t index = 1;
send_data[0] = cmd;
if (len > 0 && p_data != NULL) {
memcpy(&send_data[1], p_data, len);
index += len;
}
cobs_encode_result ret = cobs_encode(send_buf, sizeof(send_buf), send_data, index);
// LOG_DEBUG("cobs TX status:%d, len:%d, type 0x%x", ret.status, ret.out_len, cmd);
if (ret.status == COBS_ENCODE_OK) {
return uart_write_bytes(SENSOR_PORT_NUM, send_buf, ret.out_len + 1);
}
static int cmd_send(uint8_t cmd, const char *p_data, uint8_t len) {
uint8_t send_buf[32] = {0};
uint8_t send_data[32] = {0};
if (len > 31) {
return -1;
}
uint8_t index = 1;
send_data[0] = cmd;
if (len > 0 && p_data != NULL) {
memcpy(&send_data[1], p_data, len);
index += len;
}
cobs_encode_result ret = cobs_encode(send_buf, sizeof(send_buf), send_data, index);
// LOG_DEBUG("cobs TX status:%d, len:%d, type 0x%x", ret.status, ret.out_len, cmd);
if (ret.status == COBS_ENCODE_OK) {
return uart_write_bytes(SENSOR_PORT_NUM, send_buf, ret.out_len + 1);
}
return -1;
}
bool IndicatorSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("%s: init", sensorName);
setup();
return true;
bool IndicatorSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("%s: init", sensorName);
setup();
return true;
}
void IndicatorSensor::setup()
{
uart_config_t uart_config = {
.baud_rate = SENSOR_BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_APB,
};
int intr_alloc_flags = 0;
char buffer[11];
void IndicatorSensor::setup() {
uart_config_t uart_config = {
.baud_rate = SENSOR_BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_APB,
};
int intr_alloc_flags = 0;
char buffer[11];
uart_driver_install(SENSOR_PORT_NUM, SENSOR_BUF_SIZE * 2, 0, 0, NULL, intr_alloc_flags);
uart_param_config(SENSOR_PORT_NUM, &uart_config);
uart_set_pin(SENSOR_PORT_NUM, SENSOR_RP2040_TXD, SENSOR_RP2040_RXD, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
cmd_send(PKT_TYPE_CMD_POWER_ON, NULL, 0);
// measure and send only once every minute, for the phone API
const char *interval = ultoa(60000, buffer, 10);
cmd_send(PKT_TYPE_CMD_COLLECT_INTERVAL, interval, strlen(interval) + 1);
uart_driver_install(SENSOR_PORT_NUM, SENSOR_BUF_SIZE * 2, 0, 0, NULL, intr_alloc_flags);
uart_param_config(SENSOR_PORT_NUM, &uart_config);
uart_set_pin(SENSOR_PORT_NUM, SENSOR_RP2040_TXD, SENSOR_RP2040_RXD, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
cmd_send(PKT_TYPE_CMD_POWER_ON, NULL, 0);
// measure and send only once every minute, for the phone API
const char *interval = ultoa(60000, buffer, 10);
cmd_send(PKT_TYPE_CMD_COLLECT_INTERVAL, interval, strlen(interval) + 1);
}
bool IndicatorSensor::getMetrics(meshtastic_Telemetry *measurement)
{
cobs_decode_result ret;
int len = uart_read_bytes(SENSOR_PORT_NUM, buf, (SENSOR_BUF_SIZE - 1), 100 / portTICK_PERIOD_MS);
bool IndicatorSensor::getMetrics(meshtastic_Telemetry *measurement) {
cobs_decode_result ret;
int len = uart_read_bytes(SENSOR_PORT_NUM, buf, (SENSOR_BUF_SIZE - 1), 100 / portTICK_PERIOD_MS);
float value = 0.0;
uint8_t *p_buf_start = buf;
uint8_t *p_buf_end = buf;
if (len > 0) {
while (p_buf_start < (buf + len)) {
p_buf_end = p_buf_start;
while (p_buf_end < (buf + len)) {
if (*p_buf_end == 0x00) {
break;
}
p_buf_end++;
}
// decode buf
memset(data, 0, sizeof(data));
ret = cobs_decode(data, sizeof(data), p_buf_start, p_buf_end - p_buf_start);
// LOG_DEBUG("cobs RX status:%d, len:%d, type:0x%x ", ret.status, ret.out_len, data[0]);
if (ret.out_len > 1 && ret.status == COBS_DECODE_OK) {
value = 0.0;
uint8_t pkt_type = data[0];
switch (pkt_type) {
case PKT_TYPE_SENSOR_SCD41_CO2: {
memcpy(&value, &data[1], sizeof(value));
// LOG_DEBUG("CO2: %.1f", value);
cmd_send(PKT_TYPE_ACK, ACK_PKT_PARA, 4);
break;
}
case PKT_TYPE_SENSOR_AHT20_TEMP: {
memcpy(&value, &data[1], sizeof(value));
// LOG_DEBUG("Temp: %.1f", value);
cmd_send(PKT_TYPE_ACK, ACK_PKT_PARA, 4);
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.temperature = value;
break;
}
case PKT_TYPE_SENSOR_AHT20_HUMIDITY: {
memcpy(&value, &data[1], sizeof(value));
// LOG_DEBUG("Humidity: %.1f", value);
cmd_send(PKT_TYPE_ACK, ACK_PKT_PARA, 4);
measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.environment_metrics.relative_humidity = value;
break;
}
case PKT_TYPE_SENSOR_TVOC_INDEX: {
memcpy(&value, &data[1], sizeof(value));
// LOG_DEBUG("Tvoc: %.1f", value);
cmd_send(PKT_TYPE_ACK, ACK_PKT_PARA, 4);
measurement->variant.environment_metrics.has_iaq = true;
measurement->variant.environment_metrics.iaq = value;
break;
}
default:
break;
}
}
p_buf_start = p_buf_end + 1; // next message
float value = 0.0;
uint8_t *p_buf_start = buf;
uint8_t *p_buf_end = buf;
if (len > 0) {
while (p_buf_start < (buf + len)) {
p_buf_end = p_buf_start;
while (p_buf_end < (buf + len)) {
if (*p_buf_end == 0x00) {
break;
}
return true;
p_buf_end++;
}
// decode buf
memset(data, 0, sizeof(data));
ret = cobs_decode(data, sizeof(data), p_buf_start, p_buf_end - p_buf_start);
// LOG_DEBUG("cobs RX status:%d, len:%d, type:0x%x ", ret.status, ret.out_len, data[0]);
if (ret.out_len > 1 && ret.status == COBS_DECODE_OK) {
value = 0.0;
uint8_t pkt_type = data[0];
switch (pkt_type) {
case PKT_TYPE_SENSOR_SCD41_CO2: {
memcpy(&value, &data[1], sizeof(value));
// LOG_DEBUG("CO2: %.1f", value);
cmd_send(PKT_TYPE_ACK, ACK_PKT_PARA, 4);
break;
}
case PKT_TYPE_SENSOR_AHT20_TEMP: {
memcpy(&value, &data[1], sizeof(value));
// LOG_DEBUG("Temp: %.1f", value);
cmd_send(PKT_TYPE_ACK, ACK_PKT_PARA, 4);
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.temperature = value;
break;
}
case PKT_TYPE_SENSOR_AHT20_HUMIDITY: {
memcpy(&value, &data[1], sizeof(value));
// LOG_DEBUG("Humidity: %.1f", value);
cmd_send(PKT_TYPE_ACK, ACK_PKT_PARA, 4);
measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.environment_metrics.relative_humidity = value;
break;
}
case PKT_TYPE_SENSOR_TVOC_INDEX: {
memcpy(&value, &data[1], sizeof(value));
// LOG_DEBUG("Tvoc: %.1f", value);
cmd_send(PKT_TYPE_ACK, ACK_PKT_PARA, 4);
measurement->variant.environment_metrics.has_iaq = true;
measurement->variant.environment_metrics.iaq = value;
break;
}
default:
break;
}
}
p_buf_start = p_buf_end + 1; // next message
}
return false;
return true;
}
return false;
}
#endif

View File

@@ -5,15 +5,14 @@
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
class IndicatorSensor : public TelemetrySensor
{
public:
IndicatorSensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
class IndicatorSensor : public TelemetrySensor {
public:
IndicatorSensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
private:
void setup();
private:
void setup();
};
#endif

View File

@@ -10,32 +10,30 @@
LPS22HBSensor::LPS22HBSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_LPS22, "LPS22HB") {}
bool LPS22HBSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
status = lps22hb.begin_I2C(dev->address.address, bus);
if (!status) {
return status;
}
lps22hb.setDataRate(LPS22_RATE_10_HZ);
initI2CSensor();
bool LPS22HBSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
status = lps22hb.begin_I2C(dev->address.address, bus);
if (!status) {
return status;
}
lps22hb.setDataRate(LPS22_RATE_10_HZ);
initI2CSensor();
return status;
}
bool LPS22HBSensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
bool LPS22HBSensor::getMetrics(meshtastic_Telemetry *measurement) {
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_barometric_pressure = true;
sensors_event_t temp;
sensors_event_t pressure;
lps22hb.getEvent(&pressure, &temp);
sensors_event_t temp;
sensors_event_t pressure;
lps22hb.getEvent(&pressure, &temp);
measurement->variant.environment_metrics.temperature = temp.temperature;
measurement->variant.environment_metrics.barometric_pressure = pressure.pressure;
measurement->variant.environment_metrics.temperature = temp.temperature;
measurement->variant.environment_metrics.barometric_pressure = pressure.pressure;
return true;
return true;
}
#endif

View File

@@ -7,15 +7,14 @@
#include <Adafruit_LPS2X.h>
#include <Adafruit_Sensor.h>
class LPS22HBSensor : public TelemetrySensor
{
private:
Adafruit_LPS22 lps22hb;
class LPS22HBSensor : public TelemetrySensor {
private:
Adafruit_LPS22 lps22hb;
public:
LPS22HBSensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
LPS22HBSensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -9,65 +9,60 @@
LTR390UVSensor::LTR390UVSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_LTR390UV, "LTR390UV") {}
bool LTR390UVSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
bool LTR390UVSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
status = ltr390uv.begin(bus);
if (!status) {
return status;
}
ltr390uv.setMode(LTR390_MODE_UVS);
ltr390uv.setGain(LTR390_GAIN_18); // Datasheet default
ltr390uv.setResolution(LTR390_RESOLUTION_20BIT); // Datasheet default
initI2CSensor();
status = ltr390uv.begin(bus);
if (!status) {
return status;
}
ltr390uv.setMode(LTR390_MODE_UVS);
ltr390uv.setGain(LTR390_GAIN_18); // Datasheet default
ltr390uv.setResolution(LTR390_RESOLUTION_20BIT); // Datasheet default
initI2CSensor();
return status;
}
bool LTR390UVSensor::getMetrics(meshtastic_Telemetry *measurement)
{
LOG_DEBUG("LTR390UV getMetrics");
bool LTR390UVSensor::getMetrics(meshtastic_Telemetry *measurement) {
LOG_DEBUG("LTR390UV getMetrics");
// Because the sensor does not measure Lux and UV at the same time, we need to read them in two passes.
if (ltr390uv.newDataAvailable()) {
measurement->variant.environment_metrics.has_lux = true;
measurement->variant.environment_metrics.has_uv_lux = true;
// Because the sensor does not measure Lux and UV at the same time, we need to read them in two passes.
if (ltr390uv.newDataAvailable()) {
measurement->variant.environment_metrics.has_lux = true;
measurement->variant.environment_metrics.has_uv_lux = true;
if (ltr390uv.getMode() == LTR390_MODE_ALS) {
lastLuxReading = 0.6 * ltr390uv.readALS() / (1 * 4); // Datasheet page 23 for gain x1 and 20bit resolution
LOG_DEBUG("LTR390UV Lux reading: %f", lastLuxReading);
if (ltr390uv.getMode() == LTR390_MODE_ALS) {
lastLuxReading = 0.6 * ltr390uv.readALS() / (1 * 4); // Datasheet page 23 for gain x1 and 20bit resolution
LOG_DEBUG("LTR390UV Lux reading: %f", lastLuxReading);
measurement->variant.environment_metrics.lux = lastLuxReading;
measurement->variant.environment_metrics.uv_lux = lastUVReading;
measurement->variant.environment_metrics.lux = lastLuxReading;
measurement->variant.environment_metrics.uv_lux = lastUVReading;
ltr390uv.setGain(
LTR390_GAIN_18); // Recommended for UVI - x18. Do not change, 2300 UV Sensitivity only specified for x18 gain
ltr390uv.setMode(LTR390_MODE_UVS);
ltr390uv.setGain(LTR390_GAIN_18); // Recommended for UVI - x18. Do not change, 2300 UV Sensitivity only specified for x18 gain
ltr390uv.setMode(LTR390_MODE_UVS);
return true;
return true;
} else if (ltr390uv.getMode() == LTR390_MODE_UVS) {
lastUVReading = ltr390uv.readUVS() /
2300.f; // Datasheet page 23 and page 6, only characterisation for gain x18 and 20bit resolution
LOG_DEBUG("LTR390UV UV reading: %f", lastUVReading);
} else if (ltr390uv.getMode() == LTR390_MODE_UVS) {
lastUVReading = ltr390uv.readUVS() / 2300.f; // Datasheet page 23 and page 6, only characterisation for gain x18 and 20bit resolution
LOG_DEBUG("LTR390UV UV reading: %f", lastUVReading);
measurement->variant.environment_metrics.lux = lastLuxReading;
measurement->variant.environment_metrics.uv_lux = lastUVReading;
measurement->variant.environment_metrics.lux = lastLuxReading;
measurement->variant.environment_metrics.uv_lux = lastUVReading;
ltr390uv.setGain(
LTR390_GAIN_1); // x1 gain will already max out the sensor at direct sunlight, so no need to increase it
ltr390uv.setMode(LTR390_MODE_ALS);
ltr390uv.setGain(LTR390_GAIN_1); // x1 gain will already max out the sensor at direct sunlight, so no need to increase it
ltr390uv.setMode(LTR390_MODE_ALS);
return true;
}
return true;
}
}
// In case we fail to read the sensor mode, set the has_lux and has_uv_lux back to false
measurement->variant.environment_metrics.has_lux = false;
measurement->variant.environment_metrics.has_uv_lux = false;
// In case we fail to read the sensor mode, set the has_lux and has_uv_lux back to false
measurement->variant.environment_metrics.has_lux = false;
measurement->variant.environment_metrics.has_uv_lux = false;
return false;
return false;
}
#endif

View File

@@ -6,17 +6,16 @@
#include "TelemetrySensor.h"
#include <Adafruit_LTR390.h>
class LTR390UVSensor : public TelemetrySensor
{
private:
Adafruit_LTR390 ltr390uv = Adafruit_LTR390();
float lastLuxReading = 0;
float lastUVReading = 0;
class LTR390UVSensor : public TelemetrySensor {
private:
Adafruit_LTR390 ltr390uv = Adafruit_LTR390();
float lastLuxReading = 0;
float lastUVReading = 0;
public:
LTR390UVSensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
LTR390UVSensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -2,12 +2,11 @@
#if !MESHTASTIC_EXCLUDE_I2C && __has_include(<Adafruit_MAX1704X.h>)
MAX17048Singleton *MAX17048Singleton::GetInstance()
{
if (pinstance == nullptr) {
pinstance = new MAX17048Singleton();
}
return pinstance;
MAX17048Singleton *MAX17048Singleton::GetInstance() {
if (pinstance == nullptr) {
pinstance = new MAX17048Singleton();
}
return pinstance;
}
MAX17048Singleton::MAX17048Singleton() {}
@@ -16,160 +15,147 @@ MAX17048Singleton::~MAX17048Singleton() {}
MAX17048Singleton *MAX17048Singleton::pinstance{nullptr};
bool MAX17048Singleton::runOnce(TwoWire *theWire)
{
initialized = begin(theWire);
LOG_DEBUG("%s::runOnce %s", sensorStr, initialized ? "began ok" : "begin failed");
return initialized;
bool MAX17048Singleton::runOnce(TwoWire *theWire) {
initialized = begin(theWire);
LOG_DEBUG("%s::runOnce %s", sensorStr, initialized ? "began ok" : "begin failed");
return initialized;
}
bool MAX17048Singleton::isBatteryCharging()
{
float volts = cellVoltage();
if (isnan(volts)) {
LOG_DEBUG("%s::isBatteryCharging not connected", sensorStr);
return 0;
}
bool MAX17048Singleton::isBatteryCharging() {
float volts = cellVoltage();
if (isnan(volts)) {
LOG_DEBUG("%s::isBatteryCharging not connected", sensorStr);
return 0;
}
MAX17048ChargeSample sample;
sample.chargeRate = chargeRate(); // charge/discharge rate in percent/hr
sample.cellPercent = cellPercent(); // state of charge in percent 0 to 100
chargeSamples.push(sample); // save a sample into a fifo buffer
MAX17048ChargeSample sample;
sample.chargeRate = chargeRate(); // charge/discharge rate in percent/hr
sample.cellPercent = cellPercent(); // state of charge in percent 0 to 100
chargeSamples.push(sample); // save a sample into a fifo buffer
// Keep the fifo buffer trimmed
while (chargeSamples.size() > MAX17048_CHARGING_SAMPLES)
chargeSamples.pop();
// Keep the fifo buffer trimmed
while (chargeSamples.size() > MAX17048_CHARGING_SAMPLES)
chargeSamples.pop();
// Based on the past n samples, is the lipo charging, discharging or idle
if (chargeSamples.front().chargeRate > MAX17048_CHARGING_MINIMUM_RATE &&
chargeSamples.back().chargeRate > MAX17048_CHARGING_MINIMUM_RATE) {
if (chargeSamples.front().cellPercent > chargeSamples.back().cellPercent)
chargeState = MAX17048ChargeState::EXPORT;
else if (chargeSamples.front().cellPercent < chargeSamples.back().cellPercent)
chargeState = MAX17048ChargeState::IMPORT;
else
chargeState = MAX17048ChargeState::IDLE;
} else {
chargeState = MAX17048ChargeState::IDLE;
}
// Based on the past n samples, is the lipo charging, discharging or idle
if (chargeSamples.front().chargeRate > MAX17048_CHARGING_MINIMUM_RATE && chargeSamples.back().chargeRate > MAX17048_CHARGING_MINIMUM_RATE) {
if (chargeSamples.front().cellPercent > chargeSamples.back().cellPercent)
chargeState = MAX17048ChargeState::EXPORT;
else if (chargeSamples.front().cellPercent < chargeSamples.back().cellPercent)
chargeState = MAX17048ChargeState::IMPORT;
else
chargeState = MAX17048ChargeState::IDLE;
} else {
chargeState = MAX17048ChargeState::IDLE;
}
LOG_DEBUG("%s::isBatteryCharging %s volts: %.3f soc: %.3f rate: %.3f", sensorStr, chargeLabels[chargeState], volts,
sample.cellPercent, sample.chargeRate);
return chargeState == MAX17048ChargeState::IMPORT;
LOG_DEBUG("%s::isBatteryCharging %s volts: %.3f soc: %.3f rate: %.3f", sensorStr, chargeLabels[chargeState], volts, sample.cellPercent,
sample.chargeRate);
return chargeState == MAX17048ChargeState::IMPORT;
}
uint16_t MAX17048Singleton::getBusVoltageMv()
{
float volts = cellVoltage();
if (isnan(volts)) {
LOG_DEBUG("%s::getBusVoltageMv is not connected", sensorStr);
return 0;
}
LOG_DEBUG("%s::getBusVoltageMv %.3fmV", sensorStr, volts);
return (uint16_t)(volts * 1000.0f);
uint16_t MAX17048Singleton::getBusVoltageMv() {
float volts = cellVoltage();
if (isnan(volts)) {
LOG_DEBUG("%s::getBusVoltageMv is not connected", sensorStr);
return 0;
}
LOG_DEBUG("%s::getBusVoltageMv %.3fmV", sensorStr, volts);
return (uint16_t)(volts * 1000.0f);
}
uint8_t MAX17048Singleton::getBusBatteryPercent()
{
float soc = cellPercent();
LOG_DEBUG("%s::getBusBatteryPercent %.1f%%", sensorStr, soc);
return clamp(static_cast<uint8_t>(round(soc)), static_cast<uint8_t>(0), static_cast<uint8_t>(100));
uint8_t MAX17048Singleton::getBusBatteryPercent() {
float soc = cellPercent();
LOG_DEBUG("%s::getBusBatteryPercent %.1f%%", sensorStr, soc);
return clamp(static_cast<uint8_t>(round(soc)), static_cast<uint8_t>(0), static_cast<uint8_t>(100));
}
uint16_t MAX17048Singleton::getTimeToGoSecs()
{
float rate = chargeRate(); // charge/discharge rate in percent/hr
float soc = cellPercent(); // state of charge in percent 0 to 100
soc = clamp(soc, 0.0f, 100.0f); // clamp soc between 0 and 100%
float ttg = ((100.0f - soc) / rate) * 3600.0f; // calculate seconds to charge/discharge
LOG_DEBUG("%s::getTimeToGoSecs %.0f seconds", sensorStr, ttg);
return (uint16_t)ttg;
uint16_t MAX17048Singleton::getTimeToGoSecs() {
float rate = chargeRate(); // charge/discharge rate in percent/hr
float soc = cellPercent(); // state of charge in percent 0 to 100
soc = clamp(soc, 0.0f, 100.0f); // clamp soc between 0 and 100%
float ttg = ((100.0f - soc) / rate) * 3600.0f; // calculate seconds to charge/discharge
LOG_DEBUG("%s::getTimeToGoSecs %.0f seconds", sensorStr, ttg);
return (uint16_t)ttg;
}
bool MAX17048Singleton::isBatteryConnected()
{
float volts = cellVoltage();
if (isnan(volts)) {
LOG_DEBUG("%s::isBatteryConnected is not connected", sensorStr);
return false;
}
bool MAX17048Singleton::isBatteryConnected() {
float volts = cellVoltage();
if (isnan(volts)) {
LOG_DEBUG("%s::isBatteryConnected is not connected", sensorStr);
return false;
}
// if a valid voltage is returned, then battery must be connected
// if a valid voltage is returned, then battery must be connected
return true;
}
bool MAX17048Singleton::isExternallyPowered() {
float volts = cellVoltage();
if (isnan(volts)) {
// if the battery is not connected then there must be external power
LOG_DEBUG("%s::isExternallyPowered battery is", sensorStr);
return true;
}
bool MAX17048Singleton::isExternallyPowered()
{
float volts = cellVoltage();
if (isnan(volts)) {
// if the battery is not connected then there must be external power
LOG_DEBUG("%s::isExternallyPowered battery is", sensorStr);
return true;
}
// if the bus voltage is over MAX17048_BUS_POWER_VOLTS, then the external power
// is assumed to be connected
LOG_DEBUG("%s::isExternallyPowered %s connected", sensorStr, volts >= MAX17048_BUS_POWER_VOLTS ? "is" : "is not");
return volts >= MAX17048_BUS_POWER_VOLTS;
}
// if the bus voltage is over MAX17048_BUS_POWER_VOLTS, then the external power
// is assumed to be connected
LOG_DEBUG("%s::isExternallyPowered %s connected", sensorStr, volts >= MAX17048_BUS_POWER_VOLTS ? "is" : "is not");
return volts >= MAX17048_BUS_POWER_VOLTS;
}
#if (HAS_TELEMETRY && (!MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR || !MESHTASTIC_EXCLUDE_POWER_TELEMETRY))
MAX17048Sensor::MAX17048Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_MAX17048, "MAX17048") {}
int32_t MAX17048Sensor::runOnce()
{
if (isInitialized()) {
LOG_INFO("Init sensor: %s is already initialised", sensorName);
return true;
}
int32_t MAX17048Sensor::runOnce() {
if (isInitialized()) {
LOG_INFO("Init sensor: %s is already initialised", sensorName);
return true;
}
LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
// Get a singleton instance and initialise the max17048
if (max17048 == nullptr) {
max17048 = MAX17048Singleton::GetInstance();
}
status = max17048->runOnce(nodeTelemetrySensorsMap[sensorType].second);
return initI2CSensor();
// Get a singleton instance and initialise the max17048
if (max17048 == nullptr) {
max17048 = MAX17048Singleton::GetInstance();
}
status = max17048->runOnce(nodeTelemetrySensorsMap[sensorType].second);
return initI2CSensor();
}
void MAX17048Sensor::setup() {}
bool MAX17048Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
LOG_DEBUG("MAX17048 getMetrics id: %i", measurement->which_variant);
bool MAX17048Sensor::getMetrics(meshtastic_Telemetry *measurement) {
LOG_DEBUG("MAX17048 getMetrics id: %i", measurement->which_variant);
float volts = max17048->cellVoltage();
if (isnan(volts)) {
LOG_DEBUG("MAX17048 getMetrics battery is not connected");
return false;
}
float volts = max17048->cellVoltage();
if (isnan(volts)) {
LOG_DEBUG("MAX17048 getMetrics battery is not connected");
return false;
}
float rate = max17048->chargeRate(); // charge/discharge rate in percent/hr
float soc = max17048->cellPercent(); // state of charge in percent 0 to 100
soc = clamp(soc, 0.0f, 100.0f); // clamp soc between 0 and 100%
float ttg = (100.0f - soc) / rate; // calculate hours to charge/discharge
float rate = max17048->chargeRate(); // charge/discharge rate in percent/hr
float soc = max17048->cellPercent(); // state of charge in percent 0 to 100
soc = clamp(soc, 0.0f, 100.0f); // clamp soc between 0 and 100%
float ttg = (100.0f - soc) / rate; // calculate hours to charge/discharge
LOG_DEBUG("MAX17048 getMetrics volts: %.3fV soc: %.1f%% ttg: %.1f hours", volts, soc, ttg);
if ((int)measurement->which_variant == meshtastic_Telemetry_power_metrics_tag) {
measurement->variant.power_metrics.has_ch1_voltage = true;
measurement->variant.power_metrics.ch1_voltage = volts;
} else if ((int)measurement->which_variant == meshtastic_Telemetry_device_metrics_tag) {
measurement->variant.device_metrics.has_battery_level = true;
measurement->variant.device_metrics.has_voltage = true;
measurement->variant.device_metrics.battery_level = static_cast<uint32_t>(round(soc));
measurement->variant.device_metrics.voltage = volts;
}
return true;
LOG_DEBUG("MAX17048 getMetrics volts: %.3fV soc: %.1f%% ttg: %.1f hours", volts, soc, ttg);
if ((int)measurement->which_variant == meshtastic_Telemetry_power_metrics_tag) {
measurement->variant.power_metrics.has_ch1_voltage = true;
measurement->variant.power_metrics.ch1_voltage = volts;
} else if ((int)measurement->which_variant == meshtastic_Telemetry_device_metrics_tag) {
measurement->variant.device_metrics.has_battery_level = true;
measurement->variant.device_metrics.has_voltage = true;
measurement->variant.device_metrics.battery_level = static_cast<uint32_t>(round(soc));
measurement->variant.device_metrics.voltage = volts;
}
return true;
}
uint16_t MAX17048Sensor::getBusVoltageMv()
{
return max17048->getBusVoltageMv();
};
uint16_t MAX17048Sensor::getBusVoltageMv() { return max17048->getBusVoltageMv(); };
#endif

View File

@@ -25,83 +25,81 @@
#include <queue>
struct MAX17048ChargeSample {
float cellPercent;
float chargeRate;
float cellPercent;
float chargeRate;
};
enum MAX17048ChargeState { IDLE, EXPORT, IMPORT };
// Singleton wrapper for the Adafruit_MAX17048 class
class MAX17048Singleton : public Adafruit_MAX17048
{
private:
static MAX17048Singleton *pinstance;
bool initialized = false;
std::queue<MAX17048ChargeSample> chargeSamples;
MAX17048ChargeState chargeState = IDLE;
const String chargeLabels[3] = {F("idle"), F("export"), F("import")};
const char *sensorStr = "MAX17048Sensor";
class MAX17048Singleton : public Adafruit_MAX17048 {
private:
static MAX17048Singleton *pinstance;
bool initialized = false;
std::queue<MAX17048ChargeSample> chargeSamples;
MAX17048ChargeState chargeState = IDLE;
const String chargeLabels[3] = {F("idle"), F("export"), F("import")};
const char *sensorStr = "MAX17048Sensor";
protected:
MAX17048Singleton();
~MAX17048Singleton();
protected:
MAX17048Singleton();
~MAX17048Singleton();
public:
// Create a singleton instance (not thread safe)
static MAX17048Singleton *GetInstance();
public:
// Create a singleton instance (not thread safe)
static MAX17048Singleton *GetInstance();
// Singletons should not be cloneable.
MAX17048Singleton(MAX17048Singleton &other) = delete;
// Singletons should not be cloneable.
MAX17048Singleton(MAX17048Singleton &other) = delete;
// Singletons should not be assignable.
void operator=(const MAX17048Singleton &) = delete;
// Singletons should not be assignable.
void operator=(const MAX17048Singleton &) = delete;
// Initialise the sensor (not thread safe)
virtual bool runOnce(TwoWire *theWire = &Wire);
// Initialise the sensor (not thread safe)
virtual bool runOnce(TwoWire *theWire = &Wire);
// Get the current bus voltage
uint16_t getBusVoltageMv();
// Get the current bus voltage
uint16_t getBusVoltageMv();
// Get the state of charge in percent 0 to 100
uint8_t getBusBatteryPercent();
// Get the state of charge in percent 0 to 100
uint8_t getBusBatteryPercent();
// Calculate the seconds to charge/discharge
uint16_t getTimeToGoSecs();
// Calculate the seconds to charge/discharge
uint16_t getTimeToGoSecs();
// Returns true if the battery sensor has started
inline virtual bool isInitialised() { return initialized; };
// Returns true if the battery sensor has started
inline virtual bool isInitialised() { return initialized; };
// Returns true if the battery is currently on charge (not thread safe)
bool isBatteryCharging();
// Returns true if the battery is currently on charge (not thread safe)
bool isBatteryCharging();
// Returns true if a battery is actually connected
bool isBatteryConnected();
// Returns true if a battery is actually connected
bool isBatteryConnected();
// Returns true if there is bus or external power connected
bool isExternallyPowered();
// Returns true if there is bus or external power connected
bool isExternallyPowered();
};
#if (HAS_TELEMETRY && (!MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR || !MESHTASTIC_EXCLUDE_POWER_TELEMETRY))
class MAX17048Sensor : public TelemetrySensor, VoltageSensor
{
private:
MAX17048Singleton *max17048 = nullptr;
class MAX17048Sensor : public TelemetrySensor, VoltageSensor {
private:
MAX17048Singleton *max17048 = nullptr;
protected:
virtual void setup() override;
protected:
virtual void setup() override;
public:
MAX17048Sensor();
public:
MAX17048Sensor();
// Initialise the sensor
virtual int32_t runOnce() override;
// Initialise the sensor
virtual int32_t runOnce() override;
// Get the current bus voltage and state of charge
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
// Get the current bus voltage and state of charge
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
// Get the current bus voltage
virtual uint16_t getBusVoltageMv() override;
// Get the current bus voltage
virtual uint16_t getBusVoltageMv() override;
};
#endif

View File

@@ -9,75 +9,71 @@
MAX30102Sensor::MAX30102Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_MAX30102, "MAX30102") {}
int32_t MAX30102Sensor::runOnce()
{
LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
int32_t MAX30102Sensor::runOnce() {
LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
if (max30102.begin(*nodeTelemetrySensorsMap[sensorType].second, _speed, nodeTelemetrySensorsMap[sensorType].first) ==
true) // MAX30102 init
{
byte brightness = 60; // 0=Off to 255=50mA
byte sampleAverage = 4; // 1, 2, 4, 8, 16, 32
byte leds = 2; // 1 = Red only, 2 = Red + IR
byte sampleRate = 100; // 50, 100, 200, 400, 800, 1000, 1600, 3200
int pulseWidth = 411; // 69, 118, 215, 411
int adcRange = 4096; // 2048, 4096, 8192, 16384
if (max30102.begin(*nodeTelemetrySensorsMap[sensorType].second, _speed, nodeTelemetrySensorsMap[sensorType].first) == true) // MAX30102 init
{
byte brightness = 60; // 0=Off to 255=50mA
byte sampleAverage = 4; // 1, 2, 4, 8, 16, 32
byte leds = 2; // 1 = Red only, 2 = Red + IR
byte sampleRate = 100; // 50, 100, 200, 400, 800, 1000, 1600, 3200
int pulseWidth = 411; // 69, 118, 215, 411
int adcRange = 4096; // 2048, 4096, 8192, 16384
max30102.enableDIETEMPRDY(); // Enable the temperature ready interrupt
max30102.setup(brightness, sampleAverage, leds, sampleRate, pulseWidth, adcRange);
LOG_DEBUG("MAX30102 Init Succeed");
status = true;
} else {
LOG_ERROR("MAX30102 Init Failed");
status = false;
}
return initI2CSensor();
max30102.enableDIETEMPRDY(); // Enable the temperature ready interrupt
max30102.setup(brightness, sampleAverage, leds, sampleRate, pulseWidth, adcRange);
LOG_DEBUG("MAX30102 Init Succeed");
status = true;
} else {
LOG_ERROR("MAX30102 Init Failed");
status = false;
}
return initI2CSensor();
}
void MAX30102Sensor::setup() {}
bool MAX30102Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
uint32_t ir_buff[MAX30102_BUFFER_LEN];
uint32_t red_buff[MAX30102_BUFFER_LEN];
int32_t spo2;
int8_t spo2_valid;
int32_t heart_rate;
int8_t heart_rate_valid;
float temp = max30102.readTemperature();
bool MAX30102Sensor::getMetrics(meshtastic_Telemetry *measurement) {
uint32_t ir_buff[MAX30102_BUFFER_LEN];
uint32_t red_buff[MAX30102_BUFFER_LEN];
int32_t spo2;
int8_t spo2_valid;
int32_t heart_rate;
int8_t heart_rate_valid;
float temp = max30102.readTemperature();
measurement->variant.environment_metrics.temperature = temp;
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.health_metrics.temperature = temp;
measurement->variant.health_metrics.has_temperature = true;
for (byte i = 0; i < MAX30102_BUFFER_LEN; i++) {
while (max30102.available() == false)
max30102.check();
measurement->variant.environment_metrics.temperature = temp;
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.health_metrics.temperature = temp;
measurement->variant.health_metrics.has_temperature = true;
for (byte i = 0; i < MAX30102_BUFFER_LEN; i++) {
while (max30102.available() == false)
max30102.check();
red_buff[i] = max30102.getRed();
ir_buff[i] = max30102.getIR();
max30102.nextSample();
}
red_buff[i] = max30102.getRed();
ir_buff[i] = max30102.getIR();
max30102.nextSample();
}
maxim_heart_rate_and_oxygen_saturation(ir_buff, MAX30102_BUFFER_LEN, red_buff, &spo2, &spo2_valid, &heart_rate,
&heart_rate_valid);
LOG_DEBUG("heart_rate=%d(%d), sp02=%d(%d)", heart_rate, heart_rate_valid, spo2, spo2_valid);
if (heart_rate_valid) {
measurement->variant.health_metrics.has_heart_bpm = true;
measurement->variant.health_metrics.heart_bpm = heart_rate;
} else {
measurement->variant.health_metrics.has_heart_bpm = false;
}
if (spo2_valid) {
measurement->variant.health_metrics.has_spO2 = true;
measurement->variant.health_metrics.spO2 = spo2;
} else {
measurement->variant.health_metrics.has_spO2 = true;
}
return true;
maxim_heart_rate_and_oxygen_saturation(ir_buff, MAX30102_BUFFER_LEN, red_buff, &spo2, &spo2_valid, &heart_rate, &heart_rate_valid);
LOG_DEBUG("heart_rate=%d(%d), sp02=%d(%d)", heart_rate, heart_rate_valid, spo2, spo2_valid);
if (heart_rate_valid) {
measurement->variant.health_metrics.has_heart_bpm = true;
measurement->variant.health_metrics.heart_bpm = heart_rate;
} else {
measurement->variant.health_metrics.has_heart_bpm = false;
}
if (spo2_valid) {
measurement->variant.health_metrics.has_spO2 = true;
measurement->variant.health_metrics.spO2 = spo2;
} else {
measurement->variant.health_metrics.has_spO2 = true;
}
return true;
}
#endif

View File

@@ -8,19 +8,18 @@
#define MAX30102_BUFFER_LEN 100
class MAX30102Sensor : public TelemetrySensor
{
private:
MAX30105 max30102 = MAX30105();
uint32_t _speed = 200000UL;
class MAX30102Sensor : public TelemetrySensor {
private:
MAX30105 max30102 = MAX30105();
uint32_t _speed = 200000UL;
protected:
virtual void setup() override;
protected:
virtual void setup() override;
public:
MAX30102Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
public:
MAX30102Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
};
#endif

View File

@@ -9,26 +9,24 @@
MCP9808Sensor::MCP9808Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_MCP9808, "MCP9808") {}
bool MCP9808Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
status = mcp9808.begin(dev->address.address, bus);
if (!status) {
return status;
}
mcp9808.setResolution(2);
initI2CSensor();
bool MCP9808Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
status = mcp9808.begin(dev->address.address, bus);
if (!status) {
return status;
}
mcp9808.setResolution(2);
initI2CSensor();
return status;
}
bool MCP9808Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
bool MCP9808Sensor::getMetrics(meshtastic_Telemetry *measurement) {
measurement->variant.environment_metrics.has_temperature = true;
LOG_DEBUG("MCP9808 getMetrics");
measurement->variant.environment_metrics.temperature = mcp9808.readTempC();
return true;
LOG_DEBUG("MCP9808 getMetrics");
measurement->variant.environment_metrics.temperature = mcp9808.readTempC();
return true;
}
#endif

View File

@@ -6,15 +6,14 @@
#include "TelemetrySensor.h"
#include <Adafruit_MCP9808.h>
class MCP9808Sensor : public TelemetrySensor
{
private:
Adafruit_MCP9808 mcp9808;
class MCP9808Sensor : public TelemetrySensor {
private:
Adafruit_MCP9808 mcp9808;
public:
MCP9808Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
MCP9808Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -7,38 +7,36 @@
#include "TelemetrySensor.h"
MLX90614Sensor::MLX90614Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_MLX90614, "MLX90614") {}
int32_t MLX90614Sensor::runOnce()
{
LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
int32_t MLX90614Sensor::runOnce() {
LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
if (mlx.begin(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second) == true) // MLX90614 init
{
LOG_DEBUG("MLX90614 emissivity: %f", mlx.readEmissivity());
if (fabs(MLX90614_EMISSIVITY - mlx.readEmissivity()) > 0.001) {
mlx.writeEmissivity(MLX90614_EMISSIVITY);
LOG_INFO("MLX90614 emissivity updated. In case of weird data, power cycle");
}
LOG_DEBUG("MLX90614 Init Succeed");
status = true;
} else {
LOG_ERROR("MLX90614 Init Failed");
status = false;
if (mlx.begin(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second) == true) // MLX90614 init
{
LOG_DEBUG("MLX90614 emissivity: %f", mlx.readEmissivity());
if (fabs(MLX90614_EMISSIVITY - mlx.readEmissivity()) > 0.001) {
mlx.writeEmissivity(MLX90614_EMISSIVITY);
LOG_INFO("MLX90614 emissivity updated. In case of weird data, power cycle");
}
return initI2CSensor();
LOG_DEBUG("MLX90614 Init Succeed");
status = true;
} else {
LOG_ERROR("MLX90614 Init Failed");
status = false;
}
return initI2CSensor();
}
void MLX90614Sensor::setup() {}
bool MLX90614Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.temperature = mlx.readAmbientTempC();
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.health_metrics.temperature = mlx.readObjectTempC();
measurement->variant.health_metrics.has_temperature = true;
return true;
bool MLX90614Sensor::getMetrics(meshtastic_Telemetry *measurement) {
measurement->variant.environment_metrics.temperature = mlx.readAmbientTempC();
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.health_metrics.temperature = mlx.readObjectTempC();
measurement->variant.health_metrics.has_temperature = true;
return true;
}
#endif

View File

@@ -7,18 +7,17 @@
#define MLX90614_EMISSIVITY 0.98 // human skin
class MLX90614Sensor : public TelemetrySensor
{
private:
Adafruit_MLX90614 mlx = Adafruit_MLX90614();
class MLX90614Sensor : public TelemetrySensor {
private:
Adafruit_MLX90614 mlx = Adafruit_MLX90614();
protected:
virtual void setup() override;
protected:
virtual void setup() override;
public:
MLX90614Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
public:
MLX90614Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
};
#endif

View File

@@ -8,29 +8,27 @@
MLX90632Sensor::MLX90632Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_MLX90632, "MLX90632") {}
bool MLX90632Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
bool MLX90632Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
MLX90632::status returnError;
if (mlx.begin(dev->address.address, *bus, returnError) == true) // MLX90632 init
{
LOG_DEBUG("MLX90632 Init Succeed");
status = true;
} else {
LOG_ERROR("MLX90632 Init Failed");
status = false;
}
initI2CSensor();
return status;
MLX90632::status returnError;
if (mlx.begin(dev->address.address, *bus, returnError) == true) // MLX90632 init
{
LOG_DEBUG("MLX90632 Init Succeed");
status = true;
} else {
LOG_ERROR("MLX90632 Init Failed");
status = false;
}
initI2CSensor();
return status;
}
bool MLX90632Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.temperature = mlx.getObjectTemp(); // Get the object temperature in Fahrenheit
bool MLX90632Sensor::getMetrics(meshtastic_Telemetry *measurement) {
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.temperature = mlx.getObjectTemp(); // Get the object temperature in Fahrenheit
return true;
return true;
}
#endif

View File

@@ -6,15 +6,14 @@
#include "TelemetrySensor.h"
#include <SparkFun_MLX90632_Arduino_Library.h>
class MLX90632Sensor : public TelemetrySensor
{
private:
MLX90632 mlx = MLX90632();
class MLX90632Sensor : public TelemetrySensor {
private:
MLX90632 mlx = MLX90632();
public:
MLX90632Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
MLX90632Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -16,128 +16,121 @@ meshtastic_Nau7802Config nau7802config = meshtastic_Nau7802Config_init_zero;
NAU7802Sensor::NAU7802Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_NAU7802, "NAU7802") {}
bool NAU7802Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
status = nau7802.begin(*bus);
if (!status) {
return status;
}
nau7802.setSampleRate(NAU7802_SPS_320);
if (!loadCalibrationData()) {
LOG_ERROR("Failed to load calibration data");
}
nau7802.calibrateAFE();
LOG_INFO("Offset: %d, Calibration factor: %.2f", nau7802.getZeroOffset(), nau7802.getCalibrationFactor());
initI2CSensor();
bool NAU7802Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
status = nau7802.begin(*bus);
if (!status) {
return status;
}
nau7802.setSampleRate(NAU7802_SPS_320);
if (!loadCalibrationData()) {
LOG_ERROR("Failed to load calibration data");
}
nau7802.calibrateAFE();
LOG_INFO("Offset: %d, Calibration factor: %.2f", nau7802.getZeroOffset(), nau7802.getCalibrationFactor());
initI2CSensor();
return status;
}
bool NAU7802Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
LOG_DEBUG("NAU7802 getMetrics");
nau7802.powerUp();
// Wait for the sensor to become ready for one second max
uint32_t start = millis();
while (!nau7802.available()) {
delay(100);
if (!Throttle::isWithinTimespanMs(start, 1000)) {
nau7802.powerDown();
return false;
}
bool NAU7802Sensor::getMetrics(meshtastic_Telemetry *measurement) {
LOG_DEBUG("NAU7802 getMetrics");
nau7802.powerUp();
// Wait for the sensor to become ready for one second max
uint32_t start = millis();
while (!nau7802.available()) {
delay(100);
if (!Throttle::isWithinTimespanMs(start, 1000)) {
nau7802.powerDown();
return false;
}
measurement->variant.environment_metrics.has_weight = true;
// Check if we have correct calibration values after powerup
LOG_DEBUG("Offset: %d, Calibration factor: %.2f", nau7802.getZeroOffset(), nau7802.getCalibrationFactor());
measurement->variant.environment_metrics.weight = nau7802.getWeight() / 1000; // sample is in kg
nau7802.powerDown();
return true;
}
measurement->variant.environment_metrics.has_weight = true;
// Check if we have correct calibration values after powerup
LOG_DEBUG("Offset: %d, Calibration factor: %.2f", nau7802.getZeroOffset(), nau7802.getCalibrationFactor());
measurement->variant.environment_metrics.weight = nau7802.getWeight() / 1000; // sample is in kg
nau7802.powerDown();
return true;
}
void NAU7802Sensor::calibrate(float weight)
{
nau7802.calculateCalibrationFactor(weight * 1000, 64); // internal sample is in grams
if (!saveCalibrationData()) {
LOG_WARN("Failed to save calibration data");
}
LOG_INFO("Offset: %d, Calibration factor: %.2f", nau7802.getZeroOffset(), nau7802.getCalibrationFactor());
void NAU7802Sensor::calibrate(float weight) {
nau7802.calculateCalibrationFactor(weight * 1000, 64); // internal sample is in grams
if (!saveCalibrationData()) {
LOG_WARN("Failed to save calibration data");
}
LOG_INFO("Offset: %d, Calibration factor: %.2f", nau7802.getZeroOffset(), nau7802.getCalibrationFactor());
}
AdminMessageHandleResult NAU7802Sensor::handleAdminMessage(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request,
meshtastic_AdminMessage *response)
{
AdminMessageHandleResult result;
meshtastic_AdminMessage *response) {
AdminMessageHandleResult result;
switch (request->which_payload_variant) {
case meshtastic_AdminMessage_set_scale_tag:
if (request->set_scale == 0) {
this->tare();
LOG_DEBUG("Client requested to tare scale");
} else {
this->calibrate(request->set_scale);
LOG_DEBUG("Client requested to calibrate to %d kg", request->set_scale);
}
result = AdminMessageHandleResult::HANDLED;
break;
default:
result = AdminMessageHandleResult::NOT_HANDLED;
}
return result;
}
void NAU7802Sensor::tare()
{
nau7802.calculateZeroOffset(64);
if (!saveCalibrationData()) {
LOG_WARN("Failed to save calibration data");
}
LOG_INFO("Offset: %d, Calibration factor: %.2f", nau7802.getZeroOffset(), nau7802.getCalibrationFactor());
}
bool NAU7802Sensor::saveCalibrationData()
{
auto file = SafeFile(nau7802ConfigFileName);
nau7802config.zeroOffset = nau7802.getZeroOffset();
nau7802config.calibrationFactor = nau7802.getCalibrationFactor();
bool okay = false;
LOG_INFO("%s state write to %s", sensorName, nau7802ConfigFileName);
pb_ostream_t stream = {&writecb, static_cast<Print *>(&file), meshtastic_Nau7802Config_size};
if (!pb_encode(&stream, &meshtastic_Nau7802Config_msg, &nau7802config)) {
LOG_ERROR("Error: can't encode protobuf %s", PB_GET_ERROR(&stream));
switch (request->which_payload_variant) {
case meshtastic_AdminMessage_set_scale_tag:
if (request->set_scale == 0) {
this->tare();
LOG_DEBUG("Client requested to tare scale");
} else {
okay = true;
this->calibrate(request->set_scale);
LOG_DEBUG("Client requested to calibrate to %d kg", request->set_scale);
}
// Note: SafeFile::close() already acquires the lock and releases it internally
okay &= file.close();
result = AdminMessageHandleResult::HANDLED;
break;
return okay;
default:
result = AdminMessageHandleResult::NOT_HANDLED;
}
return result;
}
bool NAU7802Sensor::loadCalibrationData()
{
spiLock->lock();
auto file = FSCom.open(nau7802ConfigFileName, FILE_O_READ);
bool okay = false;
if (file) {
LOG_INFO("%s state read from %s", sensorName, nau7802ConfigFileName);
pb_istream_t stream = {&readcb, &file, meshtastic_Nau7802Config_size};
if (!pb_decode(&stream, &meshtastic_Nau7802Config_msg, &nau7802config)) {
LOG_ERROR("Error: can't decode protobuf %s", PB_GET_ERROR(&stream));
} else {
nau7802.setZeroOffset(nau7802config.zeroOffset);
nau7802.setCalibrationFactor(nau7802config.calibrationFactor);
okay = true;
}
file.close();
void NAU7802Sensor::tare() {
nau7802.calculateZeroOffset(64);
if (!saveCalibrationData()) {
LOG_WARN("Failed to save calibration data");
}
LOG_INFO("Offset: %d, Calibration factor: %.2f", nau7802.getZeroOffset(), nau7802.getCalibrationFactor());
}
bool NAU7802Sensor::saveCalibrationData() {
auto file = SafeFile(nau7802ConfigFileName);
nau7802config.zeroOffset = nau7802.getZeroOffset();
nau7802config.calibrationFactor = nau7802.getCalibrationFactor();
bool okay = false;
LOG_INFO("%s state write to %s", sensorName, nau7802ConfigFileName);
pb_ostream_t stream = {&writecb, static_cast<Print *>(&file), meshtastic_Nau7802Config_size};
if (!pb_encode(&stream, &meshtastic_Nau7802Config_msg, &nau7802config)) {
LOG_ERROR("Error: can't encode protobuf %s", PB_GET_ERROR(&stream));
} else {
okay = true;
}
// Note: SafeFile::close() already acquires the lock and releases it internally
okay &= file.close();
return okay;
}
bool NAU7802Sensor::loadCalibrationData() {
spiLock->lock();
auto file = FSCom.open(nau7802ConfigFileName, FILE_O_READ);
bool okay = false;
if (file) {
LOG_INFO("%s state read from %s", sensorName, nau7802ConfigFileName);
pb_istream_t stream = {&readcb, &file, meshtastic_Nau7802Config_size};
if (!pb_decode(&stream, &meshtastic_Nau7802Config_msg, &nau7802config)) {
LOG_ERROR("Error: can't decode protobuf %s", PB_GET_ERROR(&stream));
} else {
LOG_INFO("No %s state found (File: %s)", sensorName, nau7802ConfigFileName);
nau7802.setZeroOffset(nau7802config.zeroOffset);
nau7802.setCalibrationFactor(nau7802config.calibrationFactor);
okay = true;
}
spiLock->unlock();
return okay;
file.close();
} else {
LOG_INFO("No %s state found (File: %s)", sensorName, nau7802ConfigFileName);
}
spiLock->unlock();
return okay;
}
#endif

View File

@@ -7,24 +7,23 @@
#include "TelemetrySensor.h"
#include <SparkFun_Qwiic_Scale_NAU7802_Arduino_Library.h>
class NAU7802Sensor : public TelemetrySensor
{
private:
NAU7802 nau7802;
class NAU7802Sensor : public TelemetrySensor {
private:
NAU7802 nau7802;
protected:
const char *nau7802ConfigFileName = "/prefs/nau7802.dat";
bool saveCalibrationData();
bool loadCalibrationData();
protected:
const char *nau7802ConfigFileName = "/prefs/nau7802.dat";
bool saveCalibrationData();
bool loadCalibrationData();
public:
NAU7802Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
void tare();
void calibrate(float weight);
AdminMessageHandleResult handleAdminMessage(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request,
meshtastic_AdminMessage *response) override;
public:
NAU7802Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
void tare();
void calibrate(float weight);
AdminMessageHandleResult handleAdminMessage(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request,
meshtastic_AdminMessage *response) override;
};
#endif

View File

@@ -9,41 +9,39 @@
OPT3001Sensor::OPT3001Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_OPT3001, "OPT3001") {}
bool OPT3001Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
auto errorCode = opt3001.begin(dev->address.address);
status = errorCode == NO_ERROR;
if (!status) {
return status;
}
OPT3001_Config newConfig;
newConfig.RangeNumber = 0b1100;
newConfig.ConvertionTime = 0b0;
newConfig.Latch = 0b1;
newConfig.ModeOfConversionOperation = 0b11;
OPT3001_ErrorCode errorConfig = opt3001.writeConfig(newConfig);
if (errorConfig != NO_ERROR) {
LOG_ERROR("OPT3001 configuration error #%d", errorConfig);
}
status = errorConfig == NO_ERROR;
initI2CSensor();
bool OPT3001Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
auto errorCode = opt3001.begin(dev->address.address);
status = errorCode == NO_ERROR;
if (!status) {
return status;
}
OPT3001_Config newConfig;
newConfig.RangeNumber = 0b1100;
newConfig.ConvertionTime = 0b0;
newConfig.Latch = 0b1;
newConfig.ModeOfConversionOperation = 0b11;
OPT3001_ErrorCode errorConfig = opt3001.writeConfig(newConfig);
if (errorConfig != NO_ERROR) {
LOG_ERROR("OPT3001 configuration error #%d", errorConfig);
}
status = errorConfig == NO_ERROR;
initI2CSensor();
return status;
}
bool OPT3001Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_lux = true;
OPT3001 result = opt3001.readResult();
bool OPT3001Sensor::getMetrics(meshtastic_Telemetry *measurement) {
measurement->variant.environment_metrics.has_lux = true;
OPT3001 result = opt3001.readResult();
measurement->variant.environment_metrics.lux = result.lux;
LOG_INFO("Lux: %f", measurement->variant.environment_metrics.lux);
measurement->variant.environment_metrics.lux = result.lux;
LOG_INFO("Lux: %f", measurement->variant.environment_metrics.lux);
return true;
return true;
}
#endif

View File

@@ -7,18 +7,17 @@
#include "TelemetrySensor.h"
#include <ClosedCube_OPT3001.h>
class OPT3001Sensor : public TelemetrySensor
{
private:
ClosedCube_OPT3001 opt3001;
class OPT3001Sensor : public TelemetrySensor {
private:
ClosedCube_OPT3001 opt3001;
public:
OPT3001Sensor();
public:
OPT3001Sensor();
#if WIRE_INTERFACES_COUNT > 1
virtual bool onlyWire1() { return true; }
virtual bool onlyWire1() { return true; }
#endif
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -9,21 +9,19 @@
PCT2075Sensor::PCT2075Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_PCT2075, "PCT2075") {}
bool PCT2075Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
status = pct2075.begin(dev->address.address, bus);
bool PCT2075Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
status = pct2075.begin(dev->address.address, bus);
initI2CSensor();
return status;
initI2CSensor();
return status;
}
bool PCT2075Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.temperature = pct2075.getTemperature();
bool PCT2075Sensor::getMetrics(meshtastic_Telemetry *measurement) {
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.temperature = pct2075.getTemperature();
return true;
return true;
}
#endif

View File

@@ -7,15 +7,14 @@
#include "TelemetrySensor.h"
#include <Adafruit_PCT2075.h>
class PCT2075Sensor : public TelemetrySensor
{
private:
Adafruit_PCT2075 pct2075;
class PCT2075Sensor : public TelemetrySensor {
private:
Adafruit_PCT2075 pct2075;
public:
PCT2075Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
PCT2075Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -6,105 +6,102 @@
RAK12035Sensor::RAK12035Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_RAK12035, "RAK12035") {}
bool RAK12035Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
// TODO:: check for up to 2 additional sensors and start them if present.
sensor.set_sensor_addr(RAK120351_ADDR);
delay(100);
sensor.begin(dev->address.address);
bool RAK12035Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
// TODO:: check for up to 2 additional sensors and start them if present.
sensor.set_sensor_addr(RAK120351_ADDR);
delay(100);
sensor.begin(dev->address.address);
// Get sensor firmware version
uint8_t data = 0;
sensor.get_sensor_version(&data);
if (data != 0) {
LOG_INFO("Init sensor: %s", sensorName);
LOG_INFO("RAK12035Sensor Init Succeed \nSensor1 Firmware version: %i, Sensor Name: %s", data, sensorName);
status = true;
sensor.sensor_sleep();
} else {
// If we reach here, it means the sensor did not initialize correctly.
LOG_INFO("Init sensor: %s", sensorName);
LOG_ERROR("RAK12035Sensor Init Failed");
status = false;
}
if (!status) {
return status;
}
setup();
initI2CSensor();
// Get sensor firmware version
uint8_t data = 0;
sensor.get_sensor_version(&data);
if (data != 0) {
LOG_INFO("Init sensor: %s", sensorName);
LOG_INFO("RAK12035Sensor Init Succeed \nSensor1 Firmware version: %i, Sensor Name: %s", data, sensorName);
status = true;
sensor.sensor_sleep();
} else {
// If we reach here, it means the sensor did not initialize correctly.
LOG_INFO("Init sensor: %s", sensorName);
LOG_ERROR("RAK12035Sensor Init Failed");
status = false;
}
if (!status) {
return status;
}
setup();
initI2CSensor();
return status;
}
void RAK12035Sensor::setup()
{
// Set the calibration values
// Reading the saved calibration values from the sensor.
// TODO:: Check for and run calibration check for up to 2 additional sensors if present.
uint16_t zero_val = 0;
uint16_t hundred_val = 0;
uint16_t default_zero_val = 550;
uint16_t default_hundred_val = 420;
sensor.sensor_on();
delay(200);
sensor.get_dry_cal(&zero_val);
sensor.get_wet_cal(&hundred_val);
delay(200);
if (zero_val == 0 || zero_val <= hundred_val) {
LOG_INFO("Dry calibration value is %d", zero_val);
LOG_INFO("Wet calibration value is %d", hundred_val);
LOG_INFO("This does not make sense. You can recalibrate this sensor using the calibration sketch included here: "
"https://github.com/RAKWireless/RAK12035_SoilMoisture.");
LOG_INFO("For now, setting default calibration value for Dry Calibration: %d", default_zero_val);
sensor.set_dry_cal(default_zero_val);
sensor.get_dry_cal(&zero_val);
LOG_INFO("Dry calibration reset complete. New value is %d", zero_val);
}
if (hundred_val == 0 || hundred_val >= zero_val) {
LOG_INFO("Dry calibration value is %d", zero_val);
LOG_INFO("Wet calibration value is %d", hundred_val);
LOG_INFO("This does not make sense. You can recalibrate this sensor using the calibration sketch included here: "
"https://github.com/RAKWireless/RAK12035_SoilMoisture.");
LOG_INFO("For now, setting default calibration value for Wet Calibration: %d", default_hundred_val);
sensor.set_wet_cal(default_hundred_val);
sensor.get_wet_cal(&hundred_val);
LOG_INFO("Wet calibration reset complete. New value is %d", hundred_val);
}
sensor.sensor_sleep();
delay(200);
void RAK12035Sensor::setup() {
// Set the calibration values
// Reading the saved calibration values from the sensor.
// TODO:: Check for and run calibration check for up to 2 additional sensors if present.
uint16_t zero_val = 0;
uint16_t hundred_val = 0;
uint16_t default_zero_val = 550;
uint16_t default_hundred_val = 420;
sensor.sensor_on();
delay(200);
sensor.get_dry_cal(&zero_val);
sensor.get_wet_cal(&hundred_val);
delay(200);
if (zero_val == 0 || zero_val <= hundred_val) {
LOG_INFO("Dry calibration value is %d", zero_val);
LOG_INFO("Wet calibration value is %d", hundred_val);
LOG_INFO("This does not make sense. You can recalibrate this sensor using the calibration sketch included here: "
"https://github.com/RAKWireless/RAK12035_SoilMoisture.");
LOG_INFO("For now, setting default calibration value for Dry Calibration: %d", default_zero_val);
sensor.set_dry_cal(default_zero_val);
sensor.get_dry_cal(&zero_val);
LOG_INFO("Dry calibration reset complete. New value is %d", zero_val);
}
if (hundred_val == 0 || hundred_val >= zero_val) {
LOG_INFO("Dry calibration value is %d", zero_val);
LOG_INFO("Wet calibration value is %d", hundred_val);
LOG_INFO("This does not make sense. You can recalibrate this sensor using the calibration sketch included here: "
"https://github.com/RAKWireless/RAK12035_SoilMoisture.");
LOG_INFO("For now, setting default calibration value for Wet Calibration: %d", default_hundred_val);
sensor.set_wet_cal(default_hundred_val);
sensor.get_wet_cal(&hundred_val);
LOG_INFO("Wet calibration reset complete. New value is %d", hundred_val);
}
sensor.sensor_sleep();
delay(200);
LOG_INFO("Dry calibration value is %d", zero_val);
LOG_INFO("Wet calibration value is %d", hundred_val);
}
bool RAK12035Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
// TODO:: read and send metrics for up to 2 additional soil monitors if present.
// -- how to do this.. this could get a little complex..
// ie - 1> we combine them into an average and send that, 2> we send them as separate metrics
// ^-- these scenarios would require different handling of the metrics in the receiving end and maybe a setting in the
// device ui and an additional proto for that?
measurement->variant.environment_metrics.has_soil_temperature = true;
measurement->variant.environment_metrics.has_soil_moisture = true;
bool RAK12035Sensor::getMetrics(meshtastic_Telemetry *measurement) {
// TODO:: read and send metrics for up to 2 additional soil monitors if present.
// -- how to do this.. this could get a little complex..
// ie - 1> we combine them into an average and send that, 2> we send them as separate metrics
// ^-- these scenarios would require different handling of the metrics in the receiving end and maybe a setting
// in the device ui and an additional proto for that?
measurement->variant.environment_metrics.has_soil_temperature = true;
measurement->variant.environment_metrics.has_soil_moisture = true;
uint8_t moisture = 0;
uint16_t temp = 0;
bool success = false;
uint8_t moisture = 0;
uint16_t temp = 0;
bool success = false;
sensor.sensor_on();
delay(200);
success = sensor.get_sensor_moisture(&moisture);
delay(200);
success &= sensor.get_sensor_temperature(&temp);
delay(200);
sensor.sensor_sleep();
sensor.sensor_on();
delay(200);
success = sensor.get_sensor_moisture(&moisture);
delay(200);
success &= sensor.get_sensor_temperature(&temp);
delay(200);
sensor.sensor_sleep();
if (success == false) {
LOG_ERROR("Failed to read sensor data");
return false;
}
measurement->variant.environment_metrics.soil_temperature = ((float)temp / 10.0f);
measurement->variant.environment_metrics.soil_moisture = moisture;
if (success == false) {
LOG_ERROR("Failed to read sensor data");
return false;
}
measurement->variant.environment_metrics.soil_temperature = ((float)temp / 10.0f);
measurement->variant.environment_metrics.soil_moisture = moisture;
return true;
return true;
}
#endif

View File

@@ -12,18 +12,17 @@
#include "TelemetrySensor.h"
#include <Arduino.h>
class RAK12035Sensor : public TelemetrySensor
{
private:
RAK12035 sensor;
void setup();
class RAK12035Sensor : public TelemetrySensor {
private:
RAK12035 sensor;
void setup();
public:
RAK12035Sensor();
public:
RAK12035Sensor();
#if WIRE_INTERFACES_COUNT > 1
virtual bool onlyWire1() { return true; }
virtual bool onlyWire1() { return true; }
#endif
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -28,182 +28,162 @@ static uint8_t provision = 0;
extern RAK9154Sensor rak9154Sensor;
static void onewire_evt(const uint8_t pid, const uint8_t sid, const SNHUBAPI_EVT_E eid, uint8_t *msg, uint16_t len)
{
switch (eid) {
case SNHUBAPI_EVT_RECV_REQ:
case SNHUBAPI_EVT_RECV_RSP:
break;
static void onewire_evt(const uint8_t pid, const uint8_t sid, const SNHUBAPI_EVT_E eid, uint8_t *msg, uint16_t len) {
switch (eid) {
case SNHUBAPI_EVT_RECV_REQ:
case SNHUBAPI_EVT_RECV_RSP:
break;
case SNHUBAPI_EVT_QSEND:
mySerial.write(msg, len);
break;
case SNHUBAPI_EVT_QSEND:
mySerial.write(msg, len);
break;
case SNHUBAPI_EVT_ADD_SID:
// LOG_INFO("+ADD:SID:[%02x]", msg[0]);
break;
case SNHUBAPI_EVT_ADD_SID:
// LOG_INFO("+ADD:SID:[%02x]", msg[0]);
break;
case SNHUBAPI_EVT_ADD_PID:
// LOG_INFO("+ADD:PID:[%02x]", msg[0]);
case SNHUBAPI_EVT_ADD_PID:
// LOG_INFO("+ADD:PID:[%02x]", msg[0]);
#ifdef BOOT_DATA_REQ
provision = msg[0];
provision = msg[0];
#endif
break;
break;
case SNHUBAPI_EVT_GET_INTV:
break;
case SNHUBAPI_EVT_GET_INTV:
break;
case SNHUBAPI_EVT_GET_ENABLE:
break;
case SNHUBAPI_EVT_GET_ENABLE:
break;
case SNHUBAPI_EVT_SDATA_REQ:
// LOG_INFO("+EVT:PID[%02x],IPSO[%02x]",pid,msg[0]);
// for( uint16_t i=1; i<len; i++)
// {
// LOG_INFO("%02x,", msg[i]);
// }
// LOG_INFO("");
switch (msg[0]) {
case RAK_IPSO_CAPACITY:
dc_prec = msg[1];
if (dc_prec > 100) {
dc_prec = 100;
}
break;
case RAK_IPSO_DC_CURRENT:
dc_cur = (msg[2] << 8) + msg[1];
break;
case RAK_IPSO_DC_VOLTAGE:
dc_vol = (msg[2] << 8) + msg[1];
dc_vol *= 10;
break;
default:
break;
}
rak9154Sensor.setLastRead(millis());
break;
case SNHUBAPI_EVT_REPORT:
// LOG_INFO("+EVT:PID[%02x],IPSO[%02x]",pid,msg[0]);
// for( uint16_t i=1; i<len; i++)
// {
// LOG_INFO("%02x,", msg[i]);
// }
// LOG_INFO("");
switch (msg[0]) {
case RAK_IPSO_CAPACITY:
dc_prec = msg[1];
if (dc_prec > 100) {
dc_prec = 100;
}
break;
case RAK_IPSO_DC_CURRENT:
dc_cur = (msg[1] << 8) + msg[2];
break;
case RAK_IPSO_DC_VOLTAGE:
dc_vol = (msg[1] << 8) + msg[2];
dc_vol *= 10;
break;
default:
break;
}
rak9154Sensor.setLastRead(millis());
break;
case SNHUBAPI_EVT_CHKSUM_ERR:
LOG_INFO("+ERR:CHKSUM");
break;
case SNHUBAPI_EVT_SEQ_ERR:
LOG_INFO("+ERR:SEQUCE");
break;
case SNHUBAPI_EVT_SDATA_REQ:
// LOG_INFO("+EVT:PID[%02x],IPSO[%02x]",pid,msg[0]);
// for( uint16_t i=1; i<len; i++)
// {
// LOG_INFO("%02x,", msg[i]);
// }
// LOG_INFO("");
switch (msg[0]) {
case RAK_IPSO_CAPACITY:
dc_prec = msg[1];
if (dc_prec > 100) {
dc_prec = 100;
}
break;
case RAK_IPSO_DC_CURRENT:
dc_cur = (msg[2] << 8) + msg[1];
break;
case RAK_IPSO_DC_VOLTAGE:
dc_vol = (msg[2] << 8) + msg[1];
dc_vol *= 10;
break;
default:
break;
break;
}
}
rak9154Sensor.setLastRead(millis());
static int32_t onewireHandle()
{
if (provision != 0) {
RakSNHub_Protocl_API.get.data(provision);
provision = 0;
break;
case SNHUBAPI_EVT_REPORT:
// LOG_INFO("+EVT:PID[%02x],IPSO[%02x]",pid,msg[0]);
// for( uint16_t i=1; i<len; i++)
// {
// LOG_INFO("%02x,", msg[i]);
// }
// LOG_INFO("");
switch (msg[0]) {
case RAK_IPSO_CAPACITY:
dc_prec = msg[1];
if (dc_prec > 100) {
dc_prec = 100;
}
break;
case RAK_IPSO_DC_CURRENT:
dc_cur = (msg[1] << 8) + msg[2];
break;
case RAK_IPSO_DC_VOLTAGE:
dc_vol = (msg[1] << 8) + msg[2];
dc_vol *= 10;
break;
default:
break;
}
rak9154Sensor.setLastRead(millis());
while (mySerial.available()) {
char a = mySerial.read();
buff[bufflen++] = a;
delay(2); // continue data, timeout=2ms
}
break;
if (bufflen != 0) {
RakSNHub_Protocl_API.process((uint8_t *)buff, bufflen);
bufflen = 0;
}
case SNHUBAPI_EVT_CHKSUM_ERR:
LOG_INFO("+ERR:CHKSUM");
break;
return 50;
case SNHUBAPI_EVT_SEQ_ERR:
LOG_INFO("+ERR:SEQUCE");
break;
default:
break;
}
}
int32_t RAK9154Sensor::runOnce()
{
if (!rak9154Sensor.isInitialized()) {
onewirePeriodic = new Periodic("onewireHandle", onewireHandle);
static int32_t onewireHandle() {
if (provision != 0) {
RakSNHub_Protocl_API.get.data(provision);
provision = 0;
}
mySerial.begin(9600);
while (mySerial.available()) {
char a = mySerial.read();
buff[bufflen++] = a;
delay(2); // continue data, timeout=2ms
}
RakSNHub_Protocl_API.init(onewire_evt);
if (bufflen != 0) {
RakSNHub_Protocl_API.process((uint8_t *)buff, bufflen);
bufflen = 0;
}
status = true;
initialized = true;
}
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
return 50;
}
void RAK9154Sensor::setup()
{
// Set up oversampling and filter initialization
int32_t RAK9154Sensor::runOnce() {
if (!rak9154Sensor.isInitialized()) {
onewirePeriodic = new Periodic("onewireHandle", onewireHandle);
mySerial.begin(9600);
RakSNHub_Protocl_API.init(onewire_evt);
status = true;
initialized = true;
}
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
bool RAK9154Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
if (getBusVoltageMv() > 0) {
measurement->variant.environment_metrics.has_voltage = true;
measurement->variant.environment_metrics.has_current = true;
measurement->variant.environment_metrics.voltage = (float)getBusVoltageMv() / 1000;
measurement->variant.environment_metrics.current = (float)getCurrentMa() / 1000;
return true;
} else {
return false;
}
void RAK9154Sensor::setup() {
// Set up oversampling and filter initialization
}
uint16_t RAK9154Sensor::getBusVoltageMv()
{
return dc_vol;
bool RAK9154Sensor::getMetrics(meshtastic_Telemetry *measurement) {
if (getBusVoltageMv() > 0) {
measurement->variant.environment_metrics.has_voltage = true;
measurement->variant.environment_metrics.has_current = true;
measurement->variant.environment_metrics.voltage = (float)getBusVoltageMv() / 1000;
measurement->variant.environment_metrics.current = (float)getCurrentMa() / 1000;
return true;
} else {
return false;
}
}
int16_t RAK9154Sensor::getCurrentMa()
{
return dc_cur;
}
uint16_t RAK9154Sensor::getBusVoltageMv() { return dc_vol; }
int RAK9154Sensor::getBusBatteryPercent()
{
return (int)dc_prec;
}
int16_t RAK9154Sensor::getCurrentMa() { return dc_cur; }
bool RAK9154Sensor::isCharging()
{
return (dc_cur > 0) ? true : false;
}
void RAK9154Sensor::setLastRead(uint32_t lastRead)
{
this->lastRead = lastRead;
}
int RAK9154Sensor::getBusBatteryPercent() { return (int)dc_prec; }
bool RAK9154Sensor::isCharging() { return (dc_cur > 0) ? true : false; }
void RAK9154Sensor::setLastRead(uint32_t lastRead) { this->lastRead = lastRead; }
#endif // HAS_RAKPROT

View File

@@ -9,22 +9,21 @@
#include "TelemetrySensor.h"
#include "VoltageSensor.h"
class RAK9154Sensor : public TelemetrySensor, VoltageSensor, CurrentSensor
{
private:
protected:
virtual void setup() override;
uint32_t lastRead = 0;
class RAK9154Sensor : public TelemetrySensor, VoltageSensor, CurrentSensor {
private:
protected:
virtual void setup() override;
uint32_t lastRead = 0;
public:
RAK9154Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual uint16_t getBusVoltageMv() override;
virtual int16_t getCurrentMa() override;
int getBusBatteryPercent();
bool isCharging();
void setLastRead(uint32_t lastRead);
public:
RAK9154Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual uint16_t getBusVoltageMv() override;
virtual int16_t getCurrentMa() override;
int getBusBatteryPercent();
bool isCharging();
void setLastRead(uint32_t lastRead);
};
#endif // _RAK9154SENSOR_H
#endif // HAS_RAKPROT

View File

@@ -8,70 +8,66 @@
RCWL9620Sensor::RCWL9620Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_RCWL9620, "RCWL9620") {}
bool RCWL9620Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
status = 1;
begin(bus, dev->address.address);
initI2CSensor();
return status;
bool RCWL9620Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
status = 1;
begin(bus, dev->address.address);
initI2CSensor();
return status;
}
bool RCWL9620Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_distance = true;
LOG_DEBUG("RCWL9620 getMetrics");
measurement->variant.environment_metrics.distance = getDistance();
return true;
bool RCWL9620Sensor::getMetrics(meshtastic_Telemetry *measurement) {
measurement->variant.environment_metrics.has_distance = true;
LOG_DEBUG("RCWL9620 getMetrics");
measurement->variant.environment_metrics.distance = getDistance();
return true;
}
void RCWL9620Sensor::begin(TwoWire *wire, uint8_t addr, uint8_t sda, uint8_t scl, uint32_t speed)
{
_wire = wire;
_addr = addr;
_sda = sda;
_scl = scl;
_speed = speed;
_wire->begin();
void RCWL9620Sensor::begin(TwoWire *wire, uint8_t addr, uint8_t sda, uint8_t scl, uint32_t speed) {
_wire = wire;
_addr = addr;
_sda = sda;
_scl = scl;
_speed = speed;
_wire->begin();
}
float RCWL9620Sensor::getDistance()
{
uint32_t data = 0;
uint8_t b1 = 0, b2 = 0, b3 = 0;
float RCWL9620Sensor::getDistance() {
uint32_t data = 0;
uint8_t b1 = 0, b2 = 0, b3 = 0;
LOG_DEBUG("[RCWL9620] Start measure command");
LOG_DEBUG("[RCWL9620] Start measure command");
_wire->beginTransmission(_addr);
_wire->write(0x01); // À tester aussi sans cette ligne si besoin
uint8_t result = _wire->endTransmission();
LOG_DEBUG("[RCWL9620] endTransmission result = %d", result);
delay(100); // délai pour laisser le capteur répondre
_wire->beginTransmission(_addr);
_wire->write(0x01); // À tester aussi sans cette ligne si besoin
uint8_t result = _wire->endTransmission();
LOG_DEBUG("[RCWL9620] endTransmission result = %d", result);
delay(100); // délai pour laisser le capteur répondre
LOG_DEBUG("[RCWL9620] Read i2c data:");
_wire->requestFrom(_addr, (uint8_t)3);
LOG_DEBUG("[RCWL9620] Read i2c data:");
_wire->requestFrom(_addr, (uint8_t)3);
if (_wire->available() < 3) {
LOG_DEBUG("[RCWL9620] less than 3 octets !");
return 0.0;
}
if (_wire->available() < 3) {
LOG_DEBUG("[RCWL9620] less than 3 octets !");
return 0.0;
}
b1 = _wire->read();
b2 = _wire->read();
b3 = _wire->read();
b1 = _wire->read();
b2 = _wire->read();
b3 = _wire->read();
data = ((uint32_t)b1 << 16) | ((uint32_t)b2 << 8) | b3;
data = ((uint32_t)b1 << 16) | ((uint32_t)b2 << 8) | b3;
float Distance = float(data) / 1000.0;
float Distance = float(data) / 1000.0;
LOG_DEBUG("[RCWL9620] Bytes readed = %02X %02X %02X", b1, b2, b3);
LOG_DEBUG("[RCWL9620] data=%.2f, level=%.2f", (double)data, (double)Distance);
LOG_DEBUG("[RCWL9620] Bytes readed = %02X %02X %02X", b1, b2, b3);
LOG_DEBUG("[RCWL9620] data=%.2f, level=%.2f", (double)data, (double)Distance);
if (Distance > 4500.00) {
return 4500.00;
} else {
return Distance;
}
if (Distance > 4500.00) {
return 4500.00;
} else {
return Distance;
}
}
#endif

View File

@@ -6,23 +6,22 @@
#include "TelemetrySensor.h"
#include <Wire.h>
class RCWL9620Sensor : public TelemetrySensor
{
private:
uint8_t _addr = 0x57;
TwoWire *_wire = &Wire;
uint8_t _scl = -1;
uint8_t _sda = -1;
uint32_t _speed = 200000UL;
class RCWL9620Sensor : public TelemetrySensor {
private:
uint8_t _addr = 0x57;
TwoWire *_wire = &Wire;
uint8_t _scl = -1;
uint8_t _sda = -1;
uint32_t _speed = 200000UL;
protected:
void begin(TwoWire *wire = &Wire, uint8_t addr = 0x57, uint8_t sda = -1, uint8_t scl = -1, uint32_t speed = 200000UL);
float getDistance();
protected:
void begin(TwoWire *wire = &Wire, uint8_t addr = 0x57, uint8_t sda = -1, uint8_t scl = -1, uint32_t speed = 200000UL);
float getDistance();
public:
RCWL9620Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
RCWL9620Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -9,23 +9,21 @@
SHT31Sensor::SHT31Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SHT31, "SHT31") {}
bool SHT31Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
sht31 = Adafruit_SHT31(bus);
status = sht31.begin(dev->address.address);
initI2CSensor();
return status;
bool SHT31Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
sht31 = Adafruit_SHT31(bus);
status = sht31.begin(dev->address.address);
initI2CSensor();
return status;
}
bool SHT31Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.environment_metrics.temperature = sht31.readTemperature();
measurement->variant.environment_metrics.relative_humidity = sht31.readHumidity();
bool SHT31Sensor::getMetrics(meshtastic_Telemetry *measurement) {
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_relative_humidity = true;
measurement->variant.environment_metrics.temperature = sht31.readTemperature();
measurement->variant.environment_metrics.relative_humidity = sht31.readHumidity();
return true;
return true;
}
#endif

View File

@@ -6,15 +6,14 @@
#include "TelemetrySensor.h"
#include <Adafruit_SHT31.h>
class SHT31Sensor : public TelemetrySensor
{
private:
Adafruit_SHT31 sht31;
class SHT31Sensor : public TelemetrySensor {
private:
Adafruit_SHT31 sht31;
public:
SHT31Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
SHT31Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -9,40 +9,38 @@
SHT4XSensor::SHT4XSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SHT4X, "SHT4X") {}
bool SHT4XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
bool SHT4XSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
uint32_t serialNumber = 0;
uint32_t serialNumber = 0;
status = sht4x.begin(bus);
if (!status) {
return status;
}
serialNumber = sht4x.readSerial();
if (serialNumber != 0) {
LOG_DEBUG("serialNumber : %x", serialNumber);
status = 1;
} else {
LOG_DEBUG("Error trying to execute readSerial(): ");
status = 0;
}
initI2CSensor();
status = sht4x.begin(bus);
if (!status) {
return status;
}
serialNumber = sht4x.readSerial();
if (serialNumber != 0) {
LOG_DEBUG("serialNumber : %x", serialNumber);
status = 1;
} else {
LOG_DEBUG("Error trying to execute readSerial(): ");
status = 0;
}
initI2CSensor();
return status;
}
bool SHT4XSensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_relative_humidity = true;
bool SHT4XSensor::getMetrics(meshtastic_Telemetry *measurement) {
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_relative_humidity = true;
sensors_event_t humidity, temp;
sht4x.getEvent(&humidity, &temp);
measurement->variant.environment_metrics.temperature = temp.temperature;
measurement->variant.environment_metrics.relative_humidity = humidity.relative_humidity;
return true;
sensors_event_t humidity, temp;
sht4x.getEvent(&humidity, &temp);
measurement->variant.environment_metrics.temperature = temp.temperature;
measurement->variant.environment_metrics.relative_humidity = humidity.relative_humidity;
return true;
}
#endif

View File

@@ -6,15 +6,14 @@
#include "TelemetrySensor.h"
#include <Adafruit_SHT4x.h>
class SHT4XSensor : public TelemetrySensor
{
private:
Adafruit_SHT4x sht4x = Adafruit_SHT4x();
class SHT4XSensor : public TelemetrySensor {
private:
Adafruit_SHT4x sht4x = Adafruit_SHT4x();
public:
SHT4XSensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
SHT4XSensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -9,27 +9,25 @@
SHTC3Sensor::SHTC3Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SHTC3, "SHTC3") {}
bool SHTC3Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
status = shtc3.begin(bus);
bool SHTC3Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
status = shtc3.begin(bus);
initI2CSensor();
return status;
initI2CSensor();
return status;
}
bool SHTC3Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_relative_humidity = true;
bool SHTC3Sensor::getMetrics(meshtastic_Telemetry *measurement) {
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_relative_humidity = true;
sensors_event_t humidity, temp;
shtc3.getEvent(&humidity, &temp);
sensors_event_t humidity, temp;
shtc3.getEvent(&humidity, &temp);
measurement->variant.environment_metrics.temperature = temp.temperature;
measurement->variant.environment_metrics.relative_humidity = humidity.relative_humidity;
measurement->variant.environment_metrics.temperature = temp.temperature;
measurement->variant.environment_metrics.relative_humidity = humidity.relative_humidity;
return true;
return true;
}
#endif

View File

@@ -6,15 +6,14 @@
#include "TelemetrySensor.h"
#include <Adafruit_SHTC3.h>
class SHTC3Sensor : public TelemetrySensor
{
private:
Adafruit_SHTC3 shtc3 = Adafruit_SHTC3();
class SHTC3Sensor : public TelemetrySensor {
private:
Adafruit_SHTC3 shtc3 = Adafruit_SHTC3();
public:
SHTC3Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
SHTC3Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -17,95 +17,89 @@
// ntc res table
uint32_t ntc_res2[136] = {
113347, 107565, 102116, 96978, 92132, 87559, 83242, 79166, 75316, 71677, 68237, 64991, 61919, 59011, 56258, 53650, 51178,
48835, 46613, 44506, 42506, 40600, 38791, 37073, 35442, 33892, 32420, 31020, 29689, 28423, 27219, 26076, 24988, 23951,
22963, 22021, 21123, 20267, 19450, 18670, 17926, 17214, 16534, 15886, 15266, 14674, 14108, 13566, 13049, 12554, 12081,
11628, 11195, 10780, 10382, 10000, 9634, 9284, 8947, 8624, 8315, 8018, 7734, 7461, 7199, 6948, 6707, 6475,
6253, 6039, 5834, 5636, 5445, 5262, 5086, 4917, 4754, 4597, 4446, 4301, 4161, 4026, 3896, 3771, 3651,
3535, 3423, 3315, 3211, 3111, 3014, 2922, 2834, 2748, 2666, 2586, 2509, 2435, 2364, 2294, 2228, 2163,
2100, 2040, 1981, 1925, 1870, 1817, 1766, 1716, 1669, 1622, 1578, 1535, 1493, 1452, 1413, 1375, 1338,
1303, 1268, 1234, 1202, 1170, 1139, 1110, 1081, 1053, 1026, 999, 974, 949, 925, 902, 880, 858,
113347, 107565, 102116, 96978, 92132, 87559, 83242, 79166, 75316, 71677, 68237, 64991, 61919, 59011, 56258, 53650, 51178, 48835, 46613, 44506,
42506, 40600, 38791, 37073, 35442, 33892, 32420, 31020, 29689, 28423, 27219, 26076, 24988, 23951, 22963, 22021, 21123, 20267, 19450, 18670,
17926, 17214, 16534, 15886, 15266, 14674, 14108, 13566, 13049, 12554, 12081, 11628, 11195, 10780, 10382, 10000, 9634, 9284, 8947, 8624,
8315, 8018, 7734, 7461, 7199, 6948, 6707, 6475, 6253, 6039, 5834, 5636, 5445, 5262, 5086, 4917, 4754, 4597, 4446, 4301,
4161, 4026, 3896, 3771, 3651, 3535, 3423, 3315, 3211, 3111, 3014, 2922, 2834, 2748, 2666, 2586, 2509, 2435, 2364, 2294,
2228, 2163, 2100, 2040, 1981, 1925, 1870, 1817, 1766, 1716, 1669, 1622, 1578, 1535, 1493, 1452, 1413, 1375, 1338, 1303,
1268, 1234, 1202, 1170, 1139, 1110, 1081, 1053, 1026, 999, 974, 949, 925, 902, 880, 858,
};
int8_t ntc_temp2[136] = {
-30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8,
-7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
-30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3,
-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
};
T1000xSensor::T1000xSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SENSOR_UNSET, "T1000x") {}
bool T1000xSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
return true;
bool T1000xSensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
return true;
}
float T1000xSensor::getLux()
{
uint32_t lux_vot = 0;
float lux_level = 0;
float T1000xSensor::getLux() {
uint32_t lux_vot = 0;
float lux_level = 0;
for (uint32_t i = 0; i < T1000X_SENSE_SAMPLES; i++) {
lux_vot += analogRead(T1000X_LUX_PIN);
}
lux_vot = lux_vot / T1000X_SENSE_SAMPLES;
lux_vot = ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * lux_vot;
for (uint32_t i = 0; i < T1000X_SENSE_SAMPLES; i++) {
lux_vot += analogRead(T1000X_LUX_PIN);
}
lux_vot = lux_vot / T1000X_SENSE_SAMPLES;
lux_vot = ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * lux_vot;
if (lux_vot <= 80)
lux_level = 0;
else if (lux_vot >= 2480)
lux_level = 100;
else
lux_level = 100 * (lux_vot - 80) / T1000X_LIGHT_REF_VCC;
if (lux_vot <= 80)
lux_level = 0;
else if (lux_vot >= 2480)
lux_level = 100;
else
lux_level = 100 * (lux_vot - 80) / T1000X_LIGHT_REF_VCC;
return lux_level;
return lux_level;
}
float T1000xSensor::getTemp()
{
uint32_t vcc_vot = 0, ntc_vot = 0;
float T1000xSensor::getTemp() {
uint32_t vcc_vot = 0, ntc_vot = 0;
uint8_t u8i = 0;
float Vout = 0, Rt = 0, temp = 0;
float Temp = 0;
uint8_t u8i = 0;
float Vout = 0, Rt = 0, temp = 0;
float Temp = 0;
for (uint32_t i = 0; i < T1000X_SENSE_SAMPLES; i++) {
vcc_vot += analogRead(T1000X_VCC_PIN);
for (uint32_t i = 0; i < T1000X_SENSE_SAMPLES; i++) {
vcc_vot += analogRead(T1000X_VCC_PIN);
}
vcc_vot = vcc_vot / T1000X_SENSE_SAMPLES;
vcc_vot = 2 * ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * vcc_vot;
for (uint32_t i = 0; i < T1000X_SENSE_SAMPLES; i++) {
ntc_vot += analogRead(T1000X_NTC_PIN);
}
ntc_vot = ntc_vot / T1000X_SENSE_SAMPLES;
ntc_vot = ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * ntc_vot;
Vout = ntc_vot;
Rt = (HEATER_NTC_RP * vcc_vot) / Vout - HEATER_NTC_RP;
for (u8i = 0; u8i < 135; u8i++) {
if (Rt >= ntc_res2[u8i]) {
break;
}
vcc_vot = vcc_vot / T1000X_SENSE_SAMPLES;
vcc_vot = 2 * ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * vcc_vot;
}
temp = ntc_temp2[u8i - 1] + 1 * (ntc_res2[u8i - 1] - Rt) / (float)(ntc_res2[u8i - 1] - ntc_res2[u8i]);
Temp = (temp * 100 + 5) / 100; // half adjust
for (uint32_t i = 0; i < T1000X_SENSE_SAMPLES; i++) {
ntc_vot += analogRead(T1000X_NTC_PIN);
}
ntc_vot = ntc_vot / T1000X_SENSE_SAMPLES;
ntc_vot = ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * ntc_vot;
Vout = ntc_vot;
Rt = (HEATER_NTC_RP * vcc_vot) / Vout - HEATER_NTC_RP;
for (u8i = 0; u8i < 135; u8i++) {
if (Rt >= ntc_res2[u8i]) {
break;
}
}
temp = ntc_temp2[u8i - 1] + 1 * (ntc_res2[u8i - 1] - Rt) / (float)(ntc_res2[u8i - 1] - ntc_res2[u8i]);
Temp = (temp * 100 + 5) / 100; // half adjust
return Temp;
return Temp;
}
bool T1000xSensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_lux = true;
bool T1000xSensor::getMetrics(meshtastic_Telemetry *measurement) {
measurement->variant.environment_metrics.has_temperature = true;
measurement->variant.environment_metrics.has_lux = true;
measurement->variant.environment_metrics.temperature = getTemp();
measurement->variant.environment_metrics.lux = getLux();
return true;
measurement->variant.environment_metrics.temperature = getTemp();
measurement->variant.environment_metrics.lux = getLux();
return true;
}
#endif

View File

@@ -5,14 +5,13 @@
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
class T1000xSensor : public TelemetrySensor
{
public:
T1000xSensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
virtual float getLux();
virtual float getTemp();
class T1000xSensor : public TelemetrySensor {
public:
T1000xSensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
virtual float getLux();
virtual float getTemp();
};
#endif

View File

@@ -9,30 +9,28 @@
TSL2561Sensor::TSL2561Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_TSL2561, "TSL2561") {}
bool TSL2561Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
bool TSL2561Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
status = tsl.begin(bus);
if (!status) {
return status;
}
tsl.setGain(TSL2561_GAIN_1X);
tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_101MS);
initI2CSensor();
status = tsl.begin(bus);
if (!status) {
return status;
}
tsl.setGain(TSL2561_GAIN_1X);
tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_101MS);
initI2CSensor();
return status;
}
bool TSL2561Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_lux = true;
sensors_event_t event;
tsl.getEvent(&event);
measurement->variant.environment_metrics.lux = event.light;
LOG_INFO("Lux: %f", measurement->variant.environment_metrics.lux);
bool TSL2561Sensor::getMetrics(meshtastic_Telemetry *measurement) {
measurement->variant.environment_metrics.has_lux = true;
sensors_event_t event;
tsl.getEvent(&event);
measurement->variant.environment_metrics.lux = event.light;
LOG_INFO("Lux: %f", measurement->variant.environment_metrics.lux);
return true;
return true;
}
#endif

View File

@@ -6,15 +6,14 @@
#include "TelemetrySensor.h"
#include <Adafruit_TSL2561_U.h>
class TSL2561Sensor : public TelemetrySensor
{
private:
// The magic number is a sensor id, the actual value doesn't matter
Adafruit_TSL2561_Unified tsl = Adafruit_TSL2561_Unified(TSL2561_ADDR_LOW, 12345);
class TSL2561Sensor : public TelemetrySensor {
private:
// The magic number is a sensor id, the actual value doesn't matter
Adafruit_TSL2561_Unified tsl = Adafruit_TSL2561_Unified(TSL2561_ADDR_LOW, 12345);
public:
TSL2561Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
TSL2561Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -10,32 +10,30 @@
TSL2591Sensor::TSL2591Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_TSL25911FN, "TSL2591") {}
bool TSL2591Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
status = tsl.begin(bus);
if (!status) {
return status;
}
tsl.setGain(TSL2591_GAIN_LOW); // 1x gain
tsl.setTiming(TSL2591_INTEGRATIONTIME_100MS);
initI2CSensor();
bool TSL2591Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
status = tsl.begin(bus);
if (!status) {
return status;
}
tsl.setGain(TSL2591_GAIN_LOW); // 1x gain
tsl.setTiming(TSL2591_INTEGRATIONTIME_100MS);
initI2CSensor();
return status;
}
bool TSL2591Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_lux = true;
uint32_t lum = tsl.getFullLuminosity();
uint16_t ir, full;
ir = lum >> 16;
full = lum & 0xFFFF;
bool TSL2591Sensor::getMetrics(meshtastic_Telemetry *measurement) {
measurement->variant.environment_metrics.has_lux = true;
uint32_t lum = tsl.getFullLuminosity();
uint16_t ir, full;
ir = lum >> 16;
full = lum & 0xFFFF;
measurement->variant.environment_metrics.lux = tsl.calculateLux(full, ir);
LOG_INFO("Lux: %f", measurement->variant.environment_metrics.lux);
measurement->variant.environment_metrics.lux = tsl.calculateLux(full, ir);
LOG_INFO("Lux: %f", measurement->variant.environment_metrics.lux);
return true;
return true;
}
#endif

View File

@@ -6,14 +6,13 @@
#include "TelemetrySensor.h"
#include <Adafruit_TSL2591.h>
class TSL2591Sensor : public TelemetrySensor
{
private:
Adafruit_TSL2591 tsl;
class TSL2591Sensor : public TelemetrySensor {
private:
Adafruit_TSL2591 tsl;
public:
TSL2591Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
TSL2591Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -16,59 +16,55 @@ class TwoWire;
#define DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000
extern std::pair<uint8_t, TwoWire *> nodeTelemetrySensorsMap[_meshtastic_TelemetrySensorType_MAX + 1];
class TelemetrySensor
{
protected:
TelemetrySensor(meshtastic_TelemetrySensorType sensorType, const char *sensorName)
{
this->sensorName = sensorName;
this->sensorType = sensorType;
this->status = 0;
class TelemetrySensor {
protected:
TelemetrySensor(meshtastic_TelemetrySensorType sensorType, const char *sensorName) {
this->sensorName = sensorName;
this->sensorType = sensorType;
this->status = 0;
}
const char *sensorName;
meshtastic_TelemetrySensorType sensorType = meshtastic_TelemetrySensorType_SENSOR_UNSET;
unsigned status;
bool initialized = false;
int32_t initI2CSensor() {
if (!status) {
LOG_WARN("Can't connect to detected %s sensor. Remove from nodeTelemetrySensorsMap", sensorName);
nodeTelemetrySensorsMap[sensorType].first = 0;
} else {
LOG_INFO("Opened %s sensor on i2c bus", sensorName);
setup();
}
initialized = true;
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
const char *sensorName;
meshtastic_TelemetrySensorType sensorType = meshtastic_TelemetrySensorType_SENSOR_UNSET;
unsigned status;
bool initialized = false;
// TODO: check is setup used at all?
virtual void setup() {}
int32_t initI2CSensor()
{
if (!status) {
LOG_WARN("Can't connect to detected %s sensor. Remove from nodeTelemetrySensorsMap", sensorName);
nodeTelemetrySensorsMap[sensorType].first = 0;
} else {
LOG_INFO("Opened %s sensor on i2c bus", sensorName);
setup();
}
initialized = true;
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
public:
virtual ~TelemetrySensor() {}
// TODO: check is setup used at all?
virtual void setup() {}
virtual AdminMessageHandleResult handleAdminMessage(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request,
meshtastic_AdminMessage *response) {
return AdminMessageHandleResult::NOT_HANDLED;
}
public:
virtual ~TelemetrySensor() {}
virtual AdminMessageHandleResult handleAdminMessage(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request,
meshtastic_AdminMessage *response)
{
return AdminMessageHandleResult::NOT_HANDLED;
}
// TODO: delete after migration
bool hasSensor() { return nodeTelemetrySensorsMap[sensorType].first > 0; }
// TODO: delete after migration
bool hasSensor() { return nodeTelemetrySensorsMap[sensorType].first > 0; }
#if WIRE_INTERFACES_COUNT > 1
// Set to true if Implementation only works first I2C port (Wire)
virtual bool onlyWire1() { return false; }
// Set to true if Implementation only works first I2C port (Wire)
virtual bool onlyWire1() { return false; }
#endif
virtual int32_t runOnce() { return INT32_MAX; }
virtual bool isInitialized() { return initialized; }
virtual bool isRunning() { return status > 0; }
virtual int32_t runOnce() { return INT32_MAX; }
virtual bool isInitialized() { return initialized; }
virtual bool isRunning() { return status > 0; }
virtual bool getMetrics(meshtastic_Telemetry *measurement) = 0;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { return false; };
virtual bool getMetrics(meshtastic_Telemetry *measurement) = 0;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) { return false; };
};
#endif

View File

@@ -11,20 +11,19 @@
VEML7700Sensor::VEML7700Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_VEML7700, "VEML7700") {}
bool VEML7700Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
{
LOG_INFO("Init sensor: %s", sensorName);
status = veml7700.begin(bus);
if (!status) {
return status;
}
veml7700.setLowThreshold(10000);
veml7700.setHighThreshold(20000);
veml7700.interruptEnable(true);
initI2CSensor();
bool VEML7700Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) {
LOG_INFO("Init sensor: %s", sensorName);
status = veml7700.begin(bus);
if (!status) {
return status;
}
veml7700.setLowThreshold(10000);
veml7700.setHighThreshold(20000);
veml7700.interruptEnable(true);
initI2CSensor();
return status;
}
/*!
@@ -33,35 +32,29 @@ bool VEML7700Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
* @param corrected if true, apply non-linear correction
* @return lux value
*/
float VEML7700Sensor::computeLux(uint16_t rawALS, bool corrected)
{
float lux = getResolution() * rawALS;
if (corrected)
lux = (((6.0135e-13 * lux - 9.3924e-9) * lux + 8.1488e-5) * lux + 1.0023) * lux;
return lux;
float VEML7700Sensor::computeLux(uint16_t rawALS, bool corrected) {
float lux = getResolution() * rawALS;
if (corrected)
lux = (((6.0135e-13 * lux - 9.3924e-9) * lux + 8.1488e-5) * lux + 1.0023) * lux;
return lux;
}
/*!
* @brief Determines resolution for current gain and integration time
* settings.
*/
float VEML7700Sensor::getResolution(void)
{
return MAX_RES * (IT_MAX / veml7700.getIntegrationTimeValue()) * (GAIN_MAX / veml7700.getGainValue());
}
float VEML7700Sensor::getResolution(void) { return MAX_RES * (IT_MAX / veml7700.getIntegrationTimeValue()) * (GAIN_MAX / veml7700.getGainValue()); }
bool VEML7700Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_lux = true;
measurement->variant.environment_metrics.has_white_lux = true;
bool VEML7700Sensor::getMetrics(meshtastic_Telemetry *measurement) {
measurement->variant.environment_metrics.has_lux = true;
measurement->variant.environment_metrics.has_white_lux = true;
int16_t white;
measurement->variant.environment_metrics.lux = veml7700.readLux(VEML_LUX_AUTO);
white = veml7700.readWhite(true);
measurement->variant.environment_metrics.white_lux = computeLux(white, white > 100);
LOG_INFO("white lux %f, als lux %f", measurement->variant.environment_metrics.white_lux,
measurement->variant.environment_metrics.lux);
int16_t white;
measurement->variant.environment_metrics.lux = veml7700.readLux(VEML_LUX_AUTO);
white = veml7700.readWhite(true);
measurement->variant.environment_metrics.white_lux = computeLux(white, white > 100);
LOG_INFO("white lux %f, als lux %f", measurement->variant.environment_metrics.white_lux, measurement->variant.environment_metrics.lux);
return true;
return true;
}
#endif

View File

@@ -6,19 +6,18 @@
#include "TelemetrySensor.h"
#include <Adafruit_VEML7700.h>
class VEML7700Sensor : public TelemetrySensor
{
private:
const float MAX_RES = 0.0036;
const float GAIN_MAX = 2;
const float IT_MAX = 800;
Adafruit_VEML7700 veml7700;
float computeLux(uint16_t rawALS, bool corrected);
float getResolution(void);
class VEML7700Sensor : public TelemetrySensor {
private:
const float MAX_RES = 0.0036;
const float GAIN_MAX = 2;
const float IT_MAX = 800;
Adafruit_VEML7700 veml7700;
float computeLux(uint16_t rawALS, bool corrected);
float getResolution(void);
public:
VEML7700Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
public:
VEML7700Sensor();
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
};
#endif

View File

@@ -4,10 +4,9 @@
#pragma once
class VoltageSensor
{
public:
virtual uint16_t getBusVoltageMv() = 0;
class VoltageSensor {
public:
virtual uint16_t getBusVoltageMv() = 0;
};
#endif

View File

@@ -9,26 +9,14 @@
NullSensor::NullSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SENSOR_UNSET, "nullSensor") {}
int32_t NullSensor::runOnce()
{
return INT32_MAX;
}
int32_t NullSensor::runOnce() { return INT32_MAX; }
void NullSensor::setup() {}
bool NullSensor::getMetrics(meshtastic_Telemetry *measurement)
{
return false;
}
bool NullSensor::getMetrics(meshtastic_Telemetry *measurement) { return false; }
uint16_t NullSensor::getBusVoltageMv()
{
return 0;
}
uint16_t NullSensor::getBusVoltageMv() { return 0; }
int16_t NullSensor::getCurrentMa()
{
return 0;
}
int16_t NullSensor::getCurrentMa() { return 0; }
#endif

View File

@@ -9,20 +9,19 @@
#include "TelemetrySensor.h"
#include "VoltageSensor.h"
class NullSensor : public TelemetrySensor, VoltageSensor, CurrentSensor
{
class NullSensor : public TelemetrySensor, VoltageSensor, CurrentSensor {
protected:
virtual void setup() override;
protected:
virtual void setup() override;
public:
NullSensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
int32_t runTrigger() { return 0; }
public:
NullSensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
int32_t runTrigger() { return 0; }
virtual uint16_t getBusVoltageMv() override;
virtual int16_t getCurrentMa() override;
virtual uint16_t getBusVoltageMv() override;
virtual int16_t getCurrentMa() override;
};
#endif

View File

@@ -1,21 +1,9 @@
#include "UnitConversions.h"
float UnitConversions::CelsiusToFahrenheit(float celsius)
{
return (celsius * 9) / 5 + 32;
}
float UnitConversions::CelsiusToFahrenheit(float celsius) { return (celsius * 9) / 5 + 32; }
float UnitConversions::MetersPerSecondToKnots(float metersPerSecond)
{
return metersPerSecond * 1.94384;
}
float UnitConversions::MetersPerSecondToKnots(float metersPerSecond) { return metersPerSecond * 1.94384; }
float UnitConversions::MetersPerSecondToMilesPerHour(float metersPerSecond)
{
return metersPerSecond * 2.23694;
}
float UnitConversions::MetersPerSecondToMilesPerHour(float metersPerSecond) { return metersPerSecond * 2.23694; }
float UnitConversions::HectoPascalToInchesOfMercury(float hectoPascal)
{
return hectoPascal * 0.029529983071445;
}
float UnitConversions::HectoPascalToInchesOfMercury(float hectoPascal) { return hectoPascal * 0.029529983071445; }

View File

@@ -1,10 +1,9 @@
#pragma once
class UnitConversions
{
public:
static float CelsiusToFahrenheit(float celsius);
static float MetersPerSecondToKnots(float metersPerSecond);
static float MetersPerSecondToMilesPerHour(float metersPerSecond);
static float HectoPascalToInchesOfMercury(float hectoPascal);
class UnitConversions {
public:
static float CelsiusToFahrenheit(float celsius);
static float MetersPerSecondToKnots(float metersPerSecond);
static float MetersPerSecondToMilesPerHour(float metersPerSecond);
static float HectoPascalToInchesOfMercury(float hectoPascal);
};