diff --git a/CODEOWNERS b/CODEOWNERS index 122dc71b48..17d1462405 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -11,6 +11,7 @@ esphome/*.py @esphome/core esphome/core/* @esphome/core # Integrations +esphome/components/a01nyub/* @MrSuicideParrot esphome/components/absolute_humidity/* @DAVe3283 esphome/components/ac_dimmer/* @glmnet esphome/components/adc/* @esphome/core diff --git a/esphome/components/a01nyub/__init__.py b/esphome/components/a01nyub/__init__.py new file mode 100644 index 0000000000..4c84847fb6 --- /dev/null +++ b/esphome/components/a01nyub/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@MrSuicideParrot"] diff --git a/esphome/components/a01nyub/a01nyub.cpp b/esphome/components/a01nyub/a01nyub.cpp new file mode 100644 index 0000000000..75cb276f84 --- /dev/null +++ b/esphome/components/a01nyub/a01nyub.cpp @@ -0,0 +1,57 @@ +// Datasheet https://wiki.dfrobot.com/A01NYUB%20Waterproof%20Ultrasonic%20Sensor%20SKU:%20SEN0313 + +#include "a01nyub.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace a01nyub { + +static const char *const TAG = "a01nyub.sensor"; +static const uint8_t MAX_DATA_LENGTH_BYTES = 4; + +void A01nyubComponent::loop() { + uint8_t data; + while (this->available() > 0) { + if (this->read_byte(&data)) { + buffer_.push_back(data); + this->check_buffer_(); + } + } +} + +void A01nyubComponent::check_buffer_() { + if (this->buffer_.size() >= MAX_DATA_LENGTH_BYTES) { + size_t i; + for (i = 0; i < this->buffer_.size(); i++) { + // Look for the first packet + if (this->buffer_[i] == 0xFF) { + if (i + 1 + 3 < this->buffer_.size()) { // Packet is not complete + return; // Wait for completion + } + + uint8_t checksum = (this->buffer_[i] + this->buffer_[i + 1] + this->buffer_[i + 2]) & 0xFF; + if (this->buffer_[i + 3] == checksum) { + float distance = (this->buffer_[i + 1] << 8) + this->buffer_[i + 2]; + if (distance > 280) { + float meters = distance / 1000.0; + ESP_LOGV(TAG, "Distance from sensor: %f mm, %f m", distance, meters); + this->publish_state(meters); + } else { + ESP_LOGW(TAG, "Invalid data read from sensor: %s", format_hex_pretty(this->buffer_).c_str()); + } + } + break; + } + } + this->buffer_.clear(); + } +} + +void A01nyubComponent::dump_config() { + ESP_LOGCONFIG(TAG, "A01nyub Sensor:"); + LOG_SENSOR(" ", "Distance", this); +} + +} // namespace a01nyub +} // namespace esphome diff --git a/esphome/components/a01nyub/a01nyub.h b/esphome/components/a01nyub/a01nyub.h new file mode 100644 index 0000000000..6b22e9bcad --- /dev/null +++ b/esphome/components/a01nyub/a01nyub.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/uart/uart.h" + +namespace esphome { +namespace a01nyub { + +class A01nyubComponent : public sensor::Sensor, public Component, public uart::UARTDevice { + public: + // Nothing really public. + + // ========== INTERNAL METHODS ========== + void loop() override; + void dump_config() override; + + protected: + void check_buffer_(); + + std::vector buffer_; +}; + +} // namespace a01nyub +} // namespace esphome diff --git a/esphome/components/a01nyub/sensor.py b/esphome/components/a01nyub/sensor.py new file mode 100644 index 0000000000..b57daa0357 --- /dev/null +++ b/esphome/components/a01nyub/sensor.py @@ -0,0 +1,41 @@ +import esphome.codegen as cg +from esphome.components import sensor, uart +from esphome.const import ( + STATE_CLASS_MEASUREMENT, + UNIT_METER, + ICON_ARROW_EXPAND_VERTICAL, + DEVICE_CLASS_DISTANCE, +) + +CODEOWNERS = ["@MrSuicideParrot"] +DEPENDENCIES = ["uart"] + +a01nyub_ns = cg.esphome_ns.namespace("a01nyub") +A01nyubComponent = a01nyub_ns.class_( + "A01nyubComponent", sensor.Sensor, cg.Component, uart.UARTDevice +) + +CONFIG_SCHEMA = sensor.sensor_schema( + A01nyubComponent, + unit_of_measurement=UNIT_METER, + icon=ICON_ARROW_EXPAND_VERTICAL, + accuracy_decimals=3, + state_class=STATE_CLASS_MEASUREMENT, + device_class=DEVICE_CLASS_DISTANCE, +).extend(uart.UART_DEVICE_SCHEMA) + +FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema( + "a01nyub", + baud_rate=9600, + require_tx=False, + require_rx=True, + data_bits=8, + parity=None, + stop_bits=1, +) + + +async def to_code(config): + var = await sensor.new_sensor(config) + await cg.register_component(var, config) + await uart.register_uart_device(var, config) diff --git a/tests/test4.yaml b/tests/test4.yaml index 2a8cb02413..54caebf1fe 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -34,9 +34,14 @@ spi: miso_pin: GPIO23 uart: - tx_pin: GPIO22 - rx_pin: GPIO23 - baud_rate: 115200 + - id: uart115200 + tx_pin: GPIO22 + rx_pin: GPIO23 + baud_rate: 115200 + - id: uart9600 + tx_pin: GPIO22 + rx_pin: GPIO23 + baud_rate: 9600 ota: safe_mode: true @@ -58,6 +63,7 @@ time: tuya: time_id: sntp_time + uart_id: uart115200 status_pin: number: 14 inverted: true @@ -73,6 +79,7 @@ select: pipsolar: id: inverter0 + uart_id: uart115200 sx1509: - id: sx1509_hub @@ -229,6 +236,7 @@ sensor: name: inverter0_pv_charging_power - platform: hrxl_maxsonar_wr name: Rainwater Tank Level + uart_id: uart115200 filters: - sliding_window_moving_average: window_size: 12 @@ -257,6 +265,10 @@ sensor: name: Ufire Temperature ph: name: Ufire pH + - platform: a01nyub + id: a01nyub_sensor + name: "a01nyub Distance" + uart_id: uart9600 # # platform sensor.apds9960 requires component apds9960