Background calibration & ABC commands for SenseAir S8 (#1623)

This commit is contained in:
Niccolò Maggioni 2021-03-22 00:59:41 +01:00 committed by GitHub
parent 89d0d41c5a
commit 8f1eb77e05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 185 additions and 12 deletions

View File

@ -6,12 +6,20 @@ namespace senseair {
static const char *TAG = "senseair"; static const char *TAG = "senseair";
static const uint8_t SENSEAIR_REQUEST_LENGTH = 8; static const uint8_t SENSEAIR_REQUEST_LENGTH = 8;
static const uint8_t SENSEAIR_RESPONSE_LENGTH = 13; static const uint8_t SENSEAIR_PPM_STATUS_RESPONSE_LENGTH = 13;
static const uint8_t SENSEAIR_COMMAND_GET_PPM[] = {0xFE, 0x04, 0x00, 0x00, 0x00, 0x04, 0xE5, 0xC6}; 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() { void SenseAirComponent::update() {
uint8_t response[SENSEAIR_RESPONSE_LENGTH]; uint8_t response[SENSEAIR_PPM_STATUS_RESPONSE_LENGTH];
if (!this->senseair_write_command_(SENSEAIR_COMMAND_GET_PPM, response)) { if (!this->senseair_write_command_(SENSEAIR_COMMAND_GET_PPM_STATUS, response, SENSEAIR_PPM_STATUS_RESPONSE_LENGTH)) {
ESP_LOGW(TAG, "Reading data from SenseAir failed!"); ESP_LOGW(TAG, "Reading data from SenseAir failed!");
this->status_set_warning(); this->status_set_warning();
return; return;
@ -69,14 +77,67 @@ uint16_t SenseAirComponent::senseair_checksum_(uint8_t *ptr, uint8_t length) {
return crc; 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->flush();
this->write_array(command, SENSEAIR_REQUEST_LENGTH); this->write_array(command, SENSEAIR_REQUEST_LENGTH);
if (response == nullptr) if (response == nullptr)
return true; return true;
bool ret = this->read_array(response, SENSEAIR_RESPONSE_LENGTH); bool ret = this->read_array(response, response_length);
this->flush(); this->flush();
return ret; return ret;
} }

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/components/sensor/sensor.h" #include "esphome/components/sensor/sensor.h"
#include "esphome/components/uart/uart.h" #include "esphome/components/uart/uart.h"
@ -15,12 +16,68 @@ class SenseAirComponent : public PollingComponent, public uart::UARTDevice {
void update() override; void update() override;
void dump_config() override; void dump_config() override;
void background_calibration();
void background_calibration_result();
void abc_get_period();
void abc_enable();
void abc_disable();
protected: protected:
uint16_t senseair_checksum_(uint8_t *ptr, uint8_t length); 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}; 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 senseair
} // namespace esphome } // namespace esphome

View File

@ -1,5 +1,7 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv 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.components import sensor, uart
from esphome.const import ( from esphome.const import (
CONF_CO2, CONF_CO2,
@ -15,6 +17,21 @@ senseair_ns = cg.esphome_ns.namespace("senseair")
SenseAirComponent = senseair_ns.class_( SenseAirComponent = senseair_ns.class_(
"SenseAirComponent", cg.PollingComponent, uart.UARTDevice "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 = ( CONFIG_SCHEMA = (
cv.Schema( cv.Schema(
@ -38,3 +55,34 @@ def to_code(config):
if CONF_CO2 in config: if CONF_CO2 in config:
sens = yield sensor.new_sensor(config[CONF_CO2]) sens = yield sensor.new_sensor(config[CONF_CO2])
cg.add(var.set_co2_sensor(sens)) 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)

View File

@ -660,11 +660,6 @@ sensor:
- platform: pulse_width - platform: pulse_width
name: Pulse Width name: Pulse Width
pin: GPIO12 pin: GPIO12
- platform: senseair
uart_id: uart0
co2:
name: 'SenseAir CO2 Value'
update_interval: 15s
- platform: sm300d2 - platform: sm300d2
uart_id: uart0 uart_id: uart0
co2: co2:

View File

@ -70,6 +70,18 @@ sensor:
- platform: ble_rssi - platform: ble_rssi
service_uuid: '11223344-5566-7788-99aa-bbccddeeff00' service_uuid: '11223344-5566-7788-99aa-bbccddeeff00'
name: 'BLE Test Service 128' 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 - platform: ruuvitag
mac_address: FF:56:D3:2F:7D:E8 mac_address: FF:56:D3:2F:7D:E8
humidity: humidity: