mirror of
https://github.com/esphome/esphome.git
synced 2024-11-25 16:38:16 +01:00
SenseAir S8 CO2 sensor support (#705)
* Компилится * Tests * Checksum calculation * Read status
This commit is contained in:
parent
6ceb975a3a
commit
d51c0f13c0
5 changed files with 133 additions and 0 deletions
0
esphome/components/senseair/__init__.py
Normal file
0
esphome/components/senseair/__init__.py
Normal file
79
esphome/components/senseair/senseair.cpp
Normal file
79
esphome/components/senseair/senseair.cpp
Normal file
|
@ -0,0 +1,79 @@
|
|||
#include "senseair.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
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};
|
||||
|
||||
void SenseAirComponent::update() {
|
||||
uint8_t response[SENSEAIR_RESPONSE_LENGTH];
|
||||
if (!this->senseair_write_command_(SENSEAIR_COMMAND_GET_PPM, response)) {
|
||||
ESP_LOGW(TAG, "Reading data from SenseAir failed!");
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
|
||||
if (response[0] != 0xFE || response[1] != 0x04) {
|
||||
ESP_LOGW(TAG, "Invalid preamble from SenseAir!");
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t calc_checksum = this->senseair_checksum_(response, 11);
|
||||
uint16_t resp_checksum = (uint16_t(response[12]) << 8) | response[11];
|
||||
if (resp_checksum != calc_checksum) {
|
||||
ESP_LOGW(TAG, "SenseAir checksum doesn't match: 0x%02X!=0x%02X", resp_checksum, calc_checksum);
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
|
||||
this->status_clear_warning();
|
||||
const uint8_t length = response[2];
|
||||
const uint16_t status = (uint16_t(response[3]) << 8) | response[4];
|
||||
const uint16_t ppm = (uint16_t(response[length + 1]) << 8) | response[length + 2];
|
||||
|
||||
ESP_LOGD(TAG, "SenseAir Received CO₂=%uppm Status=0x%02X", ppm, status);
|
||||
if (this->co2_sensor_ != nullptr)
|
||||
this->co2_sensor_->publish_state(ppm);
|
||||
}
|
||||
|
||||
uint16_t SenseAirComponent::senseair_checksum_(uint8_t *ptr, uint8_t length) {
|
||||
uint16_t crc = 0xFFFF;
|
||||
uint8_t i;
|
||||
while (length--) {
|
||||
crc ^= *ptr++;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if ((crc & 0x01) != 0) {
|
||||
crc >>= 1;
|
||||
crc ^= 0xA001;
|
||||
} else {
|
||||
crc >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
bool SenseAirComponent::senseair_write_command_(const uint8_t *command, uint8_t *response) {
|
||||
this->flush();
|
||||
this->write_array(command, SENSEAIR_REQUEST_LENGTH);
|
||||
|
||||
if (response == nullptr)
|
||||
return true;
|
||||
|
||||
bool ret = this->read_array(response, SENSEAIR_RESPONSE_LENGTH);
|
||||
this->flush();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SenseAirComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "SenseAir:");
|
||||
LOG_SENSOR(" ", "CO2", this->co2_sensor_);
|
||||
}
|
||||
|
||||
} // namespace senseair
|
||||
} // namespace esphome
|
26
esphome/components/senseair/senseair.h
Normal file
26
esphome/components/senseair/senseair.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace senseair {
|
||||
|
||||
class SenseAirComponent : public PollingComponent, public uart::UARTDevice {
|
||||
public:
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
void set_co2_sensor(sensor::Sensor *co2_sensor) { co2_sensor_ = co2_sensor; }
|
||||
|
||||
void update() override;
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
uint16_t senseair_checksum_(uint8_t *ptr, uint8_t length);
|
||||
bool senseair_write_command_(const uint8_t *command, uint8_t *response);
|
||||
|
||||
sensor::Sensor *co2_sensor_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace senseair
|
||||
} // namespace esphome
|
24
esphome/components/senseair/sensor.py
Normal file
24
esphome/components/senseair/sensor.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, uart
|
||||
from esphome.const import CONF_CO2, CONF_ID, ICON_PERIODIC_TABLE_CO2, UNIT_PARTS_PER_MILLION
|
||||
|
||||
DEPENDENCIES = ['uart']
|
||||
|
||||
senseair_ns = cg.esphome_ns.namespace('senseair')
|
||||
SenseAirComponent = senseair_ns.class_('SenseAirComponent', cg.PollingComponent, uart.UARTDevice)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema({
|
||||
cv.GenerateID(): cv.declare_id(SenseAirComponent),
|
||||
cv.Required(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_PERIODIC_TABLE_CO2, 0),
|
||||
}).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield uart.register_uart_device(var, config)
|
||||
|
||||
if CONF_CO2 in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_CO2])
|
||||
cg.add(var.set_co2_sensor(sens))
|
|
@ -511,6 +511,10 @@ sensor:
|
|||
- platform: pulse_width
|
||||
name: Pulse Width
|
||||
pin: GPIO12
|
||||
- platform: senseair
|
||||
co2:
|
||||
name: "SenseAir CO2 Value"
|
||||
update_interval: 15s
|
||||
- platform: sht3xd
|
||||
temperature:
|
||||
name: "Living Room Temperature 8"
|
||||
|
|
Loading…
Reference in a new issue