mirror of
https://github.com/meshtastic/firmware.git
synced 2025-09-06 03:29:17 +00:00
Compare commits
33 Commits
988d61d4ad
...
3731e867d3
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3731e867d3 | ||
![]() |
0bd4cefad3 | ||
![]() |
9b1fb795d7 | ||
![]() |
3040e5a7bb | ||
![]() |
3b82d55176 | ||
![]() |
a6b8202cd4 | ||
![]() |
cfc1bf10c9 | ||
![]() |
7d1300ab66 | ||
![]() |
f0775c586f | ||
![]() |
80175b1e17 | ||
![]() |
1eeadde989 | ||
![]() |
cfc355b29a | ||
![]() |
ef203b5dca | ||
![]() |
58d916da39 | ||
![]() |
02a84142df | ||
![]() |
726cf8611b | ||
![]() |
bac816d80a | ||
![]() |
7fe9efe805 | ||
![]() |
ad7647ef95 | ||
![]() |
3cef1e89ed | ||
![]() |
a1cbe8ebb8 | ||
![]() |
16e2540f0f | ||
![]() |
e34aadabe1 | ||
![]() |
81f9b38c11 | ||
![]() |
8faf4665b0 | ||
![]() |
318da220dc | ||
![]() |
a28e83c35a | ||
![]() |
8a4427a69f | ||
![]() |
c9233fef1c | ||
![]() |
f599984990 | ||
![]() |
e73ff62b22 | ||
![]() |
30532d4c6a | ||
![]() |
8fa513ff1f |
@ -9,7 +9,7 @@ plugins:
|
||||
lint:
|
||||
enabled:
|
||||
- checkov@3.2.467
|
||||
- renovate@41.90.1
|
||||
- renovate@41.91.3
|
||||
- prettier@3.6.2
|
||||
- trufflehog@3.90.5
|
||||
- yamllint@1.37.1
|
||||
|
@ -5,7 +5,7 @@
|
||||
},
|
||||
"core": "stm32",
|
||||
"cpu": "cortex-m4",
|
||||
"extra_flags": "-DSTM32WLxx -DSTM32WLE5xx -DARDUINO_GENERIC_WLE5CCUX",
|
||||
"extra_flags": "-DSTM32WLxx -DSTM32WLE5xx -DARDUINO_RAK3172_MODULE",
|
||||
"f_cpu": "48000000L",
|
||||
"mcu": "stm32wle5ccu",
|
||||
"variant": "STM32WLxx/WL54CCU_WL55CCU_WLE4C(8-B-C)U_WLE5C(8-B-C)U",
|
||||
|
@ -1,7 +1,14 @@
|
||||
#include "DisplayFormatters.h"
|
||||
|
||||
const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName)
|
||||
const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName,
|
||||
bool usePreset)
|
||||
{
|
||||
|
||||
// If use_preset is false, always return "Custom"
|
||||
if (!usePreset) {
|
||||
return "Custom";
|
||||
}
|
||||
|
||||
switch (preset) {
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO:
|
||||
return useShortName ? "ShortT" : "ShortTurbo";
|
||||
|
@ -4,5 +4,6 @@
|
||||
class DisplayFormatters
|
||||
{
|
||||
public:
|
||||
static const char *getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName);
|
||||
static const char *getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName,
|
||||
bool usePreset);
|
||||
};
|
||||
|
@ -843,9 +843,6 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
|
||||
setPowerPMU(true); // Power (PMU): on
|
||||
writePinStandby(false); // Standby (pin): awake (not standby)
|
||||
setPowerUBLOX(true); // Standby (UBLOX): awake
|
||||
#ifdef GNSS_AIROHA
|
||||
lastFixStartMsec = 0;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case GPS_SOFTSLEEP:
|
||||
@ -863,9 +860,7 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
|
||||
writePinStandby(true); // Standby (pin): asleep (not awake)
|
||||
setPowerUBLOX(false, sleepTime); // Standby (UBLOX): asleep, timed
|
||||
#ifdef GNSS_AIROHA
|
||||
if (config.position.gps_update_interval * 1000 >= GPS_FIX_HOLD_TIME * 2) {
|
||||
digitalWrite(PIN_GPS_EN, LOW);
|
||||
}
|
||||
digitalWrite(PIN_GPS_EN, LOW);
|
||||
#endif
|
||||
break;
|
||||
|
||||
@ -877,9 +872,7 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
|
||||
writePinStandby(true); // Standby (pin): asleep
|
||||
setPowerUBLOX(false, 0); // Standby (UBLOX): asleep, indefinitely
|
||||
#ifdef GNSS_AIROHA
|
||||
if (config.position.gps_update_interval * 1000 >= GPS_FIX_HOLD_TIME * 2) {
|
||||
digitalWrite(PIN_GPS_EN, LOW);
|
||||
}
|
||||
digitalWrite(PIN_GPS_EN, LOW);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@ -1062,6 +1055,8 @@ void GPS::down()
|
||||
}
|
||||
// If update interval long enough (or softsleep unsupported): hardsleep instead
|
||||
setPowerState(GPS_HARDSLEEP, sleepTime);
|
||||
// Reset the fix quality to 0, since we're off.
|
||||
fixQual = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1121,11 +1116,19 @@ int32_t GPS::runOnce()
|
||||
shouldPublish = true;
|
||||
}
|
||||
|
||||
uint8_t prev_fixQual = fixQual;
|
||||
bool gotLoc = lookForLocation();
|
||||
if (gotLoc && !hasValidLocation) { // declare that we have location ASAP
|
||||
LOG_DEBUG("hasValidLocation RISING EDGE");
|
||||
hasValidLocation = true;
|
||||
shouldPublish = true;
|
||||
// Hold for 20secs after getting a lock to download ephemeris etc
|
||||
fixHoldEnds = millis() + 20000;
|
||||
}
|
||||
|
||||
if (gotLoc && prev_fixQual == 0) { // just got a lock after turning back on.
|
||||
fixHoldEnds = millis() + 20000;
|
||||
shouldPublish = true; // Publish immediately, since next publish is at end of hold
|
||||
}
|
||||
|
||||
bool tooLong = scheduling.searchedTooLong();
|
||||
@ -1134,8 +1137,7 @@ int32_t GPS::runOnce()
|
||||
|
||||
// Once we get a location we no longer desperately want an update
|
||||
if ((gotLoc && gotTime) || tooLong) {
|
||||
|
||||
if (tooLong) {
|
||||
if (tooLong && !gotLoc) {
|
||||
// we didn't get a location during this ack window, therefore declare loss of lock
|
||||
if (hasValidLocation) {
|
||||
LOG_DEBUG("hasValidLocation FALLING EDGE");
|
||||
@ -1143,9 +1145,15 @@ int32_t GPS::runOnce()
|
||||
p = meshtastic_Position_init_default;
|
||||
hasValidLocation = false;
|
||||
}
|
||||
|
||||
down();
|
||||
shouldPublish = true; // publish our update for this just finished acquisition window
|
||||
if (millis() > fixHoldEnds) {
|
||||
shouldPublish = true; // publish our update at the end of the lock hold
|
||||
publishUpdate();
|
||||
down();
|
||||
#ifdef GPS_DEBUG
|
||||
} else {
|
||||
LOG_DEBUG("Holding for GPS data download: %d ms (numSats=%d)", fixHoldEnds - millis(), p.sats_in_view);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// If state has changed do a publish
|
||||
@ -1508,24 +1516,6 @@ static int32_t toDegInt(RawDegrees d)
|
||||
*/
|
||||
bool GPS::lookForTime()
|
||||
{
|
||||
|
||||
#ifdef GNSS_AIROHA
|
||||
uint8_t fix = reader.fixQuality();
|
||||
if (fix >= 1 && fix <= 5) {
|
||||
if (lastFixStartMsec > 0) {
|
||||
if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) {
|
||||
return false;
|
||||
} else {
|
||||
clearBuffer();
|
||||
}
|
||||
} else {
|
||||
lastFixStartMsec = millis();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
auto ti = reader.time;
|
||||
auto d = reader.date;
|
||||
if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed
|
||||
@ -1564,25 +1554,6 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
|
||||
*/
|
||||
bool GPS::lookForLocation()
|
||||
{
|
||||
#ifdef GNSS_AIROHA
|
||||
if ((config.position.gps_update_interval * 1000) >= (GPS_FIX_HOLD_TIME * 2)) {
|
||||
uint8_t fix = reader.fixQuality();
|
||||
if (fix >= 1 && fix <= 5) {
|
||||
if (lastFixStartMsec > 0) {
|
||||
if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) {
|
||||
return false;
|
||||
} else {
|
||||
clearBuffer();
|
||||
}
|
||||
} else {
|
||||
lastFixStartMsec = millis();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// By default, TinyGPS++ does not parse GPGSA lines, which give us
|
||||
// the 2D/3D fixType (see NMEAGPS.h)
|
||||
// At a minimum, use the fixQuality indicator in GPGGA (FIXME?)
|
||||
|
@ -159,7 +159,7 @@ class GPS : private concurrency::OSThread
|
||||
uint8_t fixType = 0; // fix type from GPGSA
|
||||
#endif
|
||||
|
||||
uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastFixStartMsec = 0;
|
||||
uint32_t fixHoldEnds = 0;
|
||||
uint32_t rx_gpio = 0;
|
||||
uint32_t tx_gpio = 0;
|
||||
|
||||
|
@ -132,6 +132,10 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
|
||||
if (tv->tv_sec < BUILD_EPOCH) {
|
||||
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
||||
return RTCSetResultInvalidTime;
|
||||
} else if (tv->tv_sec > (BUILD_EPOCH + FORTY_YEARS)) {
|
||||
LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch, BUILD_EPOCH,
|
||||
BUILD_EPOCH + FORTY_YEARS);
|
||||
return RTCSetResultInvalidTime;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -250,6 +254,10 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
|
||||
if (tv.tv_sec < BUILD_EPOCH) {
|
||||
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
|
||||
return RTCSetResultInvalidTime;
|
||||
} else if (tv.tv_sec > (BUILD_EPOCH + FORTY_YEARS)) {
|
||||
LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch, BUILD_EPOCH,
|
||||
BUILD_EPOCH + FORTY_YEARS);
|
||||
return RTCSetResultInvalidTime;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -55,3 +55,6 @@ time_t gm_mktime(struct tm *tm);
|
||||
#define SEC_PER_DAY 86400
|
||||
#define SEC_PER_HOUR 3600
|
||||
#define SEC_PER_MIN 60
|
||||
#ifdef BUILD_EPOCH
|
||||
#define FORTY_YEARS (40UL * 365 * SEC_PER_DAY) // probably time to update your firmware
|
||||
#endif
|
||||
|
@ -263,12 +263,6 @@ void drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
|
||||
display->drawString(x + 1, y, "USB");
|
||||
}
|
||||
|
||||
// auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, true);
|
||||
|
||||
// display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode), y, mode);
|
||||
// if (config.display.heading_bold)
|
||||
// display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode) - 1, y, mode);
|
||||
|
||||
uint32_t currentMillis = millis();
|
||||
uint32_t seconds = currentMillis / 1000;
|
||||
uint32_t minutes = seconds / 60;
|
||||
@ -398,7 +392,7 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
|
||||
display->drawString(nameX, getTextPositions(display)[line++], shortnameble);
|
||||
|
||||
// === Second Row: Radio Preset ===
|
||||
auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false);
|
||||
auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false, config.lora.use_preset);
|
||||
char regionradiopreset[25];
|
||||
const char *region = myRegion ? myRegion->name : NULL;
|
||||
if (region != nullptr) {
|
||||
|
@ -1525,7 +1525,7 @@ extern meshtastic_DeviceMetadata getDeviceMetadata()
|
||||
#if ((!HAS_SCREEN || NO_EXT_GPIO) || MESHTASTIC_EXCLUDE_CANNEDMESSAGES) && !defined(MESHTASTIC_INCLUDE_NICHE_GRAPHICS)
|
||||
deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_CANNEDMSG_CONFIG;
|
||||
#endif
|
||||
#if NO_EXT_GPIO
|
||||
#if NO_EXT_GPIO || MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION
|
||||
deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_EXTNOTIF_CONFIG;
|
||||
#endif
|
||||
// Only edge case here is if we apply this a device with built in Accelerometer and want to detect interrupts
|
||||
|
@ -368,7 +368,7 @@ const char *Channels::getName(size_t chIndex)
|
||||
// Per mesh.proto spec, if bandwidth is specified we must ignore modemPreset enum, we assume that in that case
|
||||
// the app effed up and forgot to set channelSettings.name
|
||||
if (config.lora.use_preset) {
|
||||
channelName = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false);
|
||||
channelName = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false, config.lora.use_preset);
|
||||
} else {
|
||||
channelName = "Custom";
|
||||
}
|
||||
@ -382,7 +382,8 @@ bool Channels::isDefaultChannel(ChannelIndex chIndex)
|
||||
const auto &ch = getByIndex(chIndex);
|
||||
if (ch.settings.psk.size == 1 && ch.settings.psk.bytes[0] == 1) {
|
||||
const char *name = getName(chIndex);
|
||||
const char *presetName = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false);
|
||||
const char *presetName =
|
||||
DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false, config.lora.use_preset);
|
||||
// Check if the name is the default derived from the modem preset
|
||||
if (strcmp(name, presetName) == 0)
|
||||
return true;
|
||||
|
@ -586,7 +586,8 @@ void RadioInterface::applyModemConfig()
|
||||
|
||||
// Check if we use the default frequency slot
|
||||
RadioInterface::uses_default_frequency_slot =
|
||||
channel_num == hash(DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false)) % numChannels;
|
||||
channel_num ==
|
||||
hash(DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false, config.lora.use_preset)) % numChannels;
|
||||
|
||||
// Old frequency selection formula
|
||||
// float freq = myRegion->freqStart + ((((myRegion->freqEnd - myRegion->freqStart) / numChannels) / 2) * channel_num);
|
||||
|
@ -364,9 +364,10 @@ ExternalNotificationModule::ExternalNotificationModule()
|
||||
// moduleConfig.external_notification.alert_message_buzzer = true;
|
||||
|
||||
if (moduleConfig.external_notification.enabled) {
|
||||
#if !defined(MESHTASTIC_EXCLUDE_INPUTBROKER)
|
||||
if (inputBroker) // put our callback in the inputObserver list
|
||||
inputObserver.observe(inputBroker);
|
||||
|
||||
#endif
|
||||
if (nodeDB->loadProto(rtttlConfigFile, meshtastic_RTTTLConfig_size, sizeof(meshtastic_RTTTLConfig),
|
||||
&meshtastic_RTTTLConfig_msg, &rtttlConfig) != LoadFileResult::LOAD_SUCCESS) {
|
||||
memset(rtttlConfig.ringtone, 0, sizeof(rtttlConfig.ringtone));
|
||||
|
@ -88,7 +88,7 @@
|
||||
#include "modules/StoreForwardModule.h"
|
||||
#endif
|
||||
#endif
|
||||
#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040) || defined(ARCH_PORTDUINO)
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION
|
||||
#include "modules/ExternalNotificationModule.h"
|
||||
#endif
|
||||
@ -98,7 +98,6 @@
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !MESHTASTIC_EXCLUDE_SERIAL
|
||||
#include "modules/SerialModule.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_DROPZONE
|
||||
#include "modules/DropzoneModule.h"
|
||||
@ -246,8 +245,8 @@ void setupModules()
|
||||
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_POWER_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||
new PowerTelemetryModule();
|
||||
#endif
|
||||
#if (defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040)) && !defined(CONFIG_IDF_TARGET_ESP32S2) && \
|
||||
!defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#if (defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040) || defined(ARCH_STM32WL)) && \
|
||||
!defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#if !MESHTASTIC_EXCLUDE_SERIAL
|
||||
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
||||
new SerialModule();
|
||||
@ -268,13 +267,11 @@ void setupModules()
|
||||
storeForwardModule = new StoreForwardModule();
|
||||
#endif
|
||||
#endif
|
||||
#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040) || defined(ARCH_PORTDUINO)
|
||||
#if !MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION
|
||||
externalNotificationModule = new ExternalNotificationModule();
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_RANGETEST && !MESHTASTIC_EXCLUDE_GPS
|
||||
new RangeTestModule();
|
||||
#endif
|
||||
#endif
|
||||
} else {
|
||||
#if !MESHTASTIC_EXCLUDE_ADMIN
|
||||
|
@ -31,7 +31,7 @@ uint32_t packetSequence = 0;
|
||||
|
||||
int32_t RangeTestModule::runOnce()
|
||||
{
|
||||
#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_PORTDUINO)
|
||||
#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_STM32WL) || defined(ARCH_PORTDUINO)
|
||||
|
||||
/*
|
||||
Uncomment the preferences below if you want to use the module
|
||||
@ -130,7 +130,7 @@ void RangeTestModuleRadio::sendPayload(NodeNum dest, bool wantReplies)
|
||||
|
||||
ProcessMessage RangeTestModuleRadio::handleReceived(const meshtastic_MeshPacket &mp)
|
||||
{
|
||||
#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_PORTDUINO)
|
||||
#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_STM32WL) || defined(ARCH_PORTDUINO)
|
||||
|
||||
if (moduleConfig.range_test.enabled) {
|
||||
|
||||
|
@ -49,8 +49,8 @@
|
||||
#include "meshSolarApp.h"
|
||||
#endif
|
||||
|
||||
#if (defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040)) && !defined(CONFIG_IDF_TARGET_ESP32S2) && \
|
||||
!defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#if (defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040) || defined(ARCH_STM32WL)) && \
|
||||
!defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
|
||||
#define RX_BUFFER 256
|
||||
#define TIMEOUT 250
|
||||
@ -67,7 +67,7 @@ SerialModuleRadio *serialModuleRadio;
|
||||
defined(ELECROW_ThinkNode_M5) || defined(HELTEC_MESH_SOLAR) || defined(T_ECHO_LITE)
|
||||
SerialModule::SerialModule() : StreamAPI(&Serial), concurrency::OSThread("Serial") {}
|
||||
static Print *serialPrint = &Serial;
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C6)
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C6) || defined(RAK3172)
|
||||
SerialModule::SerialModule() : StreamAPI(&Serial1), concurrency::OSThread("Serial") {}
|
||||
static Print *serialPrint = &Serial1;
|
||||
#else
|
||||
@ -173,7 +173,18 @@ int32_t SerialModule::runOnce()
|
||||
Serial.begin(baud);
|
||||
Serial.setTimeout(moduleConfig.serial.timeout > 0 ? moduleConfig.serial.timeout : TIMEOUT);
|
||||
}
|
||||
|
||||
#elif defined(ARCH_STM32WL)
|
||||
#ifndef RAK3172
|
||||
HardwareSerial *serialInstance = &Serial2;
|
||||
#else
|
||||
HardwareSerial *serialInstance = &Serial1;
|
||||
#endif
|
||||
if (moduleConfig.serial.rxd && moduleConfig.serial.txd) {
|
||||
serialInstance->setTx(moduleConfig.serial.txd);
|
||||
serialInstance->setRx(moduleConfig.serial.rxd);
|
||||
}
|
||||
serialInstance->begin(baud);
|
||||
serialInstance->setTimeout(moduleConfig.serial.timeout > 0 ? moduleConfig.serial.timeout : TIMEOUT);
|
||||
#elif defined(ARCH_ESP32)
|
||||
|
||||
if (moduleConfig.serial.rxd && moduleConfig.serial.txd) {
|
||||
@ -260,8 +271,13 @@ int32_t SerialModule::runOnce()
|
||||
while (Serial1.available()) {
|
||||
serialPayloadSize = Serial1.readBytes(serialBytes, meshtastic_Constants_DATA_PAYLOAD_LEN);
|
||||
#else
|
||||
while (Serial2.available()) {
|
||||
serialPayloadSize = Serial2.readBytes(serialBytes, meshtastic_Constants_DATA_PAYLOAD_LEN);
|
||||
#ifndef RAK3172
|
||||
HardwareSerial *serialInstance = &Serial2;
|
||||
#else
|
||||
HardwareSerial *serialInstance = &Serial1;
|
||||
#endif
|
||||
while (serialInstance->available()) {
|
||||
serialPayloadSize = serialInstance->readBytes(serialBytes, meshtastic_Constants_DATA_PAYLOAD_LEN);
|
||||
#endif
|
||||
serialModuleRadio->sendPayload();
|
||||
}
|
||||
@ -511,7 +527,7 @@ ParsedLine parseLine(const char *line)
|
||||
void SerialModule::processWXSerial()
|
||||
{
|
||||
#if !defined(TTGO_T_ECHO) && !defined(T_ECHO_LITE) && !defined(CANARYONE) && !defined(CONFIG_IDF_TARGET_ESP32C6) && \
|
||||
!defined(MESHLINK) && !defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M5)
|
||||
!defined(MESHLINK) && !defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M5) && !defined(ARCH_STM32WL)
|
||||
static unsigned int lastAveraged = 0;
|
||||
static unsigned int averageIntervalMillis = 300000; // 5 minutes hard coded.
|
||||
static double dir_sum_sin = 0;
|
||||
|
@ -8,8 +8,8 @@
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
|
||||
#if (defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040)) && !defined(CONFIG_IDF_TARGET_ESP32S2) && \
|
||||
!defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#if (defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040) || defined(ARCH_STM32WL)) && \
|
||||
!defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
|
||||
class SerialModule : public StreamAPI, private concurrency::OSThread
|
||||
{
|
||||
|
@ -10,10 +10,19 @@
|
||||
#include "PowerFSM.h"
|
||||
#include "RTC.h"
|
||||
#include "Router.h"
|
||||
#include "UnitConversions.h"
|
||||
#include "detect/ScanI2CTwoWire.h"
|
||||
#include "graphics/ScreenFonts.h"
|
||||
#include "main.h"
|
||||
#include "sleep.h"
|
||||
#include <Throttle.h>
|
||||
|
||||
// Sensors
|
||||
#include "Sensor/PMSA0031Sensor.h"
|
||||
#include "Sensor/SCD4XSensor.h"
|
||||
|
||||
SCD4XSensor scd4xSensor;
|
||||
PMSA0031Sensor pmsa0031Sensor;
|
||||
#ifndef PMSA003I_WARMUP_MS
|
||||
// from the PMSA003I datasheet:
|
||||
// "Stable data should be got at least 30 seconds after the sensor wakeup
|
||||
@ -23,11 +32,20 @@
|
||||
|
||||
int32_t AirQualityTelemetryModule::runOnce()
|
||||
{
|
||||
if (sleepOnNextExecution == true) {
|
||||
sleepOnNextExecution = false;
|
||||
uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval,
|
||||
default_telemetry_broadcast_interval_secs);
|
||||
LOG_DEBUG("Sleeping for %ims, then awaking to send metrics again.", nightyNightMs);
|
||||
doDeepSleep(nightyNightMs, true);
|
||||
}
|
||||
|
||||
uint32_t result = UINT32_MAX;
|
||||
|
||||
/*
|
||||
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;
|
||||
|
||||
if (!(moduleConfig.telemetry.air_quality_enabled)) {
|
||||
@ -41,24 +59,27 @@ int32_t AirQualityTelemetryModule::runOnce()
|
||||
|
||||
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);
|
||||
#endif /* PMSA003I_ENABLE_PIN */
|
||||
if (aqi_found.address == 0x00) {
|
||||
|
||||
|
||||
if (!aqi.begin_I2C()) {
|
||||
#ifndef I2C_NO_RESCAN
|
||||
LOG_WARN("Could not establish i2c connection to AQI sensor. Rescan");
|
||||
LOG_WARN("Rescan for I2C AQI Sensor");
|
||||
// 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;
|
||||
uint8_t i2caddr_scan[] = {PMSA0031_ADDR, SCD4X_ADDR};
|
||||
uint8_t i2caddr_asize = 2;
|
||||
auto i2cScanner = std::unique_ptr<ScanI2CTwoWire>(new ScanI2CTwoWire());
|
||||
#if defined(I2C_SDA1)
|
||||
|
||||
#if WIRE_INTERFACES_COUNT == 2
|
||||
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;
|
||||
@ -66,8 +87,36 @@ int32_t AirQualityTelemetryModule::runOnce()
|
||||
i2cScanner->fetchI2CBus(found.address);
|
||||
return setStartDelay();
|
||||
}
|
||||
#endif
|
||||
if (aqi_found.address == 0x00) {
|
||||
return disable();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (scd4xSensor.hasSensor())
|
||||
result = scd4xSensor.runOnce();
|
||||
if (pmsa0031Sensor.hasSensor())
|
||||
result = pmsa0031Sensor.runOnce();
|
||||
return result;
|
||||
|
||||
} 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();
|
||||
|
||||
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 (((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 setStartDelay();
|
||||
}
|
||||
@ -115,6 +164,7 @@ int32_t AirQualityTelemetryModule::runOnce()
|
||||
default:
|
||||
return disable();
|
||||
}
|
||||
return min(sendToPhoneIntervalMs, result);
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,9 +174,9 @@ bool AirQualityTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPack
|
||||
#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE)
|
||||
const char *sender = getSenderShortName(mp);
|
||||
|
||||
LOG_INFO("(Received from %s): pm10_standard=%i, pm25_standard=%i, pm100_standard=%i", sender,
|
||||
LOG_INFO("(Received from %s): pm10_standard=%i, pm25_standard=%i, pm100_standard=%i, co2=%i ppm", sender,
|
||||
t->variant.air_quality_metrics.pm10_standard, t->variant.air_quality_metrics.pm25_standard,
|
||||
t->variant.air_quality_metrics.pm100_standard);
|
||||
t->variant.air_quality_metrics.pm100_standard, t->variant.air_quality_metrics.co2);
|
||||
|
||||
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,
|
||||
@ -142,13 +192,38 @@ bool AirQualityTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPack
|
||||
return false; // Let others look at this message also if they want
|
||||
}
|
||||
|
||||
bool AirQualityTelemetryModule::getAirQualityTelemetry(meshtastic_Telemetry *m)
|
||||
bool AirQualityTelemetryModule::wantUIFrame()
|
||||
{
|
||||
if (!aqi.read(&data)) {
|
||||
LOG_WARN("Skip send measurements. Could not read AQIn");
|
||||
return false;
|
||||
return moduleConfig.telemetry.environment_screen_enabled;
|
||||
}
|
||||
|
||||
void AirQualityTelemetryModule::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 "Environment"
|
||||
display->drawString(x, y, "Air Quality");
|
||||
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 "Env. From: ..." on its own
|
||||
display->drawString(x, y, "AQ. From: " + String(lastSender) + "(" + String(agoSecs) + "s)");
|
||||
|
||||
m->time = getTime();
|
||||
m->which_variant = meshtastic_Telemetry_air_quality_metrics_tag;
|
||||
m->variant.air_quality_metrics.has_pm10_standard = true;
|
||||
@ -168,11 +243,42 @@ bool AirQualityTelemetryModule::getAirQualityTelemetry(meshtastic_Telemetry *m)
|
||||
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);
|
||||
if (lastMeasurement.variant.air_quality_metrics.has_pm10_standard) {
|
||||
display->drawString(x, y += _fontHeight(FONT_SMALL),
|
||||
"PM1.0(Standard): " + String(lastMeasurement.variant.air_quality_metrics.pm10_standard, 0));
|
||||
}
|
||||
if (lastMeasurement.variant.air_quality_metrics.has_pm25_standard) {
|
||||
display->drawString(x, y += _fontHeight(FONT_SMALL),
|
||||
"PM2.5(Standard): " + String(lastMeasurement.variant.air_quality_metrics.pm25_standard, 0));
|
||||
}
|
||||
if (lastMeasurement.variant.air_quality_metrics.has_pm10_environmental) {
|
||||
display->drawString(x, y += _fontHeight(FONT_SMALL),
|
||||
"PM10.0(Standard): " + String(lastMeasurement.variant.air_quality_metrics.pm100_standard, 0));
|
||||
}
|
||||
if (lastMeasurement.variant.air_quality_metrics.has_co2) {
|
||||
display->drawString(x, y += _fontHeight(FONT_SMALL),
|
||||
"CO2: " + String(lastMeasurement.variant.air_quality_metrics.co2, 0) + " ppm");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
bool AirQualityTelemetryModule::getAirQualityTelemetry(meshtastic_Telemetry *m)
|
||||
{
|
||||
bool valid = true;
|
||||
bool hasSensor = false;
|
||||
m->time = getTime();
|
||||
m->which_variant = meshtastic_Telemetry_air_quality_metrics_tag;
|
||||
m->variant.air_quality_metrics = meshtastic_AirQualityMetrics_init_zero;
|
||||
|
||||
if (scd4xSensor.hasSensor()) {
|
||||
valid = valid && scd4xSensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
}
|
||||
if (pmsa0031Sensor.hasSensor()) {
|
||||
valid = valid && pmsa0031Sensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
}
|
||||
|
||||
return valid && hasSensor;
|
||||
}
|
||||
|
||||
meshtastic_MeshPacket *AirQualityTelemetryModule::allocReply()
|
||||
@ -207,6 +313,14 @@ bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
||||
{
|
||||
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
|
||||
if (getAirQualityTelemetry(&m)) {
|
||||
LOG_INFO("(Sending): PM1.0(Standard)=%i, PM2.5(Standard)=%i, PM10.0(Standard)=%i, cO2=%i ppm",
|
||||
m.variant.air_quality_metrics.pm10_standard, m.variant.air_quality_metrics.pm25_standard,
|
||||
m.variant.air_quality_metrics.pm100_standard, m.variant.air_quality_metrics.co2);
|
||||
|
||||
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);
|
||||
|
||||
meshtastic_MeshPacket *p = allocDataProtobuf(m);
|
||||
p->to = dest;
|
||||
p->decoded.want_response = false;
|
||||
|
@ -4,9 +4,10 @@
|
||||
|
||||
#pragma once
|
||||
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||
#include "Adafruit_PM25AQI.h"
|
||||
#include "NodeDB.h"
|
||||
#include "ProtobufModule.h"
|
||||
#include <OLEDDisplay.h>
|
||||
#include <OLEDDisplayUi.h>
|
||||
|
||||
class AirQualityTelemetryModule : private concurrency::OSThread, public ProtobufModule<meshtastic_Telemetry>
|
||||
{
|
||||
@ -20,9 +21,8 @@ class AirQualityTelemetryModule : private concurrency::OSThread, public Protobuf
|
||||
ProtobufModule("AirQualityTelemetry", meshtastic_PortNum_TELEMETRY_APP, &meshtastic_Telemetry_msg)
|
||||
{
|
||||
lastMeasurementPacket = nullptr;
|
||||
setIntervalFromNow(10 * 1000);
|
||||
aqi = Adafruit_PM25AQI();
|
||||
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
|
||||
setIntervalFromNow(10 * 1000);
|
||||
|
||||
#ifdef PMSA003I_ENABLE_PIN
|
||||
// the PMSA003I sensor uses about 300mW on its own; support powering it off when it's not actively taking
|
||||
@ -32,6 +32,12 @@ class AirQualityTelemetryModule : private concurrency::OSThread, public Protobuf
|
||||
state = State::ACTIVE;
|
||||
#endif
|
||||
}
|
||||
virtual bool wantUIFrame() override;
|
||||
#if !HAS_SCREEN
|
||||
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;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
/** Called to handle a particular incoming message
|
||||
@ -62,6 +68,8 @@ class AirQualityTelemetryModule : private concurrency::OSThread, public Protobuf
|
||||
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
|
@ -510,6 +510,7 @@ bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPac
|
||||
sender, t->variant.environment_metrics.barometric_pressure, t->variant.environment_metrics.current,
|
||||
t->variant.environment_metrics.gas_resistance, t->variant.environment_metrics.relative_humidity,
|
||||
t->variant.environment_metrics.temperature);
|
||||
|
||||
LOG_INFO("(Received from %s): voltage=%f, IAQ=%d, distance=%f, lux=%f, white_lux=%f", sender,
|
||||
t->variant.environment_metrics.voltage, t->variant.environment_metrics.iaq,
|
||||
t->variant.environment_metrics.distance, t->variant.environment_metrics.lux,
|
||||
|
47
src/modules/Telemetry/Sensor/PMSA0031Sensor.cpp
Normal file
47
src/modules/Telemetry/Sensor/PMSA0031Sensor.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
#include "configuration.h"
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||
|
||||
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||
#include "PMSA0031Sensor.h"
|
||||
#include "TelemetrySensor.h"
|
||||
#include <Adafruit_PM25AQI.h>
|
||||
|
||||
PMSA0031Sensor::PMSA0031Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_PMSA003I, "PMSA0031") {}
|
||||
|
||||
int32_t PMSA0031Sensor::runOnce()
|
||||
{
|
||||
LOG_INFO("Init sensor: %s\n", sensorName);
|
||||
if (!hasSensor()) {
|
||||
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
|
||||
}
|
||||
aqi = Adafruit_PM25AQI();
|
||||
delay(10000);
|
||||
aqi.begin_I2C();
|
||||
/* nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].first = found.address.address;
|
||||
nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].second =
|
||||
i2cScanner->fetchI2CBus(found.address););*/
|
||||
return initI2CSensor();
|
||||
}
|
||||
|
||||
void PMSA0031Sensor::setup() {}
|
||||
|
||||
bool PMSA0031Sensor::getMetrics(meshtastic_Telemetry *measurement)
|
||||
{
|
||||
uint16_t co2, error;
|
||||
float temperature, humidity;
|
||||
if (!aqi.read(&data)) {
|
||||
LOG_WARN("Skipping send measurements. Could not read AQIn");
|
||||
return false;
|
||||
}
|
||||
measurement->variant.air_quality_metrics.pm10_standard = data.pm10_standard;
|
||||
measurement->variant.air_quality_metrics.pm25_standard = data.pm25_standard;
|
||||
measurement->variant.air_quality_metrics.pm100_standard = data.pm100_standard;
|
||||
|
||||
measurement->variant.air_quality_metrics.pm10_environmental = data.pm10_env;
|
||||
measurement->variant.air_quality_metrics.pm25_environmental = data.pm25_env;
|
||||
measurement->variant.air_quality_metrics.pm100_environmental = data.pm100_env;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
24
src/modules/Telemetry/Sensor/PMSA0031Sensor.h
Normal file
24
src/modules/Telemetry/Sensor/PMSA0031Sensor.h
Normal file
@ -0,0 +1,24 @@
|
||||
#include "configuration.h"
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||
|
||||
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||
#include "TelemetrySensor.h"
|
||||
#include <Adafruit_PM25AQI.h>
|
||||
|
||||
class PMSA0031Sensor : public TelemetrySensor
|
||||
{
|
||||
private:
|
||||
Adafruit_PM25AQI aqi;
|
||||
PM25_AQI_Data data = {0};
|
||||
|
||||
protected:
|
||||
virtual void setup() override;
|
||||
|
||||
public:
|
||||
PMSA0031Sensor();
|
||||
virtual int32_t runOnce() override;
|
||||
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
|
||||
};
|
||||
|
||||
#endif
|
52
src/modules/Telemetry/Sensor/SCD4XSensor.cpp
Normal file
52
src/modules/Telemetry/Sensor/SCD4XSensor.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
#include "configuration.h"
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||
|
||||
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||
#include "SCD4XSensor.h"
|
||||
#include "TelemetrySensor.h"
|
||||
#include <SensirionI2CScd4x.h>
|
||||
|
||||
SCD4XSensor::SCD4XSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_SCD4X, "SCD4X") {}
|
||||
|
||||
int32_t SCD4XSensor::runOnce()
|
||||
{
|
||||
LOG_INFO("Init sensor: %s\n", sensorName);
|
||||
if (!hasSensor()) {
|
||||
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
|
||||
}
|
||||
// scd4x = SensirionI2CScd4x(nodeTelemetrySensorsMap[sensorType].second);
|
||||
// status = scd4x.begin(nodeTelemetrySensorsMap[sensorType].first);
|
||||
scd4x.begin(*nodeTelemetrySensorsMap[sensorType].second);
|
||||
scd4x.stopPeriodicMeasurement();
|
||||
status = scd4x.startLowPowerPeriodicMeasurement();
|
||||
if (status == 0) {
|
||||
status = 1;
|
||||
} else {
|
||||
status = 0;
|
||||
}
|
||||
return initI2CSensor();
|
||||
}
|
||||
|
||||
void SCD4XSensor::setup() {}
|
||||
|
||||
bool SCD4XSensor::getMetrics(meshtastic_Telemetry *measurement)
|
||||
{
|
||||
uint16_t co2, error;
|
||||
float temperature, humidity;
|
||||
error = scd4x.readMeasurement(co2, temperature, humidity);
|
||||
if (error || co2 == 0) {
|
||||
LOG_DEBUG("Skipping invalid SCD4X measurement.\n");
|
||||
return false;
|
||||
} else {
|
||||
measurement->variant.environment_metrics.has_temperature = true;
|
||||
measurement->variant.environment_metrics.has_relative_humidity = true;
|
||||
measurement->variant.air_quality_metrics.has_co2 = true;
|
||||
measurement->variant.environment_metrics.temperature = temperature;
|
||||
measurement->variant.environment_metrics.relative_humidity = humidity;
|
||||
measurement->variant.air_quality_metrics.co2 = co2;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
23
src/modules/Telemetry/Sensor/SCD4XSensor.h
Normal file
23
src/modules/Telemetry/Sensor/SCD4XSensor.h
Normal file
@ -0,0 +1,23 @@
|
||||
#include "configuration.h"
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||
|
||||
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||
#include "TelemetrySensor.h"
|
||||
#include <SensirionI2CScd4x.h>
|
||||
|
||||
class SCD4XSensor : public TelemetrySensor
|
||||
{
|
||||
private:
|
||||
SensirionI2CScd4x scd4x = SensirionI2CScd4x();
|
||||
|
||||
protected:
|
||||
virtual void setup() override;
|
||||
|
||||
public:
|
||||
SCD4XSensor();
|
||||
virtual int32_t runOnce() override;
|
||||
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
|
||||
};
|
||||
|
||||
#endif
|
@ -124,8 +124,7 @@ extern "C" {
|
||||
#define GPS_RTC_INT (0 + 15) // P0.15, normal is LOW, wake by HIGH
|
||||
#define GPS_RESETB_OUT (32 + 14) // P1.14, always input pull_up
|
||||
|
||||
#define GPS_FIX_HOLD_TIME 15000 // ms
|
||||
#define BATTERY_PIN 2 // P0.02/AIN0, BAT_ADC
|
||||
#define BATTERY_PIN 2 // P0.02/AIN0, BAT_ADC
|
||||
#define BATTERY_IMMUTABLE
|
||||
#define ADC_MULTIPLIER (2.0F)
|
||||
// P0.04/AIN2 is VCC_ADC, P0.05/AIN3 is CHARGER_DET, P1.03 is CHARGE_STA, P1.04 is CHARGE_DONE
|
||||
|
@ -123,7 +123,6 @@ extern "C" {
|
||||
#define GPS_RESETB_OUT (32 + 14) // P1.14, awlays input pull_up
|
||||
|
||||
// #define GPS_THREAD_INTERVAL 50
|
||||
#define GPS_FIX_HOLD_TIME 15000 // ms
|
||||
|
||||
#define BATTERY_PIN 2
|
||||
// #define ADC_CHANNEL ADC1_GPIO2_CHANNEL
|
||||
@ -157,4 +156,4 @@ extern "C" {
|
||||
* Arduino objects - C++ only
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
#endif // _VARIANT_WIO_SDK_WM1110_
|
||||
#endif // _VARIANT_WIO_SDK_WM1110_
|
||||
|
@ -6,6 +6,10 @@ board_upload.maximum_size = 233472 ; reserve the last 28KB for filesystem
|
||||
build_flags =
|
||||
${stm32_base.build_flags}
|
||||
-Ivariants/stm32/rak3172
|
||||
-DRAK3172
|
||||
-DENABLE_HWSERIAL1
|
||||
-DPIN_SERIAL1_RX=PB7
|
||||
-DPIN_SERIAL1_TX=PB6
|
||||
-DPIN_WIRE_SDA=PA11
|
||||
-DPIN_WIRE_SCL=PA12
|
||||
-DMESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR=1
|
||||
|
Loading…
Reference in New Issue
Block a user