Add new sensor sen0501

This commit is contained in:
thetestspecimen 2024-06-11 22:20:28 +03:00
parent d64e0ae704
commit 5a9763d20f
No known key found for this signature in database
GPG Key ID: C88001C355A25170
4 changed files with 339 additions and 0 deletions

View File

View File

@ -0,0 +1,165 @@
#include "sen0501.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace sen0501 {
static const char *const TAG = "sen0501";
// Device Address
static const uint8_t SEN050X_DEFAULT_DEVICE_ADDRESS = 0x22;
// Board identification
static const uint16_t DEVICE_PID_GRAVITY = 0x01F5;
static const uint16_t DEVICE_PID_BREAKOUT = 0x01F4;
static const uint16_t DEVICE_VID = 0x3343;
// Device registers
static const uint16_t REG_PID = 0x0000;
static const uint16_t REG_VID = 0x0002;
static const uint16_t REG_DEVICE_ADDR = 0x0004;
static const uint16_t REG_UART_CTRL0 = 0x0006;
static const uint16_t EG_UART_CTRL1 = 0x0008;
static const uint16_t REG_VERSION = 0x000A;
// Data registers
static const uint16_t REG_ULTRAVIOLET_INTENSITY = 0x0010;
static const uint16_t REG_LUMINOUS_INTENSITY = 0x0012;
static const uint16_t REG_TEMP = 0x0014;
static const uint16_t REG_HUMIDITY = 0x0016;
static const uint16_t REG_ATMOSPHERIC_PRESSURE = 0x0018;
// PUBLIC
void sen0501Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up sen0501...");
uint8_t product_id_first_byte;
if (!this->read_byte(REG_PID, &product_id_first_byte)) {
this->error_code_ = COMMUNICATION_FAILED;
this->mark_failed();
return;
} else {
uint8_t buf[2];
this->read_bytes(REG_PID, buf, 2);
uint16_t product_id = buf[0] << 8 | buf[1];
if ((product_id != DEVICE_PID_GRAVITY) && (product_id != DEVICE_PID_BREAKOUT)) {
this->error_code_ = WRONG_DEVICE_ID;
this->mark_failed();
return;
}
this->read_bytes(REG_VID, buf, 2);
uint16_t vendor_id = buf[0] << 8 | buf[1];
if (vendor_id != DEVICE_VID) {
this->error_code_ = WRONG_VENDOR_ID;
this->mark_failed();
return;
}
}
}
void sen0501Component::update() {
this->read_temperature_();
this->read_humidity_();
this->read_uv_intensity_();
this->read_luminous_intensity_();
this->read_atmospheric_pressure_();
}
void sen0501Component::dump_config() {
ESP_LOGCONFIG(TAG, "DFRobot Environmental Sensor - sen0501:");
LOG_I2C_DEVICE(this);
switch (this->error_code_) {
case COMMUNICATION_FAILED:
ESP_LOGE(TAG, "Communication with sen0501 failed!");
break;
case WRONG_DEVICE_ID:
ESP_LOGE(TAG, "Wrong device ID! Is it a sen0501?");
break;
case WRONG_VENDOR_ID:
ESP_LOGE(TAG, "Wrong vendor ID! Is it made by DFRobot(Zhiwei Robotics Corp)?");
break;
case NONE:
default:
break;
}
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "Temperature", this->temperature_);
LOG_SENSOR(" ", "Humidity", this->humidity_);
LOG_SENSOR(" ", "UV Intensity", this->uv_intensity_);
LOG_SENSOR(" ", "Luminous Intensity", this->luminous_intensity_);
LOG_SENSOR(" ", "Atmospheric Pressure", this->atmospheric_pressure_);
LOG_SENSOR(" ", "Elevation", this->elevation_);
}
float sen0501Component::get_setup_priority() const { return setup_priority::DATA; }
// PROTECTED
float sen0501Component::mapfloat(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
void sen0501Component::read_temperature_() {
uint8_t buffer[2];
uint16_t data;
float temp;
read_bytes(REG_TEMP, buffer, 2);
data = buffer[0] << 8 | buffer[1];
temp = 175.0f * float(data) / 65536.0f - 45.0f;
this->temperature_->publish_state(temp);
}
void sen0501Component::read_humidity_() {
uint8_t buffer[2];
uint16_t data;
float humidity;
read_bytes(REG_HUMIDITY, buffer, 2);
data = buffer[0] << 8 | buffer[1];
humidity = (float) data * 100 / 65536;
this->humidity_->publish_state(humidity);
}
void sen0501Component::read_uv_intensity_() {
uint8_t buffer[2];
uint16_t uvLevel;
uint16_t version = 0;
float ultraviolet;
read_bytes(REG_VERSION, buffer, 2);
version = buffer[0] << 8 | buffer[1];
if (version == 0x1001) {
read_bytes(REG_ULTRAVIOLET_INTENSITY, buffer, 2);
uvLevel = buffer[0] << 8 | buffer[1];
ultraviolet = (float) uvLevel / 1800.0;
} else {
read_bytes(REG_ULTRAVIOLET_INTENSITY, buffer, 2);
uvLevel = buffer[0] << 8 | buffer[1];
float outputVoltage = 3.0 * uvLevel / 1024;
if (outputVoltage <= 0.99)
outputVoltage = 0.99;
else if (outputVoltage >= 2.99)
outputVoltage = 2.99;
ultraviolet = mapfloat(outputVoltage, 0.99, 2.9, 0.0, 15.0);
}
this->uv_intensity_->publish_state(ultraviolet);
}
void sen0501Component::read_luminous_intensity_() {
uint8_t buffer[2];
uint16_t data;
read_bytes(REG_LUMINOUS_INTENSITY, buffer, 2);
data = buffer[0] << 8 | buffer[1];
float luminous = data;
luminous = luminous * (1.0023f + luminous * (8.1488e-5f + luminous * (-9.3924e-9f + luminous * 6.0135e-13f)));
this->luminous_intensity_->publish_state(luminous);
}
void sen0501Component::read_atmospheric_pressure_() {
uint8_t buffer[2];
uint16_t atmosphere;
read_bytes(REG_ATMOSPHERIC_PRESSURE, buffer, 2);
atmosphere = buffer[0] << 8 | buffer[1];
float elevation = 44330 * (1.0 - pow(atmosphere / 1015.0f, 0.1903));
this->atmospheric_pressure_->publish_state(atmosphere);
this->elevation_->publish_state(elevation);
}
} // namespace sen0501
} // namespace esphome

View File

@ -0,0 +1,52 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
// ref:
// https://github.com/DFRobot/DFRobot_EnvironmentalSensor
namespace esphome {
namespace sen0501 {
class sen0501Component : public PollingComponent, public i2c::I2CDevice {
public:
void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; }
void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; }
void set_uv_intensity(sensor::Sensor *uv_intensity) { uv_intensity_ = uv_intensity; }
void set_luminous_intensity(sensor::Sensor *luminous_intensity) { luminous_intensity_ = luminous_intensity; }
void set_atmospheric_pressure(sensor::Sensor *atmospheric_pressure) { atmospheric_pressure_ = atmospheric_pressure; }
void set_elevation(sensor::Sensor *elevation) { elevation_ = elevation; }
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
protected:
float mapfloat(float x, float in_min, float in_max, float out_min, float out_max);
void read_temperature_();
void read_humidity_();
void read_uv_intensity_();
void read_luminous_intensity_();
void read_atmospheric_pressure_();
sensor::Sensor *temperature_{nullptr};
sensor::Sensor *humidity_{nullptr};
sensor::Sensor *uv_intensity_{nullptr};
sensor::Sensor *luminous_intensity_{nullptr};
sensor::Sensor *atmospheric_pressure_{nullptr};
sensor::Sensor *elevation_{nullptr};
enum ErrorCode {
NONE = 0,
COMMUNICATION_FAILED,
WRONG_DEVICE_ID,
WRONG_VENDOR_ID,
} error_code_{NONE};
};
} // namespace sen0501
} // namespace esphome

View File

@ -0,0 +1,122 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import (
ICON_BRIGHTNESS_5,
ICON_ELEVATION,
ICON_GAUGE,
ICON_THERMOMETER,
ICON_UV_RADIATION,
ICON_WATER_PERCENT,
UNIT_CELSIUS,
UNIT_HECTOPASCAL,
UNIT_IRRADIANCE,
UNIT_LUX,
UNIT_METER,
UNIT_PERCENT,
DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
DEVICE_CLASS_DISTANCE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_ILLUMINANCE,
DEVICE_CLASS_IRRADIANCE,
DEVICE_CLASS_TEMPERATURE,
CONF_ELEVATION,
CONF_HUMIDITY,
CONF_ID,
CONF_ILLUMINANCE,
CONF_TEMPERATURE,
CONF_PRESSURE,
CONF_UV_IRRADIANCE,
STATE_CLASS_MEASUREMENT,
)
CODEOWNERS = ["@thetestspecimen"]
DEPENDENCIES = ["i2c"]
sen0501_ns = cg.esphome_ns.namespace("sen0501")
sen0501Component = sen0501_ns.class_(
"sen0501Component", cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(sen0501Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
icon=ICON_THERMOMETER,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
icon=ICON_WATER_PERCENT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_UV_IRRADIANCE): sensor.sensor_schema(
unit_of_measurement=UNIT_IRRADIANCE,
icon=ICON_UV_RADIATION,
accuracy_decimals=2,
device_class=DEVICE_CLASS_IRRADIANCE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(
unit_of_measurement=UNIT_LUX,
icon=ICON_BRIGHTNESS_5,
accuracy_decimals=2,
device_class=DEVICE_CLASS_ILLUMINANCE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
unit_of_measurement=UNIT_HECTOPASCAL,
icon=ICON_GAUGE,
accuracy_decimals=1,
device_class=DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_ELEVATION): sensor.sensor_schema(
unit_of_measurement=UNIT_METER,
icon=ICON_ELEVATION,
accuracy_decimals=1,
device_class=DEVICE_CLASS_DISTANCE,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x22))
)
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 temperature_config := config.get(CONF_TEMPERATURE):
sens = await sensor.new_sensor(temperature_config)
cg.add(var.set_temperature(sens))
if humidity_config := config.get(CONF_HUMIDITY):
sens = await sensor.new_sensor(humidity_config)
cg.add(var.set_humidity(sens))
if uv_irradiance_config := config.get(CONF_UV_IRRADIANCE):
sens = await sensor.new_sensor(uv_irradiance_config)
cg.add(var.set_uv_intensity(sens))
if brightness_config := config.get(CONF_ILLUMINANCE):
sens = await sensor.new_sensor(brightness_config)
cg.add(var.set_luminous_intensity(sens))
if pressure_config := config.get(CONF_PRESSURE):
sens = await sensor.new_sensor(pressure_config)
cg.add(var.set_atmospheric_pressure(sens))
if elevation_config := config.get(CONF_ELEVATION):
sens = await sensor.new_sensor(elevation_config)
cg.add(var.set_elevation(sens))