diff --git a/esphome/components/am2320/__init__.py b/esphome/components/am2320/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/am2320/am2320.cpp b/esphome/components/am2320/am2320.cpp new file mode 100644 index 0000000000..59cb977fe8 --- /dev/null +++ b/esphome/components/am2320/am2320.cpp @@ -0,0 +1,107 @@ +// Implementation based on: +// - ESPEasy: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P034_DHT12.ino +// - DHT12_sensor_library: https://github.com/xreef/DHT12_sensor_library/blob/master/DHT12.cpp +// - Arduino - AM2320: https://github.com/EngDial/AM2320/blob/master/src/AM2320.cpp + +#include "am2320.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace am2320 { + +static const char *TAG = "am2320"; + +// ---=== Calc CRC16 ===--- +uint16_t crc_16(uint8_t *ptr, uint8_t length) { + uint16_t crc = 0xFFFF; + uint8_t i; + //------------------------------ + while (length--) { + crc ^= *ptr++; + for (i = 0; i < 8; i++) + if ((crc & 0x01) != 0) { + crc >>= 1; + crc ^= 0xA001; + } else + crc >>= 1; + } + return crc; +} + +void AM2320Component::update() { + uint8_t data[8]; + data[0] = 0; + data[1] = 4; + if (!this->read_data_(data)) { + this->status_set_warning(); + return; + } + + float temperature = (((data[4] & 0x7F) << 8) + data[5]) / 10.0; + temperature = (data[4] & 0x80) ? -temperature : temperature; + float humidity = ((data[2] << 8) + data[3]) / 10.0; + + ESP_LOGD(TAG, "Got temperature=%.1f°C humidity=%.1f%%", temperature, humidity); + if (this->temperature_sensor_ != nullptr) + this->temperature_sensor_->publish_state(temperature); + if (this->humidity_sensor_ != nullptr) + this->humidity_sensor_->publish_state(humidity); + this->status_clear_warning(); +} +void AM2320Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up AM2320..."); + uint8_t data[8]; + data[0] = 0; + data[1] = 4; + if (!this->read_data_(data)) { + this->mark_failed(); + return; + } +} +void AM2320Component::dump_config() { + ESP_LOGD(TAG, "AM2320:"); + LOG_I2C_DEVICE(this); + if (this->is_failed()) { + ESP_LOGE(TAG, "Communication with AM2320 failed!"); + } + LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); + LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); +} +float AM2320Component::get_setup_priority() const { return setup_priority::DATA; } + +bool AM2320Component::read_bytes_(uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion) { + if (!this->write_bytes(a_register, data, 2)) { + ESP_LOGW(TAG, "Writing bytes for AM2320 failed!"); + return false; + } + + if (conversion > 0) + delay(conversion); + return this->parent_->raw_receive(this->address_, data, len); +} + +bool AM2320Component::read_data_(uint8_t *data) { + // Wake up + this->write_bytes(0, data, 0); + + // Write instruction 3, 2 bytes, get 8 bytes back (2 preamble, 2 bytes temperature, 2 bytes humidity, 2 bytes CRC) + if (!this->read_bytes_(3, data, 8, 2)) { + ESP_LOGW(TAG, "Updating AM2320 failed!"); + return false; + } + + uint16_t checksum; + + checksum = data[7] << 8; + checksum += data[6]; + + if (crc_16(data, 6) != checksum) { + ESP_LOGW(TAG, "AM2320 Checksum invalid!"); + return false; + } + + return true; +} + +} // namespace am2320 +} // namespace esphome diff --git a/esphome/components/am2320/am2320.h b/esphome/components/am2320/am2320.h new file mode 100644 index 0000000000..33e1d30aa0 --- /dev/null +++ b/esphome/components/am2320/am2320.h @@ -0,0 +1,29 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace am2320 { + +class AM2320Component : public PollingComponent, public i2c::I2CDevice { + public: + void setup() override; + void dump_config() override; + float get_setup_priority() const override; + void update() override; + + void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } + void set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; } + + protected: + bool read_data_(uint8_t *data); + bool read_bytes_(uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion = 0); + + sensor::Sensor *temperature_sensor_; + sensor::Sensor *humidity_sensor_; +}; + +} // namespace am2320 +} // namespace esphome diff --git a/esphome/components/am2320/sensor.py b/esphome/components/am2320/sensor.py new file mode 100644 index 0000000000..d62899663c --- /dev/null +++ b/esphome/components/am2320/sensor.py @@ -0,0 +1,30 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import i2c, sensor +from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, \ + UNIT_CELSIUS, ICON_THERMOMETER, ICON_WATER_PERCENT, UNIT_PERCENT + +DEPENDENCIES = ['i2c'] + +am2320_ns = cg.esphome_ns.namespace('am2320') +AM2320Component = am2320_ns.class_('AM2320Component', cg.PollingComponent, i2c.I2CDevice) + +CONFIG_SCHEMA = cv.Schema({ + cv.GenerateID(): cv.declare_id(AM2320Component), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), +}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x5C)) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield i2c.register_i2c_device(var, config) + + if CONF_TEMPERATURE in config: + sens = yield sensor.new_sensor(config[CONF_TEMPERATURE]) + cg.add(var.set_temperature_sensor(sens)) + + if CONF_HUMIDITY in config: + sens = yield sensor.new_sensor(config[CONF_HUMIDITY]) + cg.add(var.set_humidity_sensor(sens)) diff --git a/tests/test3.yaml b/tests/test3.yaml index 9b41efd7fb..85dc714173 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -94,6 +94,11 @@ sensor: - platform: homeassistant entity_id: sensor.hello_world id: ha_hello_world + - platform: am2320 + temperature: + name: "Temperature" + humidity: + name: "Humidity" - platform: adc pin: VCC id: my_sensor