diff --git a/platformio.ini b/platformio.ini
index e8eff024d..85bce3279 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -139,3 +139,5 @@ lib_deps =
adafruit/Adafruit TSL2591 Library@^1.4.5
ClosedCube OPT3001@^1.1.2
emotibit/EmotiBit MLX90632@^1.0.8
+ dfrobot/DFRobot_RTU@^1.0.3
+ https://github.com/meshtastic/DFRobot_LarkWeatherStation#0e884fc86b7a0b602c7ff3d26b893b997f15c6ac
\ No newline at end of file
diff --git a/src/configuration.h b/src/configuration.h
index 744406f18..462210cf2 100644
--- a/src/configuration.h
+++ b/src/configuration.h
@@ -135,6 +135,7 @@ along with this program. If not, see .
#define OPT3001_ADDR 0x45
#define OPT3001_ADDR_ALT 0x44
#define MLX90632_ADDR 0x3A
+#define DFROBOT_LARK_ADDR 0x42
// -----------------------------------------------------------------------------
// ACCELEROMETER
diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h
index a90d9218a..13dd66763 100644
--- a/src/detect/ScanI2C.h
+++ b/src/detect/ScanI2C.h
@@ -49,6 +49,7 @@ class ScanI2C
OPT3001,
MLX90632,
AHT10,
+ DFROBOT_LARK,
} DeviceType;
// typedef uint8_t DeviceAddress;
diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp
index 3aeb8560e..86099ad19 100644
--- a/src/detect/ScanI2CTwoWire.cpp
+++ b/src/detect/ScanI2CTwoWire.cpp
@@ -281,8 +281,9 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
if (registerValue == 0x5449) {
LOG_INFO("INA3221 sensor found at address 0x%x\n", (uint8_t)addr.address);
type = INA3221;
- } else { // Unknown device
- LOG_INFO("No INA3221 found at address 0x%x\n", (uint8_t)addr.address);
+ } else {
+ LOG_INFO("DFRobot Lark weather station found at address 0x%x\n", (uint8_t)addr.address);
+ type = DFROBOT_LARK;
}
break;
case MCP9808_ADDR:
diff --git a/src/gps/GeoCoord.cpp b/src/gps/GeoCoord.cpp
index cb4e69ff2..2224bd281 100644
--- a/src/gps/GeoCoord.cpp
+++ b/src/gps/GeoCoord.cpp
@@ -486,3 +486,91 @@ std::shared_ptr GeoCoord::pointAtDistance(double bearing, double range
return std::make_shared(double(lat), double(lon), this->getAltitude());
}
+
+/**
+ * Convert bearing to degrees
+ * @param bearing
+ * The bearing in string format
+ * @return Bearing in degrees
+ */
+uint GeoCoord::bearingToDegrees(const char *bearing)
+{
+ if (strcmp(bearing, "N") == 0)
+ return 0;
+ else if (strcmp(bearing, "NNE") == 0)
+ return 22;
+ else if (strcmp(bearing, "NE") == 0)
+ return 45;
+ else if (strcmp(bearing, "ENE") == 0)
+ return 67;
+ else if (strcmp(bearing, "E") == 0)
+ return 90;
+ else if (strcmp(bearing, "ESE") == 0)
+ return 112;
+ else if (strcmp(bearing, "SE") == 0)
+ return 135;
+ else if (strcmp(bearing, "SSE") == 0)
+ return 157;
+ else if (strcmp(bearing, "S") == 0)
+ return 180;
+ else if (strcmp(bearing, "SSW") == 0)
+ return 202;
+ else if (strcmp(bearing, "SW") == 0)
+ return 225;
+ else if (strcmp(bearing, "WSW") == 0)
+ return 247;
+ else if (strcmp(bearing, "W") == 0)
+ return 270;
+ else if (strcmp(bearing, "WNW") == 0)
+ return 292;
+ else if (strcmp(bearing, "NW") == 0)
+ return 315;
+ else if (strcmp(bearing, "NNW") == 0)
+ return 337;
+ else
+ return 0;
+}
+
+/**
+ * Convert bearing to string
+ * @param degrees
+ * The bearing in degrees
+ * @return Bearing in string format
+ */
+const char *GeoCoord::degreesToBearing(uint degrees)
+{
+ if (degrees >= 348 || degrees < 11)
+ return "N";
+ else if (degrees >= 11 && degrees < 34)
+ return "NNE";
+ else if (degrees >= 34 && degrees < 56)
+ return "NE";
+ else if (degrees >= 56 && degrees < 79)
+ return "ENE";
+ else if (degrees >= 79 && degrees < 101)
+ return "E";
+ else if (degrees >= 101 && degrees < 124)
+ return "ESE";
+ else if (degrees >= 124 && degrees < 146)
+ return "SE";
+ else if (degrees >= 146 && degrees < 169)
+ return "SSE";
+ else if (degrees >= 169 && degrees < 191)
+ return "S";
+ else if (degrees >= 191 && degrees < 214)
+ return "SSW";
+ else if (degrees >= 214 && degrees < 236)
+ return "SW";
+ else if (degrees >= 236 && degrees < 259)
+ return "WSW";
+ else if (degrees >= 259 && degrees < 281)
+ return "W";
+ else if (degrees >= 281 && degrees < 304)
+ return "WNW";
+ else if (degrees >= 304 && degrees < 326)
+ return "NW";
+ else if (degrees >= 326 && degrees < 348)
+ return "NNW";
+ else
+ return "N";
+}
diff --git a/src/gps/GeoCoord.h b/src/gps/GeoCoord.h
index e811035db..b02d12afb 100644
--- a/src/gps/GeoCoord.h
+++ b/src/gps/GeoCoord.h
@@ -117,6 +117,8 @@ class GeoCoord
static float bearing(double lat1, double lon1, double lat2, double lon2);
static float rangeRadiansToMeters(double range_radians);
static float rangeMetersToRadians(double range_meters);
+ static uint bearingToDegrees(const char *bearing);
+ static const char *degreesToBearing(uint degrees);
// Point to point conversions
int32_t distanceTo(const GeoCoord &pointB);
diff --git a/src/main.cpp b/src/main.cpp
index 52de93e83..6797c8375 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -540,6 +540,7 @@ void setup()
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::MLX90632, meshtastic_TelemetrySensorType_MLX90632)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::SHT4X, meshtastic_TelemetrySensorType_SHT4X)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::AHT10, meshtastic_TelemetrySensorType_AHT10)
+ SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::DFROBOT_LARK, meshtastic_TelemetrySensorType_DFROBOT_LARK)
i2cScanner.reset();
diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp
index 9032389c5..8972a8e3f 100644
--- a/src/modules/Telemetry/EnvironmentTelemetry.cpp
+++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp
@@ -23,6 +23,7 @@
#include "Sensor/BME680Sensor.h"
#include "Sensor/BMP085Sensor.h"
#include "Sensor/BMP280Sensor.h"
+#include "Sensor/DFRobotLarkSensor.h"
#include "Sensor/LPS22HBSensor.h"
#include "Sensor/MCP9808Sensor.h"
#include "Sensor/MLX90632Sensor.h"
@@ -49,6 +50,7 @@ SHT4XSensor sht4xSensor;
RCWL9620Sensor rcwl9620Sensor;
AHT10Sensor aht10Sensor;
MLX90632Sensor mlx90632Sensor;
+DFRobotLarkSensor dfRobotLarkSensor;
#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
@@ -72,7 +74,7 @@ int32_t EnvironmentTelemetryModule::runOnce()
// moduleConfig.telemetry.environment_measurement_enabled = 1;
// moduleConfig.telemetry.environment_screen_enabled = 1;
- // moduleConfig.telemetry.environment_update_interval = 45;
+ // moduleConfig.telemetry.environment_update_interval = 15;
if (!(moduleConfig.telemetry.environment_measurement_enabled || moduleConfig.telemetry.environment_screen_enabled)) {
// If this module is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it
@@ -87,6 +89,8 @@ int32_t EnvironmentTelemetryModule::runOnce()
LOG_INFO("Environment Telemetry: Initializing\n");
// it's possible to have this module enabled, only for displaying values on the screen.
// therefore, we should only enable the sensor loop if measurement is also enabled
+ if (dfRobotLarkSensor.hasSensor())
+ result = dfRobotLarkSensor.runOnce();
if (bmp085Sensor.hasSensor())
result = bmp085Sensor.runOnce();
if (bmp280Sensor.hasSensor())
@@ -240,6 +244,10 @@ bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPac
t->variant.environment_metrics.temperature);
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);
+
#endif
// release previous packet before occupying a new spot
if (lastMeasurementPacket != nullptr)
@@ -259,6 +267,10 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
m.time = getTime();
m.which_variant = meshtastic_Telemetry_environment_metrics_tag;
+ if (dfRobotLarkSensor.hasSensor()) {
+ valid = valid && dfRobotLarkSensor.getMetrics(&m);
+ hasSensor = true;
+ }
if (sht31Sensor.hasSensor()) {
valid = valid && sht31Sensor.getMetrics(&m);
hasSensor = true;
@@ -342,6 +354,9 @@ 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);
+
sensor_read_error_count = 0;
meshtastic_MeshPacket *p = allocDataProtobuf(m);
diff --git a/src/modules/Telemetry/Sensor/DFRobotLarkSensor.cpp b/src/modules/Telemetry/Sensor/DFRobotLarkSensor.cpp
new file mode 100644
index 000000000..830552023
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/DFRobotLarkSensor.cpp
@@ -0,0 +1,53 @@
+#include "configuration.h"
+
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
+
+#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "DFRobotLarkSensor.h"
+#include "TelemetrySensor.h"
+#include "gps/GeoCoord.h"
+#include
+#include
+
+DFRobotLarkSensor::DFRobotLarkSensor() : TelemetrySensor(meshtastic_TelemetrySensorType_DFROBOT_LARK, "DFROBOT_LARK") {}
+
+int32_t DFRobotLarkSensor::runOnce()
+{
+ LOG_INFO("Init sensor: %s\n", sensorName);
+ if (!hasSensor()) {
+ return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
+ }
+
+ lark = DFRobot_LarkWeatherStation_I2C(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second);
+
+ if (lark.begin() == 0) // DFRobotLarkSensor init
+ {
+ LOG_DEBUG("DFRobotLarkSensor Init Succeed\n");
+ status = true;
+ } else {
+ LOG_ERROR("DFRobotLarkSensor Init Failed\n");
+ status = false;
+ }
+ return initI2CSensor();
+}
+
+void DFRobotLarkSensor::setup() {}
+
+bool DFRobotLarkSensor::getMetrics(meshtastic_Telemetry *measurement)
+{
+ measurement->variant.environment_metrics.temperature = lark.getValue("Temp").toFloat();
+ measurement->variant.environment_metrics.relative_humidity = lark.getValue("Humi").toFloat();
+ measurement->variant.environment_metrics.wind_speed = lark.getValue("Speed").toFloat();
+ measurement->variant.environment_metrics.wind_direction = GeoCoord::bearingToDegrees(lark.getValue("Dir").c_str());
+ measurement->variant.environment_metrics.barometric_pressure = lark.getValue("Pressure").toFloat();
+
+ LOG_INFO("Temperature: %f\n", measurement->variant.environment_metrics.temperature);
+ LOG_INFO("Humidity: %f\n", measurement->variant.environment_metrics.relative_humidity);
+ LOG_INFO("Wind Speed: %f\n", measurement->variant.environment_metrics.wind_speed);
+ LOG_INFO("Wind Direction: %d\n", measurement->variant.environment_metrics.wind_direction);
+ LOG_INFO("Barometric Pressure: %f\n", measurement->variant.environment_metrics.barometric_pressure);
+
+ return true;
+}
+
+#endif
\ No newline at end of file
diff --git a/src/modules/Telemetry/Sensor/DFRobotLarkSensor.h b/src/modules/Telemetry/Sensor/DFRobotLarkSensor.h
new file mode 100644
index 000000000..b26d690b1
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/DFRobotLarkSensor.h
@@ -0,0 +1,24 @@
+#include "configuration.h"
+
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
+
+#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "TelemetrySensor.h"
+#include
+#include
+
+class DFRobotLarkSensor : public TelemetrySensor
+{
+ private:
+ DFRobot_LarkWeatherStation_I2C lark = DFRobot_LarkWeatherStation_I2C();
+
+ protected:
+ virtual void setup() override;
+
+ public:
+ DFRobotLarkSensor();
+ virtual int32_t runOnce() override;
+ virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
+};
+
+#endif
\ No newline at end of file