mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 16:37:52 +01:00
Background calibration & ABC commands for SenseAir S8 (#1623)
This commit is contained in:
parent
89d0d41c5a
commit
8f1eb77e05
@ -6,12 +6,20 @@ namespace senseair {
|
||||
|
||||
static const char *TAG = "senseair";
|
||||
static const uint8_t SENSEAIR_REQUEST_LENGTH = 8;
|
||||
static const uint8_t SENSEAIR_RESPONSE_LENGTH = 13;
|
||||
static const uint8_t SENSEAIR_COMMAND_GET_PPM[] = {0xFE, 0x04, 0x00, 0x00, 0x00, 0x04, 0xE5, 0xC6};
|
||||
static const uint8_t SENSEAIR_PPM_STATUS_RESPONSE_LENGTH = 13;
|
||||
static const uint8_t SENSEAIR_ABC_PERIOD_RESPONSE_LENGTH = 7;
|
||||
static const uint8_t SENSEAIR_CAL_RESULT_RESPONSE_LENGTH = 7;
|
||||
static const uint8_t SENSEAIR_COMMAND_GET_PPM_STATUS[] = {0xFE, 0x04, 0x00, 0x00, 0x00, 0x04, 0xE5, 0xC6};
|
||||
static const uint8_t SENSEAIR_COMMAND_CLEAR_ACK_REGISTER[] = {0xFE, 0x06, 0x00, 0x00, 0x00, 0x00, 0x9D, 0xC5};
|
||||
static const uint8_t SENSEAIR_COMMAND_BACKGROUND_CAL[] = {0xFE, 0x06, 0x00, 0x01, 0x7C, 0x06, 0x6C, 0xC7};
|
||||
static const uint8_t SENSEAIR_COMMAND_BACKGROUND_CAL_RESULT[] = {0xFE, 0x03, 0x00, 0x00, 0x00, 0x01, 0x90, 0x05};
|
||||
static const uint8_t SENSEAIR_COMMAND_ABC_ENABLE[] = {0xFE, 0x06, 0x00, 0x1F, 0x00, 0xB4, 0xAC, 0x74}; // 180 hours
|
||||
static const uint8_t SENSEAIR_COMMAND_ABC_DISABLE[] = {0xFE, 0x06, 0x00, 0x1F, 0x00, 0x00, 0xAC, 0x03};
|
||||
static const uint8_t SENSEAIR_COMMAND_ABC_GET_PERIOD[] = {0xFE, 0x03, 0x00, 0x1F, 0x00, 0x01, 0xA1, 0xC3};
|
||||
|
||||
void SenseAirComponent::update() {
|
||||
uint8_t response[SENSEAIR_RESPONSE_LENGTH];
|
||||
if (!this->senseair_write_command_(SENSEAIR_COMMAND_GET_PPM, response)) {
|
||||
uint8_t response[SENSEAIR_PPM_STATUS_RESPONSE_LENGTH];
|
||||
if (!this->senseair_write_command_(SENSEAIR_COMMAND_GET_PPM_STATUS, response, SENSEAIR_PPM_STATUS_RESPONSE_LENGTH)) {
|
||||
ESP_LOGW(TAG, "Reading data from SenseAir failed!");
|
||||
this->status_set_warning();
|
||||
return;
|
||||
@ -69,14 +77,67 @@ uint16_t SenseAirComponent::senseair_checksum_(uint8_t *ptr, uint8_t length) {
|
||||
return crc;
|
||||
}
|
||||
|
||||
bool SenseAirComponent::senseair_write_command_(const uint8_t *command, uint8_t *response) {
|
||||
void SenseAirComponent::background_calibration() {
|
||||
ESP_LOGD(TAG, "SenseAir Starting background calibration");
|
||||
this->senseair_write_command_(SENSEAIR_COMMAND_CLEAR_ACK_REGISTER, nullptr, 0);
|
||||
this->senseair_write_command_(SENSEAIR_COMMAND_BACKGROUND_CAL, nullptr, 0);
|
||||
}
|
||||
|
||||
void SenseAirComponent::background_calibration_result() {
|
||||
ESP_LOGD(TAG, "SenseAir Requesting background calibration result");
|
||||
uint8_t response[SENSEAIR_CAL_RESULT_RESPONSE_LENGTH];
|
||||
if (!this->senseair_write_command_(SENSEAIR_COMMAND_BACKGROUND_CAL_RESULT, response,
|
||||
SENSEAIR_CAL_RESULT_RESPONSE_LENGTH)) {
|
||||
ESP_LOGE(TAG, "Requesting background calibration result from SenseAir failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (response[0] != 0xFE || response[1] != 0x03) {
|
||||
ESP_LOGE(TAG, "Invalid reply from SenseAir! %02x%02x%02x %02x%02x %02x%02x", response[0], response[1], response[2],
|
||||
response[3], response[4], response[5], response[6]);
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "SenseAir Result=%s (%02x%02x%02x)", response[2] == 2 ? "OK" : "NOT_OK", response[2], response[3],
|
||||
response[4]);
|
||||
}
|
||||
|
||||
void SenseAirComponent::abc_enable() {
|
||||
ESP_LOGD(TAG, "SenseAir Enabling automatic baseline calibration");
|
||||
this->senseair_write_command_(SENSEAIR_COMMAND_ABC_ENABLE, nullptr, 0);
|
||||
}
|
||||
|
||||
void SenseAirComponent::abc_disable() {
|
||||
ESP_LOGD(TAG, "SenseAir Disabling automatic baseline calibration");
|
||||
this->senseair_write_command_(SENSEAIR_COMMAND_ABC_DISABLE, nullptr, 0);
|
||||
}
|
||||
|
||||
void SenseAirComponent::abc_get_period() {
|
||||
ESP_LOGD(TAG, "SenseAir Requesting ABC period");
|
||||
uint8_t response[SENSEAIR_ABC_PERIOD_RESPONSE_LENGTH];
|
||||
if (!this->senseair_write_command_(SENSEAIR_COMMAND_ABC_GET_PERIOD, response, SENSEAIR_ABC_PERIOD_RESPONSE_LENGTH)) {
|
||||
ESP_LOGE(TAG, "Requesting ABC period from SenseAir failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (response[0] != 0xFE || response[1] != 0x03) {
|
||||
ESP_LOGE(TAG, "Invalid reply from SenseAir! %02x%02x%02x %02x%02x %02x%02x", response[0], response[1], response[2],
|
||||
response[3], response[4], response[5], response[6]);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t hours = (uint16_t(response[3]) << 8) | response[4];
|
||||
ESP_LOGD(TAG, "SenseAir Read ABC Period: %u hours", hours);
|
||||
}
|
||||
|
||||
bool SenseAirComponent::senseair_write_command_(const uint8_t *command, uint8_t *response, uint8_t response_length) {
|
||||
this->flush();
|
||||
this->write_array(command, SENSEAIR_REQUEST_LENGTH);
|
||||
|
||||
if (response == nullptr)
|
||||
return true;
|
||||
|
||||
bool ret = this->read_array(response, SENSEAIR_RESPONSE_LENGTH);
|
||||
bool ret = this->read_array(response, response_length);
|
||||
this->flush();
|
||||
return ret;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
|
||||
@ -15,12 +16,68 @@ class SenseAirComponent : public PollingComponent, public uart::UARTDevice {
|
||||
void update() override;
|
||||
void dump_config() override;
|
||||
|
||||
void background_calibration();
|
||||
void background_calibration_result();
|
||||
void abc_get_period();
|
||||
void abc_enable();
|
||||
void abc_disable();
|
||||
|
||||
protected:
|
||||
uint16_t senseair_checksum_(uint8_t *ptr, uint8_t length);
|
||||
bool senseair_write_command_(const uint8_t *command, uint8_t *response);
|
||||
bool senseair_write_command_(const uint8_t *command, uint8_t *response, uint8_t response_length);
|
||||
|
||||
sensor::Sensor *co2_sensor_{nullptr};
|
||||
};
|
||||
|
||||
template<typename... Ts> class SenseAirBackgroundCalibrationAction : public Action<Ts...> {
|
||||
public:
|
||||
SenseAirBackgroundCalibrationAction(SenseAirComponent *senseair) : senseair_(senseair) {}
|
||||
|
||||
void play(Ts... x) override { this->senseair_->background_calibration(); }
|
||||
|
||||
protected:
|
||||
SenseAirComponent *senseair_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class SenseAirBackgroundCalibrationResultAction : public Action<Ts...> {
|
||||
public:
|
||||
SenseAirBackgroundCalibrationResultAction(SenseAirComponent *senseair) : senseair_(senseair) {}
|
||||
|
||||
void play(Ts... x) override { this->senseair_->background_calibration_result(); }
|
||||
|
||||
protected:
|
||||
SenseAirComponent *senseair_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class SenseAirABCEnableAction : public Action<Ts...> {
|
||||
public:
|
||||
SenseAirABCEnableAction(SenseAirComponent *senseair) : senseair_(senseair) {}
|
||||
|
||||
void play(Ts... x) override { this->senseair_->abc_enable(); }
|
||||
|
||||
protected:
|
||||
SenseAirComponent *senseair_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class SenseAirABCDisableAction : public Action<Ts...> {
|
||||
public:
|
||||
SenseAirABCDisableAction(SenseAirComponent *senseair) : senseair_(senseair) {}
|
||||
|
||||
void play(Ts... x) override { this->senseair_->abc_disable(); }
|
||||
|
||||
protected:
|
||||
SenseAirComponent *senseair_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class SenseAirABCGetPeriodAction : public Action<Ts...> {
|
||||
public:
|
||||
SenseAirABCGetPeriodAction(SenseAirComponent *senseair) : senseair_(senseair) {}
|
||||
|
||||
void play(Ts... x) override { this->senseair_->abc_get_period(); }
|
||||
|
||||
protected:
|
||||
SenseAirComponent *senseair_;
|
||||
};
|
||||
|
||||
} // namespace senseair
|
||||
} // namespace esphome
|
||||
|
@ -1,5 +1,7 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
from esphome.automation import maybe_simple_id
|
||||
from esphome.components import sensor, uart
|
||||
from esphome.const import (
|
||||
CONF_CO2,
|
||||
@ -15,6 +17,21 @@ senseair_ns = cg.esphome_ns.namespace("senseair")
|
||||
SenseAirComponent = senseair_ns.class_(
|
||||
"SenseAirComponent", cg.PollingComponent, uart.UARTDevice
|
||||
)
|
||||
SenseAirBackgroundCalibrationAction = senseair_ns.class_(
|
||||
"SenseAirBackgroundCalibrationAction", automation.Action
|
||||
)
|
||||
SenseAirBackgroundCalibrationResultAction = senseair_ns.class_(
|
||||
"SenseAirBackgroundCalibrationResultAction", automation.Action
|
||||
)
|
||||
SenseAirABCEnableAction = senseair_ns.class_(
|
||||
"SenseAirABCEnableAction", automation.Action
|
||||
)
|
||||
SenseAirABCDisableAction = senseair_ns.class_(
|
||||
"SenseAirABCDisableAction", automation.Action
|
||||
)
|
||||
SenseAirABCGetPeriodAction = senseair_ns.class_(
|
||||
"SenseAirABCGetPeriodAction", automation.Action
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
@ -38,3 +55,34 @@ def to_code(config):
|
||||
if CONF_CO2 in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_CO2])
|
||||
cg.add(var.set_co2_sensor(sens))
|
||||
|
||||
|
||||
CALIBRATION_ACTION_SCHEMA = maybe_simple_id(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(SenseAirComponent),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"senseair.background_calibration",
|
||||
SenseAirBackgroundCalibrationAction,
|
||||
CALIBRATION_ACTION_SCHEMA,
|
||||
)
|
||||
@automation.register_action(
|
||||
"senseair.background_calibration_result",
|
||||
SenseAirBackgroundCalibrationResultAction,
|
||||
CALIBRATION_ACTION_SCHEMA,
|
||||
)
|
||||
@automation.register_action(
|
||||
"senseair.abc_enable", SenseAirABCEnableAction, CALIBRATION_ACTION_SCHEMA
|
||||
)
|
||||
@automation.register_action(
|
||||
"senseair.abc_disable", SenseAirABCDisableAction, CALIBRATION_ACTION_SCHEMA
|
||||
)
|
||||
@automation.register_action(
|
||||
"senseair.abc_get_period", SenseAirABCGetPeriodAction, CALIBRATION_ACTION_SCHEMA
|
||||
)
|
||||
def senseair_action_to_code(config, action_id, template_arg, args):
|
||||
paren = yield cg.get_variable(config[CONF_ID])
|
||||
yield cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
@ -660,11 +660,6 @@ sensor:
|
||||
- platform: pulse_width
|
||||
name: Pulse Width
|
||||
pin: GPIO12
|
||||
- platform: senseair
|
||||
uart_id: uart0
|
||||
co2:
|
||||
name: 'SenseAir CO2 Value'
|
||||
update_interval: 15s
|
||||
- platform: sm300d2
|
||||
uart_id: uart0
|
||||
co2:
|
||||
|
@ -70,6 +70,18 @@ sensor:
|
||||
- platform: ble_rssi
|
||||
service_uuid: '11223344-5566-7788-99aa-bbccddeeff00'
|
||||
name: 'BLE Test Service 128'
|
||||
- platform: senseair
|
||||
id: senseair0
|
||||
co2:
|
||||
name: 'SenseAir CO2 Value'
|
||||
on_value:
|
||||
then:
|
||||
- senseair.background_calibration: senseair0
|
||||
- senseair.background_calibration_result: senseair0
|
||||
- senseair.abc_get_period: senseair0
|
||||
- senseair.abc_enable: senseair0
|
||||
- senseair.abc_disable: senseair0
|
||||
update_interval: 15s
|
||||
- platform: ruuvitag
|
||||
mac_address: FF:56:D3:2F:7D:E8
|
||||
humidity:
|
||||
|
Loading…
Reference in New Issue
Block a user