From 2a49811f6e81c598c1e10c005b66940e35cb17bb Mon Sep 17 00:00:00 2001 From: piechade Date: Thu, 28 Jul 2022 01:22:49 +0200 Subject: [PATCH] Add support for SMT100 Soil Moisture Sensor (#3654) Co-authored-by: Dennis Piecha --- CODEOWNERS | 1 + esphome/components/smt100/__init__.py | 1 + esphome/components/smt100/sensor.py | 94 +++++++++++++++++++++++++++ esphome/components/smt100/smt100.cpp | 84 ++++++++++++++++++++++++ esphome/components/smt100/smt100.h | 43 ++++++++++++ esphome/const.py | 2 + tests/test3.yaml | 17 +++++ 7 files changed, 242 insertions(+) create mode 100644 esphome/components/smt100/__init__.py create mode 100644 esphome/components/smt100/sensor.py create mode 100644 esphome/components/smt100/smt100.cpp create mode 100644 esphome/components/smt100/smt100.h diff --git a/CODEOWNERS b/CODEOWNERS index 9237ff813c..770c1fa7de 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -189,6 +189,7 @@ esphome/components/shutdown/* @esphome/core @jsuanet esphome/components/sim800l/* @glmnet esphome/components/sm2135/* @BoukeHaarsma23 esphome/components/sml/* @alengwenus +esphome/components/smt100/* @piechade esphome/components/socket/* @esphome/core esphome/components/sonoff_d1/* @anatoly-savchenkov esphome/components/spi/* @esphome/core diff --git a/esphome/components/smt100/__init__.py b/esphome/components/smt100/__init__.py new file mode 100644 index 0000000000..44ed591400 --- /dev/null +++ b/esphome/components/smt100/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@piechade"] diff --git a/esphome/components/smt100/sensor.py b/esphome/components/smt100/sensor.py new file mode 100644 index 0000000000..33eb78b841 --- /dev/null +++ b/esphome/components/smt100/sensor.py @@ -0,0 +1,94 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor, uart + +from esphome.const import ( + CONF_ID, + CONF_COUNTS, + CONF_DIELECTRIC_CONSTANT, + CONF_TEMPERATURE, + CONF_MOISTURE, + CONF_VOLTAGE, + ICON_WATER_PERCENT, + DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_VOLTAGE, + STATE_CLASS_MEASUREMENT, + UNIT_EMPTY, + UNIT_CELSIUS, + UNIT_PERCENT, + UNIT_VOLT, +) + +DEPENDENCIES = ["uart"] + +smt100_ns = cg.esphome_ns.namespace("smt100") +SMT100 = smt100_ns.class_("SMT100Component", cg.PollingComponent, uart.UARTDevice) + + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(SMT100), + cv.Optional(CONF_COUNTS): sensor.sensor_schema( + unit_of_measurement=UNIT_EMPTY, + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_DIELECTRIC_CONSTANT): sensor.sensor_schema( + unit_of_measurement=UNIT_EMPTY, + accuracy_decimals=2, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=2, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_MOISTURE): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + icon=ICON_WATER_PERCENT, + accuracy_decimals=2, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( + unit_of_measurement=UNIT_VOLT, + accuracy_decimals=2, + device_class=DEVICE_CLASS_VOLTAGE, + state_class=STATE_CLASS_MEASUREMENT, + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(uart.UART_DEVICE_SCHEMA) +) + +FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema( + "smt100", baud_rate=9600, require_rx=True, require_tx=True +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await uart.register_uart_device(var, config) + + if CONF_COUNTS in config: + sens = await sensor.new_sensor(config[CONF_COUNTS]) + cg.add(var.set_counts_sensor(sens)) + + if CONF_DIELECTRIC_CONSTANT in config: + sens = await sensor.new_sensor(config[CONF_DIELECTRIC_CONSTANT]) + cg.add(var.set_dielectric_constant_sensor(sens)) + + if CONF_TEMPERATURE in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE]) + cg.add(var.set_temperature_sensor(sens)) + + if CONF_MOISTURE in config: + sens = await sensor.new_sensor(config[CONF_MOISTURE]) + cg.add(var.set_moisture_sensor(sens)) + + if CONF_VOLTAGE in config: + sens = await sensor.new_sensor(config[CONF_VOLTAGE]) + cg.add(var.set_voltage_sensor(sens)) diff --git a/esphome/components/smt100/smt100.cpp b/esphome/components/smt100/smt100.cpp new file mode 100644 index 0000000000..24ba05b894 --- /dev/null +++ b/esphome/components/smt100/smt100.cpp @@ -0,0 +1,84 @@ +#include "smt100.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace smt100 { + +static const char *const TAG = "smt100"; + +void SMT100Component::update() { + ESP_LOGV(TAG, "Sending measurement request"); + this->write_str("GetAllMeasurements!\r"); +} + +void SMT100Component::loop() { + static char buffer[MAX_LINE_LENGTH]; + while (this->available() != 0) { + if (readline_(read(), buffer, MAX_LINE_LENGTH) > 0) { + int counts = (int) strtol((strtok(buffer, ",")), nullptr, 10); + float dielectric_constant = (float) strtod((strtok(nullptr, ",")), nullptr); + float moisture = (float) strtod((strtok(nullptr, ",")), nullptr); + float temperature = (float) strtod((strtok(nullptr, ",")), nullptr); + float voltage = (float) strtod((strtok(nullptr, ",")), nullptr); + + if (this->counts_sensor_ != nullptr) { + counts_sensor_->publish_state(counts); + } + + if (this->dielectric_constant_sensor_ != nullptr) { + dielectric_constant_sensor_->publish_state(dielectric_constant); + } + + if (this->moisture_sensor_ != nullptr) { + moisture_sensor_->publish_state(moisture); + } + + if (this->temperature_sensor_ != nullptr) { + temperature_sensor_->publish_state(temperature); + } + + if (this->voltage_sensor_ != nullptr) { + voltage_sensor_->publish_state(voltage); + } + } + } +} + +float SMT100Component::get_setup_priority() const { return setup_priority::DATA; } + +void SMT100Component::dump_config() { + ESP_LOGCONFIG(TAG, "SMT100:"); + + LOG_SENSOR(TAG, "Counts", this->temperature_sensor_); + LOG_SENSOR(TAG, "Dielectric Constant", this->temperature_sensor_); + LOG_SENSOR(TAG, "Temperature", this->temperature_sensor_); + LOG_SENSOR(TAG, "Moisture", this->moisture_sensor_); + LOG_UPDATE_INTERVAL(this); + this->check_uart_settings(9600); +} + +int SMT100Component::readline_(int readch, char *buffer, int len) { + static int pos = 0; + int rpos; + + if (readch > 0) { + switch (readch) { + case '\n': // Ignore new-lines + break; + case '\r': // Return on CR + rpos = pos; + pos = 0; // Reset position index ready for next time + return rpos; + default: + if (pos < len - 1) { + buffer[pos++] = readch; + buffer[pos] = 0; + } + } + } + // No end of line has been found, so return -1. + return -1; +} + +} // namespace smt100 +} // namespace esphome diff --git a/esphome/components/smt100/smt100.h b/esphome/components/smt100/smt100.h new file mode 100644 index 0000000000..017818bdcf --- /dev/null +++ b/esphome/components/smt100/smt100.h @@ -0,0 +1,43 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/uart/uart.h" + +namespace esphome { +namespace smt100 { + +class SMT100Component : public PollingComponent, public uart::UARTDevice { + static const uint16_t MAX_LINE_LENGTH = 31; + + public: + SMT100Component() = default; + + void dump_config() override; + void loop() override; + void update() override; + + float get_setup_priority() const override; + + void set_counts_sensor(sensor::Sensor *counts_sensor) { this->counts_sensor_ = counts_sensor; } + void set_dielectric_constant_sensor(sensor::Sensor *dielectric_constant_sensor) { + this->dielectric_constant_sensor_ = dielectric_constant_sensor; + } + void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; } + void set_moisture_sensor(sensor::Sensor *moisture_sensor) { this->moisture_sensor_ = moisture_sensor; } + void set_voltage_sensor(sensor::Sensor *voltage_sensor) { this->voltage_sensor_ = voltage_sensor; } + + protected: + int readline_(int readch, char *buffer, int len); + + sensor::Sensor *counts_sensor_{nullptr}; + sensor::Sensor *dielectric_constant_sensor_{nullptr}; + sensor::Sensor *moisture_sensor_{nullptr}; + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *voltage_sensor_{nullptr}; + + uint32_t last_transmission_{0}; +}; + +} // namespace smt100 +} // namespace esphome diff --git a/esphome/const.py b/esphome/const.py index 9e83c63fd5..694609480d 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -126,6 +126,7 @@ CONF_COOL_MODE = "cool_mode" CONF_COOL_OVERRUN = "cool_overrun" CONF_COUNT = "count" CONF_COUNT_MODE = "count_mode" +CONF_COUNTS = "counts" CONF_COURSE = "course" CONF_CRON = "cron" CONF_CS_PIN = "cs_pin" @@ -164,6 +165,7 @@ CONF_DELTA = "delta" CONF_DEVICE = "device" CONF_DEVICE_CLASS = "device_class" CONF_DEVICE_FACTOR = "device_factor" +CONF_DIELECTRIC_CONSTANT = "dielectric_constant" CONF_DIMENSIONS = "dimensions" CONF_DIO_PIN = "dio_pin" CONF_DIR_PIN = "dir_pin" diff --git a/tests/test3.yaml b/tests/test3.yaml index af0e784465..560eda2585 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -270,6 +270,10 @@ uart: tx_pin: GPIO4 rx_pin: GPIO5 baud_rate: 9600 + - id: uart10 + tx_pin: GPIO4 + rx_pin: GPIO5 + baud_rate: 9600 modbus: uart_id: uart1 @@ -755,6 +759,19 @@ sensor: temperature: name: "mlxtemp" oversampling: 2 + - platform: smt100 + uart_id: uart10 + counts: + name: "Counts" + dielectric_constant: + name: "Dielectric Constant" + temperature: + name: "Temperature" + moisture: + name: "Moisture" + voltage: + name: "Voltage" + update_interval: 60s time: - platform: homeassistant