mirror of
https://github.com/meshcore-dev/MeshCore.git
synced 2026-08-29 05:28:38 +00:00
Merge remote-tracking branch 'upstream/dev' into rak-ethernet
# Conflicts: # examples/companion_radio/main.cpp
This commit is contained in:
+2
-1
@@ -193,8 +193,9 @@ public:
|
||||
bool millisHasNowPassed(unsigned long timestamp) const;
|
||||
unsigned long futureMillis(int millis_from_now) const;
|
||||
|
||||
private:
|
||||
bool tryParsePacket(Packet* pkt, const uint8_t* raw, int len);
|
||||
|
||||
private:
|
||||
void checkRecv();
|
||||
void checkSend();
|
||||
};
|
||||
|
||||
@@ -27,9 +27,11 @@ bool AutoDiscoverRTCClock::i2c_probe(TwoWire& wire, uint8_t addr) {
|
||||
}
|
||||
|
||||
void AutoDiscoverRTCClock::begin(TwoWire& wire) {
|
||||
#if !defined(DISABLE_DS3231_PROBE)
|
||||
if (i2c_probe(wire, DS3231_ADDRESS)) {
|
||||
ds3231_success = rtc_3231.begin(&wire);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (i2c_probe(wire, RV3028_ADDRESS)) {
|
||||
rtc_rv3028.initI2C(wire);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "CommonCLI.h"
|
||||
#include "TxtDataHelpers.h"
|
||||
#include "AdvertDataHelpers.h"
|
||||
#include "TxtDataHelpers.h"
|
||||
#include <RTClib.h>
|
||||
|
||||
#ifndef BRIDGE_MAX_BAUD
|
||||
@@ -285,7 +286,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
|
||||
// change admin password
|
||||
StrHelper::strncpy(_prefs->password, &command[9], sizeof(_prefs->password));
|
||||
savePrefs();
|
||||
sprintf(reply, "password now: %s", _prefs->password); // echo back just to let admin know for sure!!
|
||||
sprintf(reply, "password now: ");
|
||||
StrHelper::strncpy(&reply[14], _prefs->password, 160-15); // echo back just to let admin know for sure!!
|
||||
} else if (memcmp(command, "clear stats", 11) == 0) {
|
||||
_callbacks->clearStats();
|
||||
strcpy(reply, "(OK - stats reset)");
|
||||
@@ -726,7 +728,8 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
|
||||
strcpy(reply, "Error: unsupported by this board");
|
||||
};
|
||||
} else {
|
||||
sprintf(reply, "unknown config: %s", config);
|
||||
strcpy(reply, "unknown config: ");
|
||||
StrHelper::strncpy(&reply[16], config, 160-17);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -784,10 +787,11 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
|
||||
} else if (memcmp(config, "direct.txdelay", 14) == 0) {
|
||||
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor));
|
||||
} else if (memcmp(config, "owner.info", 10) == 0) {
|
||||
auto start = reply;
|
||||
*reply++ = '>';
|
||||
*reply++ = ' ';
|
||||
const char* sp = _prefs->owner_info;
|
||||
while (*sp) {
|
||||
while (*sp && reply - start < 159) {
|
||||
*reply++ = (*sp == '\n') ? '|' : *sp; // translate newline back to orig '|'
|
||||
sp++;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,10 @@ void ESPNOWRadio::init() {
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ESPNOWRadio::getRngSeed() {
|
||||
return millis() + intID(); // TODO: where to get some entropy?
|
||||
}
|
||||
|
||||
void ESPNOWRadio::setTxPower(uint8_t dbm) {
|
||||
esp_wifi_set_max_tx_power(dbm * 4);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,13 @@ protected:
|
||||
public:
|
||||
ESPNOWRadio() { n_recv = n_sent = n_recv_errors = 0; }
|
||||
|
||||
uint32_t getRngSeed();
|
||||
|
||||
void setParams(float freq, float bw, uint8_t sf, uint8_t cr) {
|
||||
// no-op
|
||||
}
|
||||
void powerOff() { /* no-op */ }
|
||||
|
||||
void init();
|
||||
int recvRaw(uint8_t* bytes, int sz) override;
|
||||
uint32_t getEstAirtimeFor(int len_bytes) override;
|
||||
|
||||
@@ -7,6 +7,15 @@
|
||||
class CustomLLCC68Wrapper : public RadioLibWrapper {
|
||||
public:
|
||||
CustomLLCC68Wrapper(CustomLLCC68& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { }
|
||||
|
||||
void setParams(float freq, float bw, uint8_t sf, uint8_t cr) override {
|
||||
((CustomLLCC68 *)_radio)->setFrequency(freq);
|
||||
((CustomLLCC68 *)_radio)->setSpreadingFactor(sf);
|
||||
((CustomLLCC68 *)_radio)->setBandwidth(bw);
|
||||
((CustomLLCC68 *)_radio)->setCodingRate(cr);
|
||||
updatePreamble(sf);
|
||||
}
|
||||
|
||||
bool isReceivingPacket() override {
|
||||
return ((CustomLLCC68 *)_radio)->isReceiving();
|
||||
}
|
||||
@@ -20,6 +29,7 @@ public:
|
||||
int sf = ((CustomLLCC68 *)_radio)->spreadingFactor;
|
||||
return packetScoreInt(snr, sf, packet_len);
|
||||
}
|
||||
uint8_t getSpreadingFactor() const override { return ((CustomLLCC68 *)_radio)->spreadingFactor; }
|
||||
|
||||
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); }
|
||||
|
||||
|
||||
@@ -36,4 +36,6 @@ class CustomLR1110 : public LR1110 {
|
||||
bool detected = ((irq & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID) || (irq & RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED));
|
||||
return detected;
|
||||
}
|
||||
|
||||
uint8_t getSpreadingFactor() const { return spreadingFactor; }
|
||||
};
|
||||
@@ -7,6 +7,15 @@
|
||||
class CustomLR1110Wrapper : public RadioLibWrapper {
|
||||
public:
|
||||
CustomLR1110Wrapper(CustomLR1110& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { }
|
||||
|
||||
void setParams(float freq, float bw, uint8_t sf, uint8_t cr) override {
|
||||
((CustomLR1110 *)_radio)->setFrequency(freq);
|
||||
((CustomLR1110 *)_radio)->setSpreadingFactor(sf);
|
||||
((CustomLR1110 *)_radio)->setBandwidth(bw);
|
||||
((CustomLR1110 *)_radio)->setCodingRate(cr);
|
||||
updatePreamble(sf);
|
||||
}
|
||||
|
||||
void doResetAGC() override { lr11x0ResetAGC((LR11x0 *)_radio, ((CustomLR1110 *)_radio)->getFreqMHz()); }
|
||||
bool isReceivingPacket() override {
|
||||
return ((CustomLR1110 *)_radio)->isReceiving();
|
||||
@@ -19,12 +28,14 @@ public:
|
||||
|
||||
void onSendFinished() override {
|
||||
RadioLibWrapper::onSendFinished();
|
||||
_radio->setPreambleLength(16); // overcomes weird issues with small and big pkts
|
||||
_radio->setPreambleLength(preambleLengthForSF(getSpreadingFactor())); // overcomes weird issues with small and big pkts
|
||||
}
|
||||
|
||||
float getLastRSSI() const override { return ((CustomLR1110 *)_radio)->getRSSI(); }
|
||||
float getLastSNR() const override { return ((CustomLR1110 *)_radio)->getSNR(); }
|
||||
|
||||
uint8_t getSpreadingFactor() const override { return ((CustomLR1110 *)_radio)->getSpreadingFactor(); }
|
||||
|
||||
void setRxBoostedGainMode(bool en) override {
|
||||
((CustomLR1110 *)_radio)->setRxBoostedGainMode(en);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,15 @@
|
||||
class CustomSTM32WLxWrapper : public RadioLibWrapper {
|
||||
public:
|
||||
CustomSTM32WLxWrapper(CustomSTM32WLx& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { }
|
||||
|
||||
void setParams(float freq, float bw, uint8_t sf, uint8_t cr) override {
|
||||
((CustomSTM32WLx *)_radio)->setFrequency(freq);
|
||||
((CustomSTM32WLx *)_radio)->setSpreadingFactor(sf);
|
||||
((CustomSTM32WLx *)_radio)->setBandwidth(bw);
|
||||
((CustomSTM32WLx *)_radio)->setCodingRate(cr);
|
||||
updatePreamble(sf);
|
||||
}
|
||||
|
||||
bool isReceivingPacket() override {
|
||||
return ((CustomSTM32WLx *)_radio)->isReceiving();
|
||||
}
|
||||
@@ -21,6 +30,7 @@ public:
|
||||
int sf = ((CustomSTM32WLx *)_radio)->spreadingFactor;
|
||||
return packetScoreInt(snr, sf, packet_len);
|
||||
}
|
||||
uint8_t getSpreadingFactor() const override { return ((CustomSTM32WLx *)_radio)->spreadingFactor; }
|
||||
|
||||
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); }
|
||||
};
|
||||
|
||||
@@ -11,6 +11,15 @@
|
||||
class CustomSX1262Wrapper : public RadioLibWrapper {
|
||||
public:
|
||||
CustomSX1262Wrapper(CustomSX1262& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { }
|
||||
|
||||
void setParams(float freq, float bw, uint8_t sf, uint8_t cr) override {
|
||||
((CustomSX1262 *)_radio)->setFrequency(freq);
|
||||
((CustomSX1262 *)_radio)->setSpreadingFactor(sf);
|
||||
((CustomSX1262 *)_radio)->setBandwidth(bw);
|
||||
((CustomSX1262 *)_radio)->setCodingRate(cr);
|
||||
updatePreamble(sf);
|
||||
}
|
||||
|
||||
bool isReceivingPacket() override {
|
||||
return ((CustomSX1262 *)_radio)->isReceiving();
|
||||
}
|
||||
@@ -24,6 +33,7 @@ public:
|
||||
int sf = ((CustomSX1262 *)_radio)->spreadingFactor;
|
||||
return packetScoreInt(snr, sf, packet_len);
|
||||
}
|
||||
uint8_t getSpreadingFactor() const override { return ((CustomSX1262 *)_radio)->spreadingFactor; }
|
||||
virtual void powerOff() override {
|
||||
((CustomSX1262 *)_radio)->sleep(false);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,15 @@
|
||||
class CustomSX1268Wrapper : public RadioLibWrapper {
|
||||
public:
|
||||
CustomSX1268Wrapper(CustomSX1268& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { }
|
||||
|
||||
void setParams(float freq, float bw, uint8_t sf, uint8_t cr) override {
|
||||
((CustomSX1268 *)_radio)->setFrequency(freq);
|
||||
((CustomSX1268 *)_radio)->setSpreadingFactor(sf);
|
||||
((CustomSX1268 *)_radio)->setBandwidth(bw);
|
||||
((CustomSX1268 *)_radio)->setCodingRate(cr);
|
||||
updatePreamble(sf);
|
||||
}
|
||||
|
||||
bool isReceivingPacket() override {
|
||||
return ((CustomSX1268 *)_radio)->isReceiving();
|
||||
}
|
||||
@@ -24,6 +33,7 @@ public:
|
||||
int sf = ((CustomSX1268 *)_radio)->spreadingFactor;
|
||||
return packetScoreInt(snr, sf, packet_len);
|
||||
}
|
||||
uint8_t getSpreadingFactor() const override { return ((CustomSX1268 *)_radio)->spreadingFactor; }
|
||||
|
||||
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); }
|
||||
|
||||
|
||||
@@ -10,6 +10,15 @@
|
||||
class CustomSX1276Wrapper : public RadioLibWrapper {
|
||||
public:
|
||||
CustomSX1276Wrapper(CustomSX1276& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { }
|
||||
|
||||
void setParams(float freq, float bw, uint8_t sf, uint8_t cr) override {
|
||||
((CustomSX1276 *)_radio)->setFrequency(freq);
|
||||
((CustomSX1276 *)_radio)->setSpreadingFactor(sf);
|
||||
((CustomSX1276 *)_radio)->setBandwidth(bw);
|
||||
((CustomSX1276 *)_radio)->setCodingRate(cr);
|
||||
updatePreamble(sf);
|
||||
}
|
||||
|
||||
bool isReceivingPacket() override {
|
||||
return ((CustomSX1276 *)_radio)->isReceiving();
|
||||
}
|
||||
@@ -23,4 +32,5 @@ public:
|
||||
int sf = ((CustomSX1276 *)_radio)->spreadingFactor;
|
||||
return packetScoreInt(snr, sf, packet_len);
|
||||
}
|
||||
uint8_t getSpreadingFactor() const override { return ((CustomSX1276 *)_radio)->spreadingFactor; }
|
||||
};
|
||||
|
||||
@@ -26,6 +26,8 @@ void setFlag(void) {
|
||||
|
||||
void RadioLibWrapper::begin() {
|
||||
_radio->setPacketReceivedAction(setFlag); // this is also SentComplete interrupt
|
||||
_preamble_sf = getSpreadingFactor();
|
||||
_radio->setPreambleLength(preambleLengthForSF(_preamble_sf)); // longer preamble for lower SF improves reliability
|
||||
state = STATE_IDLE;
|
||||
|
||||
if (_board->getStartupReason() == BD_STARTUP_RX_PACKET) { // received a LoRa packet (while in deep sleep)
|
||||
@@ -40,6 +42,14 @@ void RadioLibWrapper::begin() {
|
||||
_floor_sample_sum = 0;
|
||||
}
|
||||
|
||||
uint32_t RadioLibWrapper::getRngSeed() {
|
||||
return _radio->random(0x7FFFFFFF);
|
||||
}
|
||||
|
||||
void RadioLibWrapper::setTxPower(int8_t dbm) {
|
||||
_radio->setOutputPower(dbm);
|
||||
}
|
||||
|
||||
void RadioLibWrapper::idle() {
|
||||
_radio->standby();
|
||||
state = STATE_IDLE; // need another startReceive()
|
||||
|
||||
@@ -11,6 +11,7 @@ protected:
|
||||
int16_t _noise_floor, _threshold;
|
||||
uint16_t _num_floor_samples;
|
||||
int32_t _floor_sample_sum;
|
||||
uint8_t _preamble_sf;
|
||||
|
||||
void idle();
|
||||
void startRecv();
|
||||
@@ -19,7 +20,7 @@ protected:
|
||||
virtual void doResetAGC();
|
||||
|
||||
public:
|
||||
RadioLibWrapper(PhysicalLayer& radio, mesh::MainBoard& board) : _radio(&radio), _board(&board) { n_recv = n_sent = 0; }
|
||||
RadioLibWrapper(PhysicalLayer& radio, mesh::MainBoard& board) : _radio(&radio), _board(&board), _preamble_sf(0) { n_recv = n_sent = 0; }
|
||||
|
||||
void begin() override;
|
||||
virtual void powerOff() { _radio->sleep(); }
|
||||
@@ -37,7 +38,14 @@ public:
|
||||
return isChannelActive();
|
||||
}
|
||||
|
||||
virtual void setParams(float freq, float bw, uint8_t sf, uint8_t cr) = 0;
|
||||
uint32_t getRngSeed();
|
||||
void setTxPower(int8_t dbm);
|
||||
|
||||
virtual float getCurrentRSSI() =0;
|
||||
virtual uint8_t getSpreadingFactor() const { return LORA_SF; }
|
||||
static uint16_t preambleLengthForSF(uint8_t sf) { return sf <= 8 ? 32 : 16; }
|
||||
void updatePreamble(uint8_t sf) { _preamble_sf = sf; _radio->setPreambleLength(preambleLengthForSF(sf)); }
|
||||
|
||||
int getNoiseFloor() const override { return _noise_floor; }
|
||||
void triggerNoiseFloorCalibrate(int threshold) override;
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
#include "EnvironmentSensorManager.h"
|
||||
|
||||
#include <Wire.h>
|
||||
|
||||
#if ENV_PIN_SDA && ENV_PIN_SCL
|
||||
#define TELEM_WIRE &Wire1 // Use Wire1 as the I2C bus for Environment Sensors
|
||||
#else
|
||||
#define TELEM_WIRE &Wire // Use default I2C bus for Environment Sensors
|
||||
#endif
|
||||
|
||||
// ============================================================
|
||||
// Sensor library includes and static driver instances
|
||||
// ============================================================
|
||||
|
||||
#ifdef ENV_INCLUDE_BME680
|
||||
#ifndef TELEM_BME680_ADDRESS
|
||||
#define TELEM_BME680_ADDRESS 0x76
|
||||
@@ -31,7 +37,7 @@ static Adafruit_AHTX0 AHTX0;
|
||||
#ifndef TELEM_BME280_ADDRESS
|
||||
#define TELEM_BME280_ADDRESS 0x76 // BME280 environmental sensor I2C address
|
||||
#endif
|
||||
#define TELEM_BME280_SEALEVELPRESSURE_HPA (1013.25) // Athmospheric pressure at sea level
|
||||
#define TELEM_BME280_SEALEVELPRESSURE_HPA (1013.25) // Atmospheric pressure at sea level
|
||||
#include <Adafruit_BME280.h>
|
||||
static Adafruit_BME280 BME280;
|
||||
#endif
|
||||
@@ -40,7 +46,7 @@ static Adafruit_BME280 BME280;
|
||||
#ifndef TELEM_BMP280_ADDRESS
|
||||
#define TELEM_BMP280_ADDRESS 0x76 // BMP280 environmental sensor I2C address
|
||||
#endif
|
||||
#define TELEM_BMP280_SEALEVELPRESSURE_HPA (1013.25) // Athmospheric pressure at sea level
|
||||
#define TELEM_BMP280_SEALEVELPRESSURE_HPA (1013.25) // Atmospheric pressure at sea level
|
||||
#include <Adafruit_BMP280.h>
|
||||
static Adafruit_BMP280 BMP280(TELEM_WIRE);
|
||||
#endif
|
||||
@@ -51,7 +57,7 @@ static Adafruit_SHTC3 SHTC3;
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_SHT4X
|
||||
#define TELEM_SHT4X_ADDRESS 0x44 //0x44 - 0x46
|
||||
#define TELEM_SHT4X_ADDRESS 0x44
|
||||
#include <SensirionI2cSht4x.h>
|
||||
static SensirionI2cSht4x SHT4X;
|
||||
#endif
|
||||
@@ -63,7 +69,7 @@ LPS22HBClass LPS22HB(*TELEM_WIRE);
|
||||
|
||||
#if ENV_INCLUDE_INA3221
|
||||
#ifndef TELEM_INA3221_ADDRESS
|
||||
#define TELEM_INA3221_ADDRESS 0x42 // INA3221 3 channel current sensor I2C address
|
||||
#define TELEM_INA3221_ADDRESS 0x42 // INA3221 3 channel current sensor I2C address
|
||||
#endif
|
||||
#ifndef TELEM_INA3221_SHUNT_VALUE
|
||||
#define TELEM_INA3221_SHUNT_VALUE 0.100 // most variants will have a 0.1 ohm shunts
|
||||
@@ -88,9 +94,9 @@ static Adafruit_INA260 INA260;
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_INA226
|
||||
#define TELEM_INA226_ADDRESS 0x44
|
||||
#define TELEM_INA226_ADDRESS 0x44
|
||||
#define TELEM_INA226_SHUNT_VALUE 0.100
|
||||
#define TELEM_INA226_MAX_AMP 0.8
|
||||
#define TELEM_INA226_MAX_AMP 0.8
|
||||
#include <INA226.h>
|
||||
static INA226 INA226(TELEM_INA226_ADDRESS, TELEM_WIRE);
|
||||
#endif
|
||||
@@ -161,10 +167,352 @@ public:
|
||||
static RAK12500LocationProvider RAK12500_provider;
|
||||
#endif
|
||||
|
||||
// ============================================================
|
||||
// I2C bus scanner
|
||||
// Probes every valid address and records which ones ACK.
|
||||
// This runs before any sensor library is touched, so a missing
|
||||
// or misbehaving device cannot stall or crash the boot sequence.
|
||||
// ============================================================
|
||||
|
||||
static void scanI2CBus(TwoWire* wire, bool found[128]) {
|
||||
for (uint8_t addr = 0x08; addr < 0x78; addr++) {
|
||||
wire->beginTransmission(addr);
|
||||
found[addr] = (wire->endTransmission() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Per-sensor init and query functions
|
||||
//
|
||||
// init(wire, address) — called only when the address was seen
|
||||
// on the bus. Returns 0 on failure, or the number of
|
||||
// telemetry channels the sensor will consume (1 for all
|
||||
// single-output sensors; INA3221 returns one per enabled
|
||||
// hardware channel; MLX90614 and RAK12035+calibration
|
||||
// return 2).
|
||||
//
|
||||
// query(channel, sub_channel, lpp) — called once per active
|
||||
// sensor entry during querySensors(). sub_channel is always
|
||||
// 0 for single-output sensors.
|
||||
// ============================================================
|
||||
|
||||
#if ENV_INCLUDE_AHTX0
|
||||
static uint8_t init_ahtx0(TwoWire* wire, uint8_t addr) {
|
||||
return AHTX0.begin(wire, 0, addr) ? 1 : 0;
|
||||
}
|
||||
static void query_ahtx0(uint8_t ch, uint8_t, CayenneLPP& lpp) {
|
||||
sensors_event_t humidity, temp;
|
||||
AHTX0.getEvent(&humidity, &temp);
|
||||
lpp.addTemperature(ch, temp.temperature);
|
||||
lpp.addRelativeHumidity(ch, humidity.relative_humidity);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENV_INCLUDE_BME680
|
||||
static uint8_t init_bme680(TwoWire*, uint8_t addr) {
|
||||
// Wire was set in the static constructor; begin() takes address only.
|
||||
return BME680.begin(addr) ? 1 : 0;
|
||||
}
|
||||
static void query_bme680(uint8_t ch, uint8_t, CayenneLPP& lpp) {
|
||||
if (BME680.performReading()) {
|
||||
lpp.addTemperature(ch, BME680.temperature);
|
||||
lpp.addRelativeHumidity(ch, BME680.humidity);
|
||||
lpp.addBarometricPressure(ch, BME680.pressure / 100);
|
||||
lpp.addAltitude(ch, 44330.0 * (1.0 - pow((BME680.pressure / 100) / TELEM_BME680_SEALEVELPRESSURE_HPA, 0.1903)));
|
||||
lpp.addAnalogInput(ch, BME680.gas_resistance);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_BME280
|
||||
static uint8_t init_bme280(TwoWire* wire, uint8_t addr) {
|
||||
if (!BME280.begin(addr, wire)) return 0;
|
||||
BME280.setSampling(Adafruit_BME280::MODE_FORCED,
|
||||
Adafruit_BME280::SAMPLING_X1,
|
||||
Adafruit_BME280::SAMPLING_X1,
|
||||
Adafruit_BME280::SAMPLING_X1,
|
||||
Adafruit_BME280::FILTER_OFF,
|
||||
Adafruit_BME280::STANDBY_MS_1000);
|
||||
return 1;
|
||||
}
|
||||
static void query_bme280(uint8_t ch, uint8_t, CayenneLPP& lpp) {
|
||||
if (BME280.takeForcedMeasurement()) {
|
||||
lpp.addTemperature(ch, BME280.readTemperature());
|
||||
lpp.addRelativeHumidity(ch, BME280.readHumidity());
|
||||
lpp.addBarometricPressure(ch, BME280.readPressure() / 100);
|
||||
lpp.addAltitude(ch, BME280.readAltitude(TELEM_BME280_SEALEVELPRESSURE_HPA));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_BMP280
|
||||
static uint8_t init_bmp280(TwoWire*, uint8_t addr) {
|
||||
// BMP280 static instance was constructed with TELEM_WIRE; begin() uses it.
|
||||
return BMP280.begin(addr) ? 1 : 0;
|
||||
}
|
||||
static void query_bmp280(uint8_t ch, uint8_t, CayenneLPP& lpp) {
|
||||
lpp.addTemperature(ch, BMP280.readTemperature());
|
||||
lpp.addBarometricPressure(ch, BMP280.readPressure() / 100);
|
||||
lpp.addAltitude(ch, BMP280.readAltitude(TELEM_BMP280_SEALEVELPRESSURE_HPA));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_SHTC3
|
||||
static uint8_t init_shtc3(TwoWire* wire, uint8_t) {
|
||||
// Adafruit_SHTC3::begin() does not accept an address (fixed at 0x70).
|
||||
return SHTC3.begin(wire) ? 1 : 0;
|
||||
}
|
||||
static void query_shtc3(uint8_t ch, uint8_t, CayenneLPP& lpp) {
|
||||
sensors_event_t humidity, temp;
|
||||
SHTC3.getEvent(&humidity, &temp);
|
||||
lpp.addTemperature(ch, temp.temperature);
|
||||
lpp.addRelativeHumidity(ch, humidity.relative_humidity);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_SHT4X
|
||||
static uint8_t init_sht4x(TwoWire* wire, uint8_t addr) {
|
||||
// SensirionI2cSht4x::begin() does not probe the hardware; use serialNumber()
|
||||
// as the actual presence check since it performs a real I2C transaction.
|
||||
SHT4X.begin(*wire, addr);
|
||||
uint32_t serial = 0;
|
||||
return (SHT4X.serialNumber(serial) == 0) ? 1 : 0;
|
||||
}
|
||||
static void query_sht4x(uint8_t ch, uint8_t, CayenneLPP& lpp) {
|
||||
float temperature, humidity;
|
||||
if (SHT4X.measureLowestPrecision(temperature, humidity) == 0) {
|
||||
lpp.addTemperature(ch, temperature);
|
||||
lpp.addRelativeHumidity(ch, humidity);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_LPS22HB
|
||||
static uint8_t init_lps22hb(TwoWire*, uint8_t) {
|
||||
// LPS22HBClass is constructed with the wire reference; begin() uses it.
|
||||
return LPS22HB.begin() ? 1 : 0;
|
||||
}
|
||||
static void query_lps22hb(uint8_t ch, uint8_t, CayenneLPP& lpp) {
|
||||
lpp.addTemperature(ch, LPS22HB.readTemperature());
|
||||
lpp.addBarometricPressure(ch, LPS22HB.readPressure() * 10); // convert kPa to hPa
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_INA3221
|
||||
static uint8_t init_ina3221(TwoWire* wire, uint8_t addr) {
|
||||
if (!INA3221.begin(addr, wire)) return 0;
|
||||
for (int i = 0; i < TELEM_INA3221_NUM_CHANNELS; i++) {
|
||||
INA3221.setShuntResistance(i, TELEM_INA3221_SHUNT_VALUE);
|
||||
}
|
||||
// Each enabled hardware channel becomes its own telemetry channel.
|
||||
uint8_t enabled = 0;
|
||||
for (int i = 0; i < TELEM_INA3221_NUM_CHANNELS; i++) {
|
||||
if (INA3221.isChannelEnabled(i)) enabled++;
|
||||
}
|
||||
return enabled > 0 ? enabled : 1;
|
||||
}
|
||||
static void query_ina3221(uint8_t ch, uint8_t sub_ch, CayenneLPP& lpp) {
|
||||
// sub_ch is the index of the nth enabled hardware channel.
|
||||
uint8_t seen = 0;
|
||||
for (int i = 0; i < TELEM_INA3221_NUM_CHANNELS; i++) {
|
||||
if (INA3221.isChannelEnabled(i)) {
|
||||
if (seen == sub_ch) {
|
||||
float v = INA3221.getBusVoltage(i);
|
||||
float c = INA3221.getCurrentAmps(i);
|
||||
lpp.addVoltage(ch, v);
|
||||
lpp.addCurrent(ch, c);
|
||||
lpp.addPower(ch, v * c);
|
||||
return;
|
||||
}
|
||||
seen++;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_INA219
|
||||
static uint8_t init_ina219(TwoWire* wire, uint8_t) {
|
||||
// INA219 static instance was constructed with the address; begin() uses it.
|
||||
return INA219.begin(wire) ? 1 : 0;
|
||||
}
|
||||
static void query_ina219(uint8_t ch, uint8_t, CayenneLPP& lpp) {
|
||||
lpp.addVoltage(ch, INA219.getBusVoltage_V());
|
||||
lpp.addCurrent(ch, INA219.getCurrent_mA() / 1000.0f);
|
||||
lpp.addPower(ch, INA219.getPower_mW() / 1000.0f);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_INA260
|
||||
static uint8_t init_ina260(TwoWire* wire, uint8_t addr) {
|
||||
return INA260.begin(addr, wire) ? 1 : 0;
|
||||
}
|
||||
static void query_ina260(uint8_t ch, uint8_t, CayenneLPP& lpp) {
|
||||
lpp.addVoltage(ch, INA260.readBusVoltage() / 1000.0f);
|
||||
lpp.addCurrent(ch, INA260.readCurrent() / 1000.0f);
|
||||
lpp.addPower(ch, INA260.readPower() / 1000.0f);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_INA226
|
||||
static uint8_t init_ina226(TwoWire*, uint8_t) {
|
||||
// INA226 static instance was constructed with address and wire.
|
||||
if (!INA226.begin()) return 0;
|
||||
INA226.setMaxCurrentShunt(TELEM_INA226_MAX_AMP, TELEM_INA226_SHUNT_VALUE);
|
||||
return 1;
|
||||
}
|
||||
static void query_ina226(uint8_t ch, uint8_t, CayenneLPP& lpp) {
|
||||
lpp.addVoltage(ch, INA226.getBusVoltage());
|
||||
lpp.addCurrent(ch, INA226.getCurrent_mA() / 1000.0f);
|
||||
lpp.addPower(ch, INA226.getPower_mW() / 1000.0f);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_MLX90614
|
||||
static uint8_t init_mlx90614(TwoWire* wire, uint8_t addr) {
|
||||
return MLX90614.begin(addr, wire) ? 2 : 0; // 2 channels: object temp, ambient temp
|
||||
}
|
||||
static void query_mlx90614(uint8_t ch, uint8_t sub_ch, CayenneLPP& lpp) {
|
||||
if (sub_ch == 0)
|
||||
lpp.addTemperature(ch, MLX90614.readObjectTempC());
|
||||
else
|
||||
lpp.addTemperature(ch, MLX90614.readAmbientTempC());
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_VL53L0X
|
||||
static uint8_t init_vl53l0x(TwoWire* wire, uint8_t addr) {
|
||||
return VL53L0X.begin(addr, false, wire) ? 1 : 0;
|
||||
}
|
||||
static void query_vl53l0x(uint8_t ch, uint8_t, CayenneLPP& lpp) {
|
||||
VL53L0X_RangingMeasurementData_t measure;
|
||||
VL53L0X.rangingTest(&measure, false);
|
||||
lpp.addDistance(ch, measure.RangeStatus != 4 ? measure.RangeMilliMeter / 1000.0f : 0.0f);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENV_INCLUDE_BMP085
|
||||
static uint8_t init_bmp085(TwoWire* wire, uint8_t) {
|
||||
return BMP085.begin(0, wire) ? 1 : 0; // mode 0 = ULTRALOWPOWER
|
||||
}
|
||||
static void query_bmp085(uint8_t ch, uint8_t, CayenneLPP& lpp) {
|
||||
lpp.addTemperature(ch, BMP085.readTemperature());
|
||||
lpp.addBarometricPressure(ch, BMP085.readPressure() / 100);
|
||||
lpp.addAltitude(ch, BMP085.readAltitude(TELEM_BMP085_SEALEVELPRESSURE_HPA * 100));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_RAK12035
|
||||
static uint8_t init_rak12035(TwoWire* wire, uint8_t addr) {
|
||||
// RAK12035 requires setup() before begin().
|
||||
RAK12035.setup(*wire);
|
||||
if (!RAK12035.begin(addr)) return 0;
|
||||
#ifdef ENABLE_RAK12035_CALIBRATION
|
||||
return 2; // moisture channel + calibration channel
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
static void query_rak12035(uint8_t ch, uint8_t sub_ch, CayenneLPP& lpp) {
|
||||
if (sub_ch == 0) {
|
||||
lpp.addTemperature(ch, RAK12035.get_sensor_temperature());
|
||||
lpp.addPercentage(ch, RAK12035.get_sensor_moisture());
|
||||
} else {
|
||||
#ifdef ENABLE_RAK12035_CALIBRATION
|
||||
float cap = RAK12035.get_sensor_capacitance();
|
||||
float wet = RAK12035.get_humidity_full();
|
||||
float dry = RAK12035.get_humidity_zero();
|
||||
lpp.addFrequency(ch, cap);
|
||||
lpp.addTemperature(ch, wet);
|
||||
lpp.addPower(ch, dry);
|
||||
if (cap > dry) RAK12035.set_humidity_zero(cap);
|
||||
if (cap < wet) RAK12035.set_humidity_full(cap);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// ============================================================
|
||||
// Sensor descriptor table
|
||||
//
|
||||
// Each entry maps an I2C address to a sensor's init and query
|
||||
// functions. Only entries whose ENV_INCLUDE_* guard is defined
|
||||
// are compiled in. The sentinel at the end keeps the array
|
||||
// non-empty regardless of which sensors are enabled.
|
||||
//
|
||||
// Ordering here determines channel assignment at runtime:
|
||||
// the first detected+initialized sensor gets channel 2, the
|
||||
// next gets channel 3, and so on.
|
||||
// ============================================================
|
||||
|
||||
struct SensorDef {
|
||||
uint8_t address;
|
||||
const char* name;
|
||||
uint8_t (*init)(TwoWire* wire, uint8_t address);
|
||||
void (*query)(uint8_t channel, uint8_t sub_channel, CayenneLPP& telemetry);
|
||||
};
|
||||
|
||||
static const SensorDef SENSOR_TABLE[] = {
|
||||
#if ENV_INCLUDE_AHTX0
|
||||
{ TELEM_AHTX_ADDRESS, "AHT10/AHT20", init_ahtx0, query_ahtx0 },
|
||||
#endif
|
||||
#ifdef ENV_INCLUDE_BME680
|
||||
{ TELEM_BME680_ADDRESS, "BME680", init_bme680, query_bme680 },
|
||||
#endif
|
||||
#if ENV_INCLUDE_BME280
|
||||
{ TELEM_BME280_ADDRESS, "BME280", init_bme280, query_bme280 },
|
||||
#endif
|
||||
#if ENV_INCLUDE_BMP280
|
||||
{ TELEM_BMP280_ADDRESS, "BMP280", init_bmp280, query_bmp280 },
|
||||
#endif
|
||||
#if ENV_INCLUDE_SHTC3
|
||||
{ 0x70, "SHTC3", init_shtc3, query_shtc3 },
|
||||
#endif
|
||||
#if ENV_INCLUDE_SHT4X
|
||||
{ TELEM_SHT4X_ADDRESS, "SHT4X", init_sht4x, query_sht4x },
|
||||
#endif
|
||||
#if ENV_INCLUDE_LPS22HB
|
||||
{ 0x5C, "LPS22HB", init_lps22hb, query_lps22hb },
|
||||
#endif
|
||||
#if ENV_INCLUDE_INA3221
|
||||
{ TELEM_INA3221_ADDRESS, "INA3221", init_ina3221, query_ina3221 },
|
||||
#endif
|
||||
#if ENV_INCLUDE_INA219
|
||||
{ TELEM_INA219_ADDRESS, "INA219", init_ina219, query_ina219 },
|
||||
#endif
|
||||
#if ENV_INCLUDE_INA260
|
||||
{ TELEM_INA260_ADDRESS, "INA260", init_ina260, query_ina260 },
|
||||
#endif
|
||||
#if ENV_INCLUDE_INA226
|
||||
{ TELEM_INA226_ADDRESS, "INA226", init_ina226, query_ina226 },
|
||||
#endif
|
||||
#if ENV_INCLUDE_MLX90614
|
||||
{ TELEM_MLX90614_ADDRESS,"MLX90614", init_mlx90614, query_mlx90614 },
|
||||
#endif
|
||||
#if ENV_INCLUDE_VL53L0X
|
||||
{ TELEM_VL53L0X_ADDRESS, "VL53L0X", init_vl53l0x, query_vl53l0x },
|
||||
#endif
|
||||
#ifdef ENV_INCLUDE_BMP085
|
||||
{ 0x77, "BMP085", init_bmp085, query_bmp085 },
|
||||
#endif
|
||||
#if ENV_INCLUDE_RAK12035
|
||||
{ TELEM_RAK12035_ADDRESS,"RAK12035", init_rak12035, query_rak12035 },
|
||||
#endif
|
||||
{ 0, nullptr, nullptr, nullptr } // sentinel — keeps the array non-empty
|
||||
};
|
||||
|
||||
static const size_t SENSOR_TABLE_SIZE = (sizeof(SENSOR_TABLE) / sizeof(SENSOR_TABLE[0])) - 1;
|
||||
|
||||
// ============================================================
|
||||
// begin() — scan the I2C bus, then initialize only what was
|
||||
// found. A sensor whose address does not ACK during the scan
|
||||
// is never touched by a library call, preventing hangs or
|
||||
// crashes caused by absent or misbehaving hardware.
|
||||
// ============================================================
|
||||
|
||||
bool EnvironmentSensorManager::begin() {
|
||||
#if ENV_INCLUDE_GPS
|
||||
#ifdef RAK_WISBLOCK_GPS
|
||||
rakGPSInit(); //probe base board/sockets for GPS
|
||||
rakGPSInit();
|
||||
#else
|
||||
initBasicGPS();
|
||||
#endif
|
||||
@@ -181,361 +529,52 @@ bool EnvironmentSensorManager::begin() {
|
||||
MESH_DEBUG_PRINTLN("Second I2C initialized on pins SDA: %d SCL: %d", ENV_PIN_SDA, ENV_PIN_SCL);
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_AHTX0
|
||||
if (AHTX0.begin(TELEM_WIRE, 0, TELEM_AHTX_ADDRESS)) {
|
||||
MESH_DEBUG_PRINTLN("Found AHT10/AHT20 at address: %02X", TELEM_AHTX_ADDRESS);
|
||||
AHTX0_initialized = true;
|
||||
} else {
|
||||
AHTX0_initialized = false;
|
||||
MESH_DEBUG_PRINTLN("AHT10/AHT20 was not found at I2C address %02X", TELEM_AHTX_ADDRESS);
|
||||
}
|
||||
#endif
|
||||
// Scan the I2C bus before touching any sensor library.
|
||||
bool detected[128] = {};
|
||||
scanI2CBus(TELEM_WIRE, detected);
|
||||
|
||||
#if ENV_INCLUDE_BME680
|
||||
if (BME680.begin(TELEM_BME680_ADDRESS)) {
|
||||
MESH_DEBUG_PRINTLN("Found BME680 at address: %02X", TELEM_BME680_ADDRESS);
|
||||
BME680_initialized = true;
|
||||
} else {
|
||||
BME680_initialized = false;
|
||||
MESH_DEBUG_PRINTLN("BME680 was not found at I2C address %02X", TELEM_BME680_ADDRESS);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_BME280
|
||||
if (BME280.begin(TELEM_BME280_ADDRESS, TELEM_WIRE)) {
|
||||
MESH_DEBUG_PRINTLN("Found BME280 at address: %02X", TELEM_BME280_ADDRESS);
|
||||
MESH_DEBUG_PRINTLN("BME sensor ID: %02X", BME280.sensorID());
|
||||
// Reduce self-heating: single-shot conversions, light oversampling, long standby.
|
||||
BME280.setSampling(Adafruit_BME280::MODE_FORCED,
|
||||
Adafruit_BME280::SAMPLING_X1, // temperature
|
||||
Adafruit_BME280::SAMPLING_X1, // pressure
|
||||
Adafruit_BME280::SAMPLING_X1, // humidity
|
||||
Adafruit_BME280::FILTER_OFF,
|
||||
Adafruit_BME280::STANDBY_MS_1000);
|
||||
BME280_initialized = true;
|
||||
} else {
|
||||
BME280_initialized = false;
|
||||
MESH_DEBUG_PRINTLN("BME280 was not found at I2C address %02X", TELEM_BME280_ADDRESS);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_BMP280
|
||||
if (BMP280.begin(TELEM_BMP280_ADDRESS)) {
|
||||
MESH_DEBUG_PRINTLN("Found BMP280 at address: %02X", TELEM_BMP280_ADDRESS);
|
||||
MESH_DEBUG_PRINTLN("BMP sensor ID: %02X", BMP280.sensorID());
|
||||
BMP280_initialized = true;
|
||||
} else {
|
||||
BMP280_initialized = false;
|
||||
MESH_DEBUG_PRINTLN("BMP280 was not found at I2C address %02X", TELEM_BMP280_ADDRESS);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_SHTC3
|
||||
if (SHTC3.begin(TELEM_WIRE)) {
|
||||
MESH_DEBUG_PRINTLN("Found sensor: SHTC3");
|
||||
SHTC3_initialized = true;
|
||||
} else {
|
||||
SHTC3_initialized = false;
|
||||
MESH_DEBUG_PRINTLN("SHTC3 was not found at I2C address %02X", 0x70);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if ENV_INCLUDE_SHT4X
|
||||
SHT4X.begin(*TELEM_WIRE, TELEM_SHT4X_ADDRESS);
|
||||
uint32_t serialNumber = 0;
|
||||
int16_t sht4x_error;
|
||||
sht4x_error = SHT4X.serialNumber(serialNumber);
|
||||
if (sht4x_error == 0) {
|
||||
MESH_DEBUG_PRINTLN("Found SHT4X at address: %02X", TELEM_SHT4X_ADDRESS);
|
||||
SHT4X_initialized = true;
|
||||
} else {
|
||||
SHT4X_initialized = false;
|
||||
MESH_DEBUG_PRINTLN("SHT4X was not found at I2C address %02X", TELEM_SHT4X_ADDRESS);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_LPS22HB
|
||||
if (LPS22HB.begin()) {
|
||||
MESH_DEBUG_PRINTLN("Found sensor: LPS22HB");
|
||||
LPS22HB_initialized = true;
|
||||
} else {
|
||||
LPS22HB_initialized = false;
|
||||
MESH_DEBUG_PRINTLN("LPS22HB was not found at I2C address %02X", 0x5C);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_INA3221
|
||||
if (INA3221.begin(TELEM_INA3221_ADDRESS, TELEM_WIRE)) {
|
||||
MESH_DEBUG_PRINTLN("Found INA3221 at address: %02X", TELEM_INA3221_ADDRESS);
|
||||
MESH_DEBUG_PRINTLN("%04X %04X", INA3221.getDieID(), INA3221.getManufacturerID());
|
||||
|
||||
for(int i = 0; i < 3; i++) {
|
||||
INA3221.setShuntResistance(i, TELEM_INA3221_SHUNT_VALUE);
|
||||
// Walk the sensor table and initialize only detected devices.
|
||||
_active_sensor_count = 0;
|
||||
for (size_t i = 0; i < SENSOR_TABLE_SIZE && _active_sensor_count < MAX_ACTIVE_SENSORS; i++) {
|
||||
const SensorDef& def = SENSOR_TABLE[i];
|
||||
if (!detected[def.address]) {
|
||||
MESH_DEBUG_PRINTLN("%s not detected at I2C address %02X", def.name, def.address);
|
||||
continue;
|
||||
}
|
||||
uint8_t n = def.init(TELEM_WIRE, def.address);
|
||||
if (n == 0) {
|
||||
MESH_DEBUG_PRINTLN("%s found at %02X but failed to initialize", def.name, def.address);
|
||||
continue;
|
||||
}
|
||||
MESH_DEBUG_PRINTLN("Found %s at address: %02X", def.name, def.address);
|
||||
for (uint8_t sub = 0; sub < n && _active_sensor_count < MAX_ACTIVE_SENSORS; sub++) {
|
||||
_active_sensors[_active_sensor_count++] = { def.query, sub };
|
||||
}
|
||||
INA3221_initialized = true;
|
||||
} else {
|
||||
INA3221_initialized = false;
|
||||
MESH_DEBUG_PRINTLN("INA3221 was not found at I2C address %02X", TELEM_INA3221_ADDRESS);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_INA219
|
||||
if (INA219.begin(TELEM_WIRE)) {
|
||||
MESH_DEBUG_PRINTLN("Found INA219 at address: %02X", TELEM_INA219_ADDRESS);
|
||||
INA219_initialized = true;
|
||||
} else {
|
||||
INA219_initialized = false;
|
||||
MESH_DEBUG_PRINTLN("INA219 was not found at I2C address %02X", TELEM_INA219_ADDRESS);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_INA260
|
||||
if (INA260.begin(TELEM_INA260_ADDRESS, TELEM_WIRE)) {
|
||||
MESH_DEBUG_PRINTLN("Found INA260 at address: %02X", TELEM_INA260_ADDRESS);
|
||||
INA260_initialized = true;
|
||||
} else {
|
||||
INA260_initialized = false;
|
||||
MESH_DEBUG_PRINTLN("INA260 was not found at I2C address %02X", TELEM_INA260_ADDRESS);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_INA226
|
||||
if (INA226.begin()) {
|
||||
MESH_DEBUG_PRINTLN("Found INA226 at address: %02X", TELEM_INA226_ADDRESS);
|
||||
INA226.setMaxCurrentShunt(TELEM_INA226_MAX_AMP, TELEM_INA226_SHUNT_VALUE);
|
||||
INA226_initialized = true;
|
||||
} else {
|
||||
INA226_initialized = false;
|
||||
MESH_DEBUG_PRINTLN("INA226 was not found at I2C address %02X", TELEM_INA226_ADDRESS);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_MLX90614
|
||||
if (MLX90614.begin(TELEM_MLX90614_ADDRESS, TELEM_WIRE)) {
|
||||
MESH_DEBUG_PRINTLN("Found MLX90614 at address: %02X", TELEM_MLX90614_ADDRESS);
|
||||
MLX90614_initialized = true;
|
||||
} else {
|
||||
MLX90614_initialized = false;
|
||||
MESH_DEBUG_PRINTLN("MLX90614 was not found at I2C address %02X", TELEM_MLX90614_ADDRESS);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_VL53L0X
|
||||
if (VL53L0X.begin(TELEM_VL53L0X_ADDRESS, false, TELEM_WIRE)) {
|
||||
MESH_DEBUG_PRINTLN("Found VL53L0X at address: %02X", TELEM_VL53L0X_ADDRESS);
|
||||
VL53L0X_initialized = true;
|
||||
} else {
|
||||
VL53L0X_initialized = false;
|
||||
MESH_DEBUG_PRINTLN("VL53L0X was not found at I2C address %02X", TELEM_VL53L0X_ADDRESS);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_BMP085
|
||||
// First argument is MODE (aka oversampling)
|
||||
// choose ULTRALOWPOWER
|
||||
if (BMP085.begin(0, TELEM_WIRE)) {
|
||||
MESH_DEBUG_PRINTLN("Found sensor BMP085");
|
||||
BMP085_initialized = true;
|
||||
} else {
|
||||
BMP085_initialized = false;
|
||||
MESH_DEBUG_PRINTLN("BMP085 was not found at I2C address %02X", 0x77);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_RAK12035
|
||||
RAK12035.setup(*TELEM_WIRE);
|
||||
if (RAK12035.begin(TELEM_RAK12035_ADDRESS)) {
|
||||
MESH_DEBUG_PRINTLN("Found sensor RAK12035 at address: %02X", TELEM_RAK12035_ADDRESS);
|
||||
RAK12035_initialized = true;
|
||||
} else {
|
||||
RAK12035_initialized = false;
|
||||
MESH_DEBUG_PRINTLN("RAK12035 was not found at I2C address %02X", TELEM_RAK12035_ADDRESS);
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// querySensors() — GPS stays on channel 1; each active sensor
|
||||
// gets the next available channel in the order it was
|
||||
// initialized.
|
||||
// ============================================================
|
||||
|
||||
bool EnvironmentSensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) {
|
||||
next_available_channel = TELEM_CHANNEL_SELF + 1;
|
||||
|
||||
if (requester_permissions & TELEM_PERM_LOCATION && gps_active) {
|
||||
telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude); // allow lat/lon via telemetry even if no GPS is detected
|
||||
telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude);
|
||||
}
|
||||
|
||||
if (requester_permissions & TELEM_PERM_ENVIRONMENT) {
|
||||
|
||||
#if ENV_INCLUDE_AHTX0
|
||||
if (AHTX0_initialized) {
|
||||
sensors_event_t humidity, temp;
|
||||
AHTX0.getEvent(&humidity, &temp);
|
||||
telemetry.addTemperature(TELEM_CHANNEL_SELF, temp.temperature);
|
||||
telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, humidity.relative_humidity);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_BME680
|
||||
if (BME680_initialized) {
|
||||
if (BME680.performReading()) {
|
||||
telemetry.addTemperature(TELEM_CHANNEL_SELF, BME680.temperature);
|
||||
telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, BME680.humidity);
|
||||
telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, BME680.pressure / 100);
|
||||
telemetry.addAltitude(TELEM_CHANNEL_SELF, 44330.0 * (1.0 - pow((BME680.pressure / 100) / TELEM_BME680_SEALEVELPRESSURE_HPA, 0.1903)));
|
||||
telemetry.addAnalogInput(next_available_channel, BME680.gas_resistance);
|
||||
next_available_channel++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_BME280
|
||||
if (BME280_initialized) {
|
||||
if (BME280.takeForcedMeasurement()) { // trigger a fresh reading in forced mode
|
||||
telemetry.addTemperature(TELEM_CHANNEL_SELF, BME280.readTemperature());
|
||||
telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, BME280.readHumidity());
|
||||
telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, BME280.readPressure()/100);
|
||||
telemetry.addAltitude(TELEM_CHANNEL_SELF, BME280.readAltitude(TELEM_BME280_SEALEVELPRESSURE_HPA));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_BMP280
|
||||
if (BMP280_initialized) {
|
||||
telemetry.addTemperature(TELEM_CHANNEL_SELF, BMP280.readTemperature());
|
||||
telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, BMP280.readPressure()/100);
|
||||
telemetry.addAltitude(TELEM_CHANNEL_SELF, BMP280.readAltitude(TELEM_BMP280_SEALEVELPRESSURE_HPA));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_SHTC3
|
||||
if (SHTC3_initialized) {
|
||||
sensors_event_t humidity, temp;
|
||||
SHTC3.getEvent(&humidity, &temp);
|
||||
|
||||
telemetry.addTemperature(TELEM_CHANNEL_SELF, temp.temperature);
|
||||
telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, humidity.relative_humidity);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_SHT4X
|
||||
if (SHT4X_initialized) {
|
||||
float sht4x_humidity, sht4x_temperature;
|
||||
int16_t sht4x_error;
|
||||
sht4x_error = SHT4X.measureLowestPrecision(sht4x_temperature, sht4x_humidity);
|
||||
if (sht4x_error == 0) {
|
||||
telemetry.addTemperature(TELEM_CHANNEL_SELF, sht4x_temperature);
|
||||
telemetry.addRelativeHumidity(TELEM_CHANNEL_SELF, sht4x_humidity);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_LPS22HB
|
||||
if (LPS22HB_initialized) {
|
||||
telemetry.addTemperature(TELEM_CHANNEL_SELF, LPS22HB.readTemperature());
|
||||
telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, LPS22HB.readPressure() * 10); // convert kPa to hPa
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_INA3221
|
||||
if (INA3221_initialized) {
|
||||
for(int i = 0; i < TELEM_INA3221_NUM_CHANNELS; i++) {
|
||||
// add only enabled INA3221 channels to telemetry
|
||||
if (INA3221.isChannelEnabled(i)) {
|
||||
float voltage = INA3221.getBusVoltage(i);
|
||||
float current = INA3221.getCurrentAmps(i);
|
||||
telemetry.addVoltage(next_available_channel, voltage);
|
||||
telemetry.addCurrent(next_available_channel, current);
|
||||
telemetry.addPower(next_available_channel, voltage * current);
|
||||
next_available_channel++;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_INA219
|
||||
if (INA219_initialized) {
|
||||
telemetry.addVoltage(next_available_channel, INA219.getBusVoltage_V());
|
||||
telemetry.addCurrent(next_available_channel, INA219.getCurrent_mA() / 1000);
|
||||
telemetry.addPower(next_available_channel, INA219.getPower_mW() / 1000);
|
||||
for (int i = 0; i < _active_sensor_count; i++) {
|
||||
_active_sensors[i].query(next_available_channel, _active_sensors[i].sub_channel, telemetry);
|
||||
next_available_channel++;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_INA260
|
||||
if (INA260_initialized) {
|
||||
telemetry.addVoltage(next_available_channel, INA260.readBusVoltage() / 1000);
|
||||
telemetry.addCurrent(next_available_channel, INA260.readCurrent() / 1000);
|
||||
telemetry.addPower(next_available_channel, INA260.readPower() / 1000);
|
||||
next_available_channel++;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_INA226
|
||||
if (INA226_initialized) {
|
||||
telemetry.addVoltage(next_available_channel, INA226.getBusVoltage());
|
||||
telemetry.addCurrent(next_available_channel, INA226.getCurrent_mA() / 1000.0);
|
||||
telemetry.addPower(next_available_channel, INA226.getPower_mW() / 1000.0);
|
||||
next_available_channel++;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_MLX90614
|
||||
if (MLX90614_initialized) {
|
||||
telemetry.addTemperature(TELEM_CHANNEL_SELF, MLX90614.readObjectTempC());
|
||||
telemetry.addTemperature(TELEM_CHANNEL_SELF + 1, MLX90614.readAmbientTempC());
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_VL53L0X
|
||||
if (VL53L0X_initialized) {
|
||||
VL53L0X_RangingMeasurementData_t measure;
|
||||
VL53L0X.rangingTest(&measure, false); // pass in 'true' to get debug data
|
||||
if (measure.RangeStatus != 4) { // phase failures
|
||||
telemetry.addDistance(TELEM_CHANNEL_SELF, measure.RangeMilliMeter / 1000.0f); // convert mm to m
|
||||
} else {
|
||||
telemetry.addDistance(TELEM_CHANNEL_SELF, 0.0f); // no valid measurement
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_BMP085
|
||||
if (BMP085_initialized) {
|
||||
telemetry.addTemperature(TELEM_CHANNEL_SELF, BMP085.readTemperature());
|
||||
telemetry.addBarometricPressure(TELEM_CHANNEL_SELF, BMP085.readPressure() / 100);
|
||||
telemetry.addAltitude(TELEM_CHANNEL_SELF, BMP085.readAltitude(TELEM_BMP085_SEALEVELPRESSURE_HPA * 100));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENV_INCLUDE_RAK12035
|
||||
if (RAK12035_initialized) {
|
||||
|
||||
// RAK12035 Telemetry is Channel 2
|
||||
telemetry.addTemperature(2, RAK12035.get_sensor_temperature());
|
||||
telemetry.addPercentage(2, RAK12035.get_sensor_moisture());
|
||||
|
||||
// RAK12035 CALIBRATION Telemetry is Channel 3, if enabled
|
||||
|
||||
#ifdef ENABLE_RAK12035_CALIBRATION
|
||||
// Calibration Data Screen is Channel 3
|
||||
float cap = RAK12035.get_sensor_capacitance();
|
||||
float _wet = RAK12035.get_humidity_full();
|
||||
float _dry = RAK12035.get_humidity_zero();
|
||||
|
||||
telemetry.addFrequency(3, cap);
|
||||
telemetry.addTemperature(3, _wet);
|
||||
telemetry.addPower(3, _dry);
|
||||
|
||||
if(cap > _dry){
|
||||
RAK12035.set_humidity_zero(cap);
|
||||
}
|
||||
|
||||
if(cap < _wet){
|
||||
RAK12035.set_humidity_full(cap);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -555,8 +594,6 @@ const char* EnvironmentSensorManager::getSettingName(int i) const {
|
||||
return "gps";
|
||||
}
|
||||
#endif
|
||||
// convenient way to add params (needed for some tests)
|
||||
// if (i == settings++) return "param.2";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -567,8 +604,6 @@ const char* EnvironmentSensorManager::getSettingValue(int i) const {
|
||||
return gps_active ? "1" : "0";
|
||||
}
|
||||
#endif
|
||||
// convenient way to add params ...
|
||||
// if (i == settings++) return "2";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -584,11 +619,7 @@ bool EnvironmentSensorManager::setSettingValue(const char* name, const char* val
|
||||
}
|
||||
if (strcmp(name, "gps_interval") == 0) {
|
||||
uint32_t interval_seconds = atoi(value);
|
||||
if (interval_seconds > 0) {
|
||||
gps_update_interval_sec = interval_seconds;
|
||||
} else {
|
||||
gps_update_interval_sec = 1; // Default to 1 second if 0
|
||||
}
|
||||
gps_update_interval_sec = interval_seconds > 0 ? interval_seconds : 1;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -652,16 +683,10 @@ void EnvironmentSensorManager::rakGPSInit(){
|
||||
|
||||
//search for the correct IO standby pin depending on socket used
|
||||
if(gpsIsAwake(WB_IO2)){
|
||||
// MESH_DEBUG_PRINTLN("RAK base board is RAK19007/10");
|
||||
// MESH_DEBUG_PRINTLN("GPS is installed on Socket A");
|
||||
}
|
||||
else if(gpsIsAwake(WB_IO4)){
|
||||
// MESH_DEBUG_PRINTLN("RAK base board is RAK19003/9");
|
||||
// MESH_DEBUG_PRINTLN("GPS is installed on Socket C");
|
||||
}
|
||||
else if(gpsIsAwake(WB_IO5)){
|
||||
// MESH_DEBUG_PRINTLN("RAK base board is RAK19001/11");
|
||||
// MESH_DEBUG_PRINTLN("GPS is installed on Socket F");
|
||||
}
|
||||
else{
|
||||
MESH_DEBUG_PRINTLN("No GPS found");
|
||||
@@ -715,15 +740,17 @@ bool EnvironmentSensorManager::gpsIsAwake(uint8_t ioPin){
|
||||
return true;
|
||||
} else if (Serial1.available()) {
|
||||
MESH_DEBUG_PRINTLN("Serial GPS init correctly and is turned on");
|
||||
#ifdef PIN_GPS_EN
|
||||
if(PIN_GPS_EN){
|
||||
gpsResetPin = PIN_GPS_EN;
|
||||
}
|
||||
#endif
|
||||
serialGPSFlag = true;
|
||||
gps_active = true;
|
||||
gps_detected = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
pinMode(ioPin, INPUT);
|
||||
MESH_DEBUG_PRINTLN("GPS did not init with this IO pin... try the next");
|
||||
return false;
|
||||
|
||||
@@ -6,27 +6,22 @@
|
||||
|
||||
class EnvironmentSensorManager : public SensorManager {
|
||||
protected:
|
||||
int next_available_channel = TELEM_CHANNEL_SELF + 1;
|
||||
static const int MAX_ACTIVE_SENSORS = 16;
|
||||
|
||||
bool AHTX0_initialized = false;
|
||||
bool BME280_initialized = false;
|
||||
bool BMP280_initialized = false;
|
||||
bool INA3221_initialized = false;
|
||||
bool INA219_initialized = false;
|
||||
bool INA260_initialized = false;
|
||||
bool INA226_initialized = false;
|
||||
bool SHTC3_initialized = false;
|
||||
bool LPS22HB_initialized = false;
|
||||
bool MLX90614_initialized = false;
|
||||
bool VL53L0X_initialized = false;
|
||||
bool SHT4X_initialized = false;
|
||||
bool BME680_initialized = false;
|
||||
bool BMP085_initialized = false;
|
||||
bool RAK12035_initialized = false;
|
||||
// Query function pointer + sub-channel index (for multi-channel sensors like INA3221).
|
||||
// Sub-channel is 0 for all single-output sensors.
|
||||
struct ActiveSensor {
|
||||
void (*query)(uint8_t channel, uint8_t sub_channel, CayenneLPP& telemetry);
|
||||
uint8_t sub_channel;
|
||||
};
|
||||
|
||||
bool gps_detected = false;
|
||||
bool gps_active = false;
|
||||
uint32_t gps_update_interval_sec = 1; // Default 1 second
|
||||
ActiveSensor _active_sensors[MAX_ACTIVE_SENSORS];
|
||||
int _active_sensor_count = 0;
|
||||
uint8_t next_available_channel = TELEM_CHANNEL_SELF + 1;
|
||||
|
||||
bool gps_detected = false;
|
||||
bool gps_active = false;
|
||||
uint32_t gps_update_interval_sec = 1;
|
||||
|
||||
#if ENV_INCLUDE_GPS
|
||||
LocationProvider* _location;
|
||||
@@ -39,7 +34,6 @@ protected:
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
public:
|
||||
#if ENV_INCLUDE_GPS
|
||||
EnvironmentSensorManager(LocationProvider &location): _location(&location){};
|
||||
|
||||
@@ -431,7 +431,7 @@ bool RAK12035_SoilMoisture::sensor_on()
|
||||
delay(10); // Wait for the sensor code to complete initialization
|
||||
*/
|
||||
uint8_t v = 0;
|
||||
time_t timeout = millis();
|
||||
uint32_t timeout = millis();
|
||||
while ((!query_sensor())) //Wait for sensor to respond to I2C commands,
|
||||
{ //indicating it is ready
|
||||
if ((millis() - timeout) > 50){ //0.5 second timeout for sensor to respond
|
||||
|
||||
@@ -28,7 +28,10 @@ bool ST7735Display::begin() {
|
||||
#endif
|
||||
digitalWrite(PIN_TFT_RST, HIGH);
|
||||
|
||||
#if defined(HELTEC_TRACKER_V2) || defined(HELTEC_T096)
|
||||
#if defined(HELTEC_T1)
|
||||
display.initR(INITR_MINI160x80);
|
||||
display.setRotation(DISPLAY_ROTATION);
|
||||
#elif defined(HELTEC_TRACKER_V2) || defined(HELTEC_T096)
|
||||
display.initR(INITR_MINI160x80);
|
||||
display.setRotation(DISPLAY_ROTATION);
|
||||
uint8_t madctl = ST77XX_MADCTL_MY | ST77XX_MADCTL_MV |ST7735_MADCTL_BGR;//Adjust color to BGR
|
||||
|
||||
@@ -27,12 +27,6 @@ bool ST7789LCDDisplay::begin() {
|
||||
pinMode(PIN_TFT_LEDA_CTL, OUTPUT);
|
||||
digitalWrite(PIN_TFT_LEDA_CTL, HIGH);
|
||||
}
|
||||
if (PIN_TFT_RST != -1) {
|
||||
pinMode(PIN_TFT_RST, OUTPUT);
|
||||
digitalWrite(PIN_TFT_RST, LOW);
|
||||
delay(10);
|
||||
digitalWrite(PIN_TFT_RST, HIGH);
|
||||
}
|
||||
|
||||
// Im not sure if this is just a t-deck problem or not, if your display is slow try this.
|
||||
#if defined(LILYGO_TDECK) || defined(HELTEC_LORA_V4_TFT)
|
||||
@@ -166,4 +160,4 @@ uint16_t ST7789LCDDisplay::getTextWidth(const char* str) {
|
||||
|
||||
void ST7789LCDDisplay::endFrame() {
|
||||
// display.display();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "Arduino.h"
|
||||
#ifdef PIN_BUZZER
|
||||
#include "buzzer.h"
|
||||
|
||||
@@ -12,7 +13,6 @@ void genericBuzzer::begin() {
|
||||
quiet(false);
|
||||
pinMode(PIN_BUZZER, OUTPUT);
|
||||
digitalWrite(PIN_BUZZER, LOW); // need to pull low by default to avoid extreme power draw
|
||||
startup();
|
||||
}
|
||||
|
||||
void genericBuzzer::play(const char *melody) {
|
||||
|
||||
Reference in New Issue
Block a user