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