diff --git a/esphome/components/ezo/__init__.py b/esphome/components/ezo/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/ezo/ezo.cpp b/esphome/components/ezo/ezo.cpp new file mode 100644 index 0000000000..97caa1495b --- /dev/null +++ b/esphome/components/ezo/ezo.cpp @@ -0,0 +1,86 @@ +#include "ezo.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace ezo { + +static const char *TAG = "ezo.sensor"; + +static const uint16_t EZO_STATE_WAIT = 1; +static const uint16_t EZO_STATE_SEND_TEMP = 2; +static const uint16_t EZO_STATE_WAIT_TEMP = 4; + +void EZOSensor::dump_config() { + LOG_SENSOR("", "EZO", this); + LOG_I2C_DEVICE(this); + if (this->is_failed()) + ESP_LOGE(TAG, "Communication with EZO circuit failed!"); + LOG_UPDATE_INTERVAL(this); +} + +void EZOSensor::update() { + if (this->state_ & EZO_STATE_WAIT) { + ESP_LOGE(TAG, "update overrun, still waiting for previous response"); + return; + } + uint8_t c = 'R'; + this->write_bytes_raw(&c, 1); + this->state_ |= EZO_STATE_WAIT; + this->start_time_ = millis(); + this->wait_time_ = 900; +} + +void EZOSensor::loop() { + uint8_t buf[20]; + if (!(this->state_ & EZO_STATE_WAIT)) { + if (this->state_ & EZO_STATE_SEND_TEMP) { + int len = sprintf((char *) buf, "T,%0.3f", this->tempcomp_); + this->write_bytes_raw(buf, len); + this->state_ = EZO_STATE_WAIT | EZO_STATE_WAIT_TEMP; + this->start_time_ = millis(); + this->wait_time_ = 300; + } + return; + } + if (millis() - this->start_time_ < this->wait_time_) + return; + buf[0] = 0; + if (!this->read_bytes_raw(buf, 20)) { + ESP_LOGE(TAG, "read error"); + this->state_ = 0; + return; + } + switch (buf[0]) { + case 1: + break; + case 2: + ESP_LOGE(TAG, "device returned a syntax error"); + break; + case 254: + return; // keep waiting + case 255: + ESP_LOGE(TAG, "device returned no data"); + break; + default: + ESP_LOGE(TAG, "device returned an unknown response: %d", buf[0]); + break; + } + if (this->state_ & EZO_STATE_WAIT_TEMP) { + this->state_ = 0; + return; + } + this->state_ &= ~EZO_STATE_WAIT; + if (buf[0] != 1) + return; + + float val = strtof((char *) &buf[1], nullptr); + this->publish_state(val); +} + +void EZOSensor::set_tempcomp_value(float temp) { + this->tempcomp_ = temp; + this->state_ |= EZO_STATE_SEND_TEMP; +} + +} // namespace ezo +} // namespace esphome diff --git a/esphome/components/ezo/ezo.h b/esphome/components/ezo/ezo.h new file mode 100644 index 0000000000..b2b59e38d4 --- /dev/null +++ b/esphome/components/ezo/ezo.h @@ -0,0 +1,28 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace ezo { + +/// This class implements support for the EZO circuits in i2c mode +class EZOSensor : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice { + public: + void loop() override; + void dump_config() override; + void update() override; + float get_setup_priority() const override { return setup_priority::DATA; }; + + void set_tempcomp_value(float temp); + + protected: + unsigned long start_time_ = 0; + unsigned long wait_time_ = 0; + uint16_t state_ = 0; + float tempcomp_; +}; + +} // namespace ezo +} // namespace esphome diff --git a/esphome/components/ezo/sensor.py b/esphome/components/ezo/sensor.py new file mode 100644 index 0000000000..91e67dfd32 --- /dev/null +++ b/esphome/components/ezo/sensor.py @@ -0,0 +1,21 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import i2c, sensor +from esphome.const import CONF_ID + +DEPENDENCIES = ['i2c'] + +ezo_ns = cg.esphome_ns.namespace('ezo') + +EZOSensor = ezo_ns.class_('EZOSensor', sensor.Sensor, cg.PollingComponent, i2c.I2CDevice) + +CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(EZOSensor), +}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(None)) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield sensor.register_sensor(var, config) + yield i2c.register_i2c_device(var, config) diff --git a/tests/test1.yaml b/tests/test1.yaml index 4bb8e1c176..2cbf865223 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -784,6 +784,10 @@ sensor: - platform: mcp9808 name: "MCP9808 Temperature" update_interval: 15s + - platform: ezo + id: ph_ezo + address: 99 + unit_of_measurement: "pH" esp32_touch: setup_mode: False diff --git a/tests/test3.yaml b/tests/test3.yaml index 328a243a73..9f264812bf 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -421,6 +421,10 @@ sensor: name: "CSE7766 Current" power: name: "CSE776 Power" + - platform: ezo + id: ph_ezo + address: 99 + unit_of_measurement: "pH" time: - platform: homeassistant