diff --git a/platformio.ini b/platformio.ini
index 0de3e25c9..d8d398775 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -122,10 +122,7 @@ lib_deps =
adafruit/Adafruit BMP280 Library@^2.6.8
adafruit/Adafruit BMP085 Library@^1.2.4
adafruit/Adafruit BME280 Library@^2.2.2
- https://github.com/boschsensortec/Bosch-BSEC2-Library#v1.7.2502
- boschsensortec/BME68x Sensor Library@^1.1.40407
adafruit/Adafruit MCP9808 Library@^2.0.0
- https://github.com/KodinLanewave/INA3221@^1.0.0
adafruit/Adafruit INA260 Library@^1.5.0
adafruit/Adafruit INA219@^1.2.0
adafruit/Adafruit SHTC3 Library@^1.0.0
@@ -135,13 +132,22 @@ lib_deps =
adafruit/Adafruit MPU6050@^2.2.4
adafruit/Adafruit LIS3DH@^1.2.4
adafruit/Adafruit AHTX0@^2.0.5
- lewisxhe/SensorLib@^0.2.0
adafruit/Adafruit LSM6DS@^4.7.2
- mprograms/QMC5883LCompass@^1.2.0
adafruit/Adafruit VEML7700 Library@^2.1.6
adafruit/Adafruit SHT4x Library@^1.0.4
adafruit/Adafruit TSL2591 Library@^1.4.5
+ sparkfun/SparkFun Qwiic Scale NAU7802 Arduino Library@^1.0.5
ClosedCube OPT3001@^1.1.2
emotibit/EmotiBit MLX90632@^1.0.8
dfrobot/DFRobot_RTU@^1.0.3
- https://github.com/meshtastic/DFRobot_LarkWeatherStation#dee914270dc7cb3e43fbf034edd85a63a16a12ee
\ No newline at end of file
+
+
+ https://github.com/boschsensortec/Bosch-BSEC2-Library#v1.7.2502
+ boschsensortec/BME68x Sensor Library@^1.1.40407
+ https://github.com/KodinLanewave/INA3221@^1.0.0
+ lewisxhe/SensorLib@^0.2.0
+ mprograms/QMC5883LCompass@^1.2.0
+
+
+ https://github.com/meshtastic/DFRobot_LarkWeatherStation#dee914270dc7cb3e43fbf034edd85a63a16a12ee
+
diff --git a/src/configuration.h b/src/configuration.h
index 3703db3e3..1f07e7009 100644
--- a/src/configuration.h
+++ b/src/configuration.h
@@ -136,6 +136,7 @@ along with this program. If not, see .
#define OPT3001_ADDR_ALT 0x44
#define MLX90632_ADDR 0x3A
#define DFROBOT_LARK_ADDR 0x42
+#define NAU7802_ADDR 0x2A
// -----------------------------------------------------------------------------
// ACCELEROMETER
diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h
index 20994ede1..dcc1f40ae 100644
--- a/src/detect/ScanI2C.h
+++ b/src/detect/ScanI2C.h
@@ -50,7 +50,8 @@ class ScanI2C
MLX90632,
AHT10,
BMX160,
- DFROBOT_LARK
+ DFROBOT_LARK,
+ NAU7802
} DeviceType;
// typedef uint8_t DeviceAddress;
diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp
index 3dc51bf1f..2ad321204 100644
--- a/src/detect/ScanI2CTwoWire.cpp
+++ b/src/detect/ScanI2CTwoWire.cpp
@@ -350,6 +350,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
SCAN_SIMPLE_CASE(TSL25911_ADDR, TSL2591, "TSL2591 light sensor found\n");
SCAN_SIMPLE_CASE(OPT3001_ADDR, OPT3001, "OPT3001 light sensor found\n");
SCAN_SIMPLE_CASE(MLX90632_ADDR, MLX90632, "MLX90632 IR temp sensor found\n");
+ SCAN_SIMPLE_CASE(NAU7802_ADDR, NAU7802, "NAU7802 based scale found\n");
default:
LOG_INFO("Device found at address 0x%x was not able to be enumerated\n", addr.address);
diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp
index 46b8a1ad8..ff3202067 100644
--- a/src/modules/Telemetry/EnvironmentTelemetry.cpp
+++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp
@@ -27,6 +27,7 @@
#include "Sensor/LPS22HBSensor.h"
#include "Sensor/MCP9808Sensor.h"
#include "Sensor/MLX90632Sensor.h"
+#include "Sensor/NAU7802Sensor.h"
#include "Sensor/OPT3001Sensor.h"
#include "Sensor/RCWL9620Sensor.h"
#include "Sensor/SHT31Sensor.h"
@@ -51,6 +52,7 @@ RCWL9620Sensor rcwl9620Sensor;
AHT10Sensor aht10Sensor;
MLX90632Sensor mlx90632Sensor;
DFRobotLarkSensor dfRobotLarkSensor;
+NAU7802Sensor nau7802Sensor;
#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
@@ -125,6 +127,8 @@ int32_t EnvironmentTelemetryModule::runOnce()
result = aht10Sensor.runOnce();
if (mlx90632Sensor.hasSensor())
result = mlx90632Sensor.runOnce();
+ if (nau7802Sensor.hasSensor())
+ result = nau7802Sensor.runOnce();
}
return result;
} else {
@@ -223,12 +227,18 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt
"Volt/Cur: " + String(lastMeasurement.variant.environment_metrics.voltage, 0) + "V / " +
String(lastMeasurement.variant.environment_metrics.current, 0) + "mA");
}
+
if (lastMeasurement.variant.environment_metrics.iaq != 0) {
display->drawString(x, y += fontHeight(FONT_SMALL), "IAQ: " + String(lastMeasurement.variant.environment_metrics.iaq));
}
+
if (lastMeasurement.variant.environment_metrics.distance != 0)
display->drawString(x, y += fontHeight(FONT_SMALL),
"Water Level: " + String(lastMeasurement.variant.environment_metrics.distance, 0) + "mm");
+
+ if (lastMeasurement.variant.environment_metrics.weight != 0)
+ display->drawString(x, y += fontHeight(FONT_SMALL),
+ "Weight: " + String(lastMeasurement.variant.environment_metrics.weight, 0) + "kg");
}
bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t)
@@ -245,8 +255,9 @@ bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPac
LOG_INFO("(Received from %s): voltage=%f, IAQ=%d, distance=%f, lux=%f\n", sender, t->variant.environment_metrics.voltage,
t->variant.environment_metrics.iaq, t->variant.environment_metrics.distance, t->variant.environment_metrics.lux);
- LOG_INFO("(Received from %s): wind speed=%fm/s, direction=%d degrees\n", sender,
- t->variant.environment_metrics.wind_speed, t->variant.environment_metrics.wind_direction);
+ LOG_INFO("(Received from %s): wind speed=%fm/s, direction=%d degrees, weight=%fkg\n", sender,
+ t->variant.environment_metrics.wind_speed, t->variant.environment_metrics.wind_direction,
+ t->variant.environment_metrics.weight);
#endif
// release previous packet before occupying a new spot
@@ -331,6 +342,10 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
valid = valid && rcwl9620Sensor.getMetrics(&m);
hasSensor = true;
}
+ if (nau7802Sensor.hasSensor()) {
+ valid = valid && nau7802Sensor.getMetrics(&m);
+ hasSensor = true;
+ }
if (aht10Sensor.hasSensor()) {
if (!bmp280Sensor.hasSensor()) {
valid = valid && aht10Sensor.getMetrics(&m);
@@ -354,8 +369,8 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
LOG_INFO("(Sending): voltage=%f, IAQ=%d, distance=%f, lux=%f\n", m.variant.environment_metrics.voltage,
m.variant.environment_metrics.iaq, m.variant.environment_metrics.distance, m.variant.environment_metrics.lux);
- LOG_INFO("(Sending): wind speed=%fm/s, direction=%d degrees\n", m.variant.environment_metrics.wind_speed,
- m.variant.environment_metrics.wind_direction);
+ LOG_INFO("(Sending): wind speed=%fm/s, direction=%d degrees, weight=%fkg\n", m.variant.environment_metrics.wind_speed,
+ m.variant.environment_metrics.wind_direction, m.variant.environment_metrics.weight);
sensor_read_error_count = 0;
@@ -388,4 +403,102 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
return valid;
}
+AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule(const meshtastic_MeshPacket &mp,
+ meshtastic_AdminMessage *request,
+ meshtastic_AdminMessage *response)
+{
+ AdminMessageHandleResult result = AdminMessageHandleResult::NOT_HANDLED;
+ if (dfRobotLarkSensor.hasSensor()) {
+ result = dfRobotLarkSensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (sht31Sensor.hasSensor()) {
+ result = sht31Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (lps22hbSensor.hasSensor()) {
+ result = lps22hbSensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (shtc3Sensor.hasSensor()) {
+ result = shtc3Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (bmp085Sensor.hasSensor()) {
+ result = bmp085Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (bmp280Sensor.hasSensor()) {
+ result = bmp280Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (bme280Sensor.hasSensor()) {
+ result = bme280Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (bme680Sensor.hasSensor()) {
+ result = bme680Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (mcp9808Sensor.hasSensor()) {
+ result = mcp9808Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (ina219Sensor.hasSensor()) {
+ result = ina219Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (ina260Sensor.hasSensor()) {
+ result = ina260Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (veml7700Sensor.hasSensor()) {
+ result = veml7700Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (tsl2591Sensor.hasSensor()) {
+ result = tsl2591Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (opt3001Sensor.hasSensor()) {
+ result = opt3001Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (mlx90632Sensor.hasSensor()) {
+ result = mlx90632Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (rcwl9620Sensor.hasSensor()) {
+ result = rcwl9620Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (nau7802Sensor.hasSensor()) {
+ result = nau7802Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ if (aht10Sensor.hasSensor()) {
+ result = aht10Sensor.handleAdminMessage(mp, request, response);
+ if (result != AdminMessageHandleResult::NOT_HANDLED)
+ return result;
+ }
+ return result;
+}
+
#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/EnvironmentTelemetry.h b/src/modules/Telemetry/EnvironmentTelemetry.h
index cdd9491d4..ca150347e 100644
--- a/src/modules/Telemetry/EnvironmentTelemetry.h
+++ b/src/modules/Telemetry/EnvironmentTelemetry.h
@@ -37,6 +37,10 @@ class EnvironmentTelemetryModule : private concurrency::OSThread, public Protobu
*/
bool sendTelemetry(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
+ virtual AdminMessageHandleResult handleAdminMessageForModule(const meshtastic_MeshPacket &mp,
+ meshtastic_AdminMessage *request,
+ meshtastic_AdminMessage *response) override;
+
private:
float CelsiusToFahrenheit(float c);
bool firstTime = 1;
diff --git a/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp b/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp
new file mode 100644
index 000000000..39ac4b08b
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/NAU7802Sensor.cpp
@@ -0,0 +1,143 @@
+#include "configuration.h"
+
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
+
+#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "FSCommon.h"
+#include "NAU7802Sensor.h"
+#include "TelemetrySensor.h"
+#include
+#include
+
+meshtastic_Nau7802Config nau7802config = meshtastic_Nau7802Config_init_zero;
+
+NAU7802Sensor::NAU7802Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_NAU7802, "NAU7802") {}
+
+int32_t NAU7802Sensor::runOnce()
+{
+ LOG_INFO("Init sensor: %s\n", sensorName);
+ if (!hasSensor()) {
+ return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
+ }
+ status = nau7802.begin(*nodeTelemetrySensorsMap[sensorType].second);
+ nau7802.setSampleRate(NAU7802_SPS_320);
+ if (!loadCalibrationData()) {
+ LOG_ERROR("Failed to load calibration data\n");
+ }
+ nau7802.calibrateAFE();
+ LOG_INFO("Offset: %d, Calibration factor: %.2f\n", nau7802.getZeroOffset(), nau7802.getCalibrationFactor());
+ return initI2CSensor();
+}
+
+void NAU7802Sensor::setup() {}
+
+bool NAU7802Sensor::getMetrics(meshtastic_Telemetry *measurement)
+{
+ LOG_DEBUG("NAU7802Sensor::getMetrics\n");
+ nau7802.powerUp();
+ // Wait for the sensor to become ready for one second max
+ uint32_t start = millis();
+ while (!nau7802.available()) {
+ delay(100);
+ if (millis() - start > 1000) {
+ nau7802.powerDown();
+ return false;
+ }
+ }
+ // Check if we have correct calibration values after powerup
+ LOG_DEBUG("Offset: %d, Calibration factor: %.2f\n", 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\n");
+ }
+ LOG_INFO("Offset: %d, Calibration factor: %.2f\n", nau7802.getZeroOffset(), nau7802.getCalibrationFactor());
+}
+
+AdminMessageHandleResult NAU7802Sensor::handleAdminMessage(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request,
+ 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\n");
+ } else {
+ this->calibrate(request->set_scale);
+ LOG_DEBUG("Client requested to calibrate to %d kg\n", 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\n");
+ }
+ LOG_INFO("Offset: %d, Calibration factor: %.2f\n", nau7802.getZeroOffset(), nau7802.getCalibrationFactor());
+}
+
+bool NAU7802Sensor::saveCalibrationData()
+{
+ if (FSCom.exists(nau7802ConfigFileName) && !FSCom.remove(nau7802ConfigFileName)) {
+ LOG_WARN("Can't remove old state file\n");
+ }
+ auto file = FSCom.open(nau7802ConfigFileName, FILE_O_WRITE);
+ nau7802config.zeroOffset = nau7802.getZeroOffset();
+ nau7802config.calibrationFactor = nau7802.getCalibrationFactor();
+ bool okay = false;
+ if (file) {
+ LOG_INFO("%s state write to %s.\n", sensorName, nau7802ConfigFileName);
+ pb_ostream_t stream = {&writecb, &file, meshtastic_Nau7802Config_size};
+
+ if (!pb_encode(&stream, &meshtastic_Nau7802Config_msg, &nau7802config)) {
+ LOG_ERROR("Error: can't encode protobuf %s\n", PB_GET_ERROR(&stream));
+ } else {
+ okay = true;
+ }
+ file.flush();
+ file.close();
+ } else {
+ LOG_INFO("Can't write %s state (File: %s).\n", sensorName, nau7802ConfigFileName);
+ }
+ return okay;
+}
+
+bool NAU7802Sensor::loadCalibrationData()
+{
+ auto file = FSCom.open(nau7802ConfigFileName, FILE_O_READ);
+ bool okay = false;
+ if (file) {
+ LOG_INFO("%s state read from %s.\n", 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\n", PB_GET_ERROR(&stream));
+ } else {
+ nau7802.setZeroOffset(nau7802config.zeroOffset);
+ nau7802.setCalibrationFactor(nau7802config.calibrationFactor);
+ okay = true;
+ }
+ file.close();
+ } else {
+ LOG_INFO("No %s state found (File: %s).\n", sensorName, nau7802ConfigFileName);
+ }
+ return okay;
+}
+
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/NAU7802Sensor.h b/src/modules/Telemetry/Sensor/NAU7802Sensor.h
new file mode 100644
index 000000000..c53a3b31a
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/NAU7802Sensor.h
@@ -0,0 +1,31 @@
+#include "MeshModule.h"
+#include "configuration.h"
+
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
+
+#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "TelemetrySensor.h"
+#include
+
+class NAU7802Sensor : public TelemetrySensor
+{
+ private:
+ NAU7802 nau7802;
+
+ protected:
+ virtual void setup() override;
+ const char *nau7802ConfigFileName = "/prefs/nau7802.dat";
+ bool saveCalibrationData();
+ bool loadCalibrationData();
+
+ public:
+ NAU7802Sensor();
+ virtual int32_t runOnce() override;
+ virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
+ void tare();
+ void calibrate(float weight);
+ AdminMessageHandleResult handleAdminMessage(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request,
+ meshtastic_AdminMessage *response) override;
+};
+
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/TelemetrySensor.h b/src/modules/Telemetry/Sensor/TelemetrySensor.h
index 35cb7965d..da376ad31 100644
--- a/src/modules/Telemetry/Sensor/TelemetrySensor.h
+++ b/src/modules/Telemetry/Sensor/TelemetrySensor.h
@@ -4,6 +4,7 @@
#pragma once
#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "MeshModule.h"
#include "NodeDB.h"
#include
@@ -42,6 +43,12 @@ class TelemetrySensor
virtual void setup();
public:
+ virtual AdminMessageHandleResult handleAdminMessage(const meshtastic_MeshPacket &mp, meshtastic_AdminMessage *request,
+ meshtastic_AdminMessage *response)
+ {
+ return AdminMessageHandleResult::NOT_HANDLED;
+ }
+
bool hasSensor() { return nodeTelemetrySensorsMap[sensorType].first > 0; }
virtual int32_t runOnce() = 0;