forked from esphome/esphome
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add pmwcs3 capacitive soil moisture & temperature sensor component (e…
…sphome#4624) Co-authored-by: Jesse Hills <[email protected]>
- Loading branch information
1 parent
0af8d0b
commit c11c4da
Showing
6 changed files
with
337 additions
and
0 deletions.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
CODEOWNERS = ["@SeByDocKy"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
#include "pmwcs3.h" | ||
#include "esphome/core/hal.h" | ||
#include "esphome/core/log.h" | ||
|
||
namespace esphome { | ||
namespace pmwcs3 { | ||
|
||
static const uint8_t PMWCS3_I2C_ADDRESS = 0x63; | ||
static const uint8_t PMWCS3_REG_READ_START = 0x01; | ||
static const uint8_t PMWCS3_REG_READ_E25 = 0x02; | ||
static const uint8_t PMWCS3_REG_READ_EC = 0x03; | ||
static const uint8_t PMWCS3_REG_READ_TEMP = 0x04; | ||
static const uint8_t PMWCS3_REG_READ_VWC = 0x05; | ||
static const uint8_t PMWCS3_REG_CALIBRATE_AIR = 0x06; | ||
static const uint8_t PMWCS3_REG_CALIBRATE_WATER = 0x07; | ||
static const uint8_t PMWCS3_SET_I2C_ADDRESS = 0x08; | ||
static const uint8_t PMWCS3_REG_GET_DATA = 0x09; | ||
static const uint8_t PMWCS3_REG_CALIBRATE_EC = 0x10; | ||
static const uint8_t PMWCS3_REG_CAP = 0x0A; | ||
static const uint8_t PMWCS3_REG_RES = 0x0B; | ||
static const uint8_t PMWCS3_REG_RC = 0x0C; | ||
static const uint8_t PMWCS3_REG_RT = 0x0D; | ||
|
||
static const char *const TAG = "pmwcs3"; | ||
|
||
void PMWCS3Component::new_i2c_address(uint8_t address) { | ||
if (!this->write_byte(PMWCS3_SET_I2C_ADDRESS, address)) { | ||
this->status_set_warning(); | ||
ESP_LOGW(TAG, "couldn't write the new I2C address %d", address); | ||
return; | ||
} | ||
this->set_i2c_address(address); // Allows device to continue working until new firmware is written with new address. | ||
ESP_LOGVV(TAG, "changed I2C address to %d", address); | ||
this->status_clear_warning(); | ||
} | ||
|
||
void PMWCS3Component::air_calibration() { | ||
if (!this->write_bytes(PMWCS3_REG_CALIBRATE_AIR, nullptr, 0)) { | ||
this->status_set_warning(); | ||
ESP_LOGW(TAG, "couldn't start air calibration"); | ||
return; | ||
} | ||
ESP_LOGW(TAG, "Start air calibration during the next 300s"); | ||
} | ||
void PMWCS3Component::water_calibration() { | ||
if (!this->write_bytes(PMWCS3_REG_CALIBRATE_WATER, nullptr, 0)) { | ||
this->status_set_warning(); | ||
ESP_LOGW(TAG, "couldn't start water calibration"); | ||
return; | ||
} | ||
ESP_LOGW(TAG, "Start water calibration during the next 300s"); | ||
} | ||
|
||
void PMWCS3Component::setup() { ESP_LOGCONFIG(TAG, "Setting up PMWCS3..."); } | ||
|
||
void PMWCS3Component::update() { this->read_data_(); } | ||
|
||
float PMWCS3Component::get_setup_priority() const { return setup_priority::DATA; } | ||
|
||
void PMWCS3Component::dump_config() { | ||
ESP_LOGCONFIG(TAG, "PMWCS3"); | ||
LOG_I2C_DEVICE(this); | ||
if (this->is_failed()) { | ||
ESP_LOGE(TAG, "Communication with PMWCS3 failed!"); | ||
} | ||
ESP_LOGI(TAG, "%s", this->is_failed() ? "FAILED" : "OK"); | ||
|
||
LOG_UPDATE_INTERVAL(this); | ||
LOG_SENSOR(" ", "e25", this->e25_sensor_); | ||
LOG_SENSOR(" ", "ec", this->ec_sensor_); | ||
LOG_SENSOR(" ", "temperature", this->temperature_sensor_); | ||
LOG_SENSOR(" ", "vwc", this->vwc_sensor_); | ||
} | ||
void PMWCS3Component::read_data_() { | ||
uint8_t data[8]; | ||
float e25, ec, temperature, vwc; | ||
|
||
/////// Super important !!!! first activate reading PMWCS3_REG_READ_START (if not, return always the same values) //// | ||
|
||
if (!this->write_bytes(PMWCS3_REG_READ_START, nullptr, 0)) { | ||
this->status_set_warning(); | ||
ESP_LOGVV(TAG, "Failed to write into REG_READ_START register !!!"); | ||
return; | ||
} | ||
// NOLINT delay(100); | ||
|
||
if (!this->read_bytes(PMWCS3_REG_GET_DATA, (uint8_t *) &data, 8)) { | ||
ESP_LOGVV(TAG, "Error reading PMWCS3_REG_GET_DATA registers"); | ||
this->mark_failed(); | ||
return; | ||
} | ||
if (this->e25_sensor_ != nullptr) { | ||
e25 = ((data[1] << 8) | data[0]) / 100.0; | ||
this->e25_sensor_->publish_state(e25); | ||
ESP_LOGVV(TAG, "e25: data[0]=%d, data[1]=%d, result=%f", data[0], data[1], e25); | ||
} | ||
if (this->ec_sensor_ != nullptr) { | ||
ec = ((data[3] << 8) | data[2]) / 10.0; | ||
this->ec_sensor_->publish_state(ec); | ||
ESP_LOGVV(TAG, "ec: data[2]=%d, data[3]=%d, result=%f", data[2], data[3], ec); | ||
} | ||
if (this->temperature_sensor_ != nullptr) { | ||
temperature = ((data[5] << 8) | data[4]) / 100.0; | ||
this->temperature_sensor_->publish_state(temperature); | ||
ESP_LOGVV(TAG, "temp: data[4]=%d, data[5]=%d, result=%f", data[4], data[5], temperature); | ||
} | ||
if (this->vwc_sensor_ != nullptr) { | ||
vwc = ((data[7] << 8) | data[6]) / 10.0; | ||
this->vwc_sensor_->publish_state(vwc); | ||
ESP_LOGVV(TAG, "vwc: data[6]=%d, data[7]=%d, result=%f", data[6], data[7], vwc); | ||
} | ||
} | ||
|
||
} // namespace pmwcs3 | ||
} // namespace esphome |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
#pragma once | ||
#include "esphome/core/automation.h" | ||
#include "esphome/core/component.h" | ||
#include "esphome/components/sensor/sensor.h" | ||
#include "esphome/components/i2c/i2c.h" | ||
|
||
// ref: | ||
// https://github.com/tinovi/i2cArduino/blob/master/i2cArduino.h | ||
|
||
namespace esphome { | ||
namespace pmwcs3 { | ||
|
||
class PMWCS3Component : public PollingComponent, public i2c::I2CDevice { | ||
public: | ||
void setup() override; | ||
void update() override; | ||
void dump_config() override; | ||
float get_setup_priority() const override; | ||
|
||
void set_e25_sensor(sensor::Sensor *e25_sensor) { e25_sensor_ = e25_sensor; } | ||
void set_ec_sensor(sensor::Sensor *ec_sensor) { ec_sensor_ = ec_sensor; } | ||
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } | ||
void set_vwc_sensor(sensor::Sensor *vwc_sensor) { vwc_sensor_ = vwc_sensor; } | ||
|
||
void new_i2c_address(uint8_t newaddress); | ||
void air_calibration(); | ||
void water_calibration(); | ||
|
||
protected: | ||
void read_data_(); | ||
|
||
sensor::Sensor *e25_sensor_{nullptr}; | ||
sensor::Sensor *ec_sensor_{nullptr}; | ||
sensor::Sensor *temperature_sensor_{nullptr}; | ||
sensor::Sensor *vwc_sensor_{nullptr}; | ||
}; | ||
|
||
template<typename... Ts> class PMWCS3AirCalibrationAction : public Action<Ts...> { | ||
public: | ||
PMWCS3AirCalibrationAction(PMWCS3Component *parent) : parent_(parent) {} | ||
|
||
void play(Ts... x) override { this->parent_->air_calibration(); } | ||
|
||
protected: | ||
PMWCS3Component *parent_; | ||
}; | ||
|
||
template<typename... Ts> class PMWCS3WaterCalibrationAction : public Action<Ts...> { | ||
public: | ||
PMWCS3WaterCalibrationAction(PMWCS3Component *parent) : parent_(parent) {} | ||
|
||
void play(Ts... x) override { this->parent_->water_calibration(); } | ||
|
||
protected: | ||
PMWCS3Component *parent_; | ||
}; | ||
|
||
template<typename... Ts> class PMWCS3NewI2cAddressAction : public Action<Ts...> { | ||
public: | ||
PMWCS3NewI2cAddressAction(PMWCS3Component *parent) : parent_(parent) {} | ||
TEMPLATABLE_VALUE(int, new_address) | ||
|
||
void play(Ts... x) override { this->parent_->new_i2c_address(this->new_address_.value(x...)); } | ||
|
||
protected: | ||
PMWCS3Component *parent_; | ||
}; | ||
|
||
} // namespace pmwcs3 | ||
} // namespace esphome |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import esphome.codegen as cg | ||
from esphome import automation | ||
import esphome.config_validation as cv | ||
from esphome.components import i2c, sensor | ||
from esphome.const import ( | ||
CONF_ID, | ||
CONF_ADDRESS, | ||
CONF_TEMPERATURE, | ||
CONF_EC, | ||
STATE_CLASS_MEASUREMENT, | ||
ICON_THERMOMETER, | ||
) | ||
|
||
CODEOWNERS = ["@SeByDocKy"] | ||
DEPENDENCIES = ["i2c"] | ||
|
||
CONF_E25 = "e25" | ||
CONF_VWC = "vwc" | ||
|
||
ICON_EPSILON = "mdi:epsilon" | ||
ICON_SIGMA = "mdi:sigma-lower" | ||
ICON_ALPHA = "mdi:alpha-h-circle-outline" | ||
|
||
pmwcs3_ns = cg.esphome_ns.namespace("pmwcs3") | ||
PMWCS3Component = pmwcs3_ns.class_( | ||
"PMWCS3Component", cg.PollingComponent, i2c.I2CDevice | ||
) | ||
|
||
# Actions | ||
PMWCS3AirCalibrationAction = pmwcs3_ns.class_( | ||
"PMWCS3AirCalibrationAction", automation.Action | ||
) | ||
PMWCS3WaterCalibrationAction = pmwcs3_ns.class_( | ||
"PMWCS3WaterCalibrationAction", automation.Action | ||
) | ||
PMWCS3NewI2cAddressAction = pmwcs3_ns.class_( | ||
"PMWCS3NewI2cAddressAction", automation.Action | ||
) | ||
|
||
CONFIG_SCHEMA = ( | ||
cv.Schema( | ||
{ | ||
cv.GenerateID(): cv.declare_id(PMWCS3Component), | ||
cv.Optional(CONF_E25): sensor.sensor_schema( | ||
icon=ICON_EPSILON, | ||
accuracy_decimals=3, | ||
unit_of_measurement="dS/m", | ||
state_class=STATE_CLASS_MEASUREMENT, | ||
), | ||
cv.Optional(CONF_EC): sensor.sensor_schema( | ||
icon=ICON_SIGMA, | ||
accuracy_decimals=2, | ||
unit_of_measurement="mS/m", | ||
state_class=STATE_CLASS_MEASUREMENT, | ||
), | ||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( | ||
icon=ICON_THERMOMETER, | ||
accuracy_decimals=3, | ||
unit_of_measurement="°C", | ||
state_class=STATE_CLASS_MEASUREMENT, | ||
), | ||
cv.Optional(CONF_VWC): sensor.sensor_schema( | ||
icon=ICON_ALPHA, | ||
accuracy_decimals=3, | ||
unit_of_measurement="cm3cm−3", | ||
state_class=STATE_CLASS_MEASUREMENT, | ||
), | ||
} | ||
) | ||
.extend(cv.polling_component_schema("60s")) | ||
.extend(i2c.i2c_device_schema(0x63)) | ||
) | ||
|
||
|
||
async def to_code(config): | ||
var = cg.new_Pvariable(config[CONF_ID]) | ||
await cg.register_component(var, config) | ||
await i2c.register_i2c_device(var, config) | ||
|
||
if CONF_E25 in config: | ||
sens = await sensor.new_sensor(config[CONF_E25]) | ||
cg.add(var.set_e25_sensor(sens)) | ||
|
||
if CONF_EC in config: | ||
sens = await sensor.new_sensor(config[CONF_EC]) | ||
cg.add(var.set_ec_sensor(sens)) | ||
|
||
if CONF_TEMPERATURE in config: | ||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE]) | ||
cg.add(var.set_temperature_sensor(sens)) | ||
|
||
if CONF_VWC in config: | ||
sens = await sensor.new_sensor(config[CONF_VWC]) | ||
cg.add(var.set_vwc_sensor(sens)) | ||
|
||
|
||
# Actions | ||
PMWCS3_CALIBRATION_SCHEMA = cv.Schema( | ||
{ | ||
cv.GenerateID(): cv.use_id(PMWCS3Component), | ||
} | ||
) | ||
|
||
|
||
@automation.register_action( | ||
"pmwcs3.air_calibration", | ||
PMWCS3AirCalibrationAction, | ||
PMWCS3_CALIBRATION_SCHEMA, | ||
) | ||
@automation.register_action( | ||
"pmwcs3.water_calibration", | ||
PMWCS3WaterCalibrationAction, | ||
PMWCS3_CALIBRATION_SCHEMA, | ||
) | ||
async def pmwcs3_calibration_to_code(config, action_id, template_arg, args): | ||
parent = await cg.get_variable(config[CONF_ID]) | ||
var = cg.new_Pvariable(action_id, template_arg, parent) | ||
return var | ||
|
||
|
||
PMWCS3_NEW_I2C_ADDRESS_SCHEMA = cv.maybe_simple_value( | ||
{ | ||
cv.GenerateID(): cv.use_id(PMWCS3Component), | ||
cv.Required(CONF_ADDRESS): cv.templatable(cv.i2c_address), | ||
}, | ||
key=CONF_ADDRESS, | ||
) | ||
|
||
|
||
@automation.register_action( | ||
"pmwcs3.new_i2c_address", | ||
PMWCS3NewI2cAddressAction, | ||
PMWCS3_NEW_I2C_ADDRESS_SCHEMA, | ||
) | ||
async def pmwcs3newi2caddress_to_code(config, action_id, template_arg, args): | ||
parent = await cg.get_variable(config[CONF_ID]) | ||
var = cg.new_Pvariable(action_id, template_arg, parent) | ||
address = await cg.templatable(config[CONF_ADDRESS], args, int) | ||
cg.add(var.set_new_address(address)) | ||
return var |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters