mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Background calibration & ABC commands for SenseAir S8 (#1623)
This commit is contained in:
		| @@ -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: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user