mirror of
https://github.com/esphome/esphome.git
synced 2024-12-25 23:14:54 +01:00
PMSX003: Add support for specifying the update interval and spinning down (#3053)
Co-authored-by: Otto Winter <otto@otto-winter.com>
This commit is contained in:
parent
782186e13d
commit
98c733108e
4 changed files with 125 additions and 3 deletions
|
@ -49,6 +49,47 @@ void PMSX003Component::set_formaldehyde_sensor(sensor::Sensor *formaldehyde_sens
|
|||
|
||||
void PMSX003Component::loop() {
|
||||
const uint32_t now = millis();
|
||||
|
||||
// If we update less often than it takes the device to stabilise, spin the fan down
|
||||
// rather than running it constantly. It does take some time to stabilise, so we
|
||||
// need to keep track of what state we're in.
|
||||
if (this->update_interval_ > PMS_STABILISING_MS) {
|
||||
if (this->initialised_ == 0) {
|
||||
this->send_command_(PMS_CMD_AUTO_MANUAL, 0);
|
||||
this->send_command_(PMS_CMD_ON_STANDBY, 1);
|
||||
this->initialised_ = 1;
|
||||
}
|
||||
switch (this->state_) {
|
||||
case PMSX003_STATE_IDLE:
|
||||
// Power on the sensor now so it'll be ready when we hit the update time
|
||||
if (now - this->last_update_ < (this->update_interval_ - PMS_STABILISING_MS))
|
||||
return;
|
||||
|
||||
this->state_ = PMSX003_STATE_STABILISING;
|
||||
this->send_command_(PMS_CMD_ON_STANDBY, 1);
|
||||
this->fan_on_time_ = now;
|
||||
return;
|
||||
case PMSX003_STATE_STABILISING:
|
||||
// wait for the sensor to be stable
|
||||
if (now - this->fan_on_time_ < PMS_STABILISING_MS)
|
||||
return;
|
||||
// consume any command responses that are in the serial buffer
|
||||
while (this->available())
|
||||
this->read_byte(&this->data_[0]);
|
||||
// Trigger a new read
|
||||
this->send_command_(PMS_CMD_TRIG_MANUAL, 0);
|
||||
this->state_ = PMSX003_STATE_WAITING;
|
||||
break;
|
||||
case PMSX003_STATE_WAITING:
|
||||
// Just go ahead and read stuff
|
||||
break;
|
||||
}
|
||||
} else if (now - this->last_update_ < this->update_interval_) {
|
||||
// Otherwise just leave the sensor powered up and come back when we hit the update
|
||||
// time
|
||||
return;
|
||||
}
|
||||
|
||||
if (now - this->last_transmission_ >= 500) {
|
||||
// last transmission too long ago. Reset RX index.
|
||||
this->data_index_ = 0;
|
||||
|
@ -65,6 +106,7 @@ void PMSX003Component::loop() {
|
|||
// finished
|
||||
this->parse_data_();
|
||||
this->data_index_ = 0;
|
||||
this->last_update_ = now;
|
||||
} else if (!*check) {
|
||||
// wrong data
|
||||
this->data_index_ = 0;
|
||||
|
@ -131,6 +173,25 @@ optional<bool> PMSX003Component::check_byte_() {
|
|||
return {};
|
||||
}
|
||||
|
||||
void PMSX003Component::send_command_(uint8_t cmd, uint16_t data) {
|
||||
this->data_index_ = 0;
|
||||
this->data_[data_index_++] = 0x42;
|
||||
this->data_[data_index_++] = 0x4D;
|
||||
this->data_[data_index_++] = cmd;
|
||||
this->data_[data_index_++] = (data >> 8) & 0xFF;
|
||||
this->data_[data_index_++] = (data >> 0) & 0xFF;
|
||||
int sum = 0;
|
||||
for (int i = 0; i < data_index_; i++) {
|
||||
sum += this->data_[i];
|
||||
}
|
||||
this->data_[data_index_++] = (sum >> 8) & 0xFF;
|
||||
this->data_[data_index_++] = (sum >> 0) & 0xFF;
|
||||
for (int i = 0; i < data_index_; i++) {
|
||||
this->write_byte(this->data_[i]);
|
||||
}
|
||||
this->data_index_ = 0;
|
||||
}
|
||||
|
||||
void PMSX003Component::parse_data_() {
|
||||
switch (this->type_) {
|
||||
case PMSX003_TYPE_5003ST: {
|
||||
|
@ -218,6 +279,13 @@ void PMSX003Component::parse_data_() {
|
|||
}
|
||||
}
|
||||
|
||||
// Spin down the sensor again if we aren't going to need it until more time has
|
||||
// passed than it takes to stabilise
|
||||
if (this->update_interval_ > PMS_STABILISING_MS) {
|
||||
this->send_command_(PMS_CMD_ON_STANDBY, 0);
|
||||
this->state_ = PMSX003_STATE_IDLE;
|
||||
}
|
||||
|
||||
this->status_clear_warning();
|
||||
}
|
||||
uint16_t PMSX003Component::get_16_bit_uint_(uint8_t start_index) {
|
||||
|
|
|
@ -7,6 +7,13 @@
|
|||
namespace esphome {
|
||||
namespace pmsx003 {
|
||||
|
||||
// known command bytes
|
||||
#define PMS_CMD_AUTO_MANUAL 0xE1 // data=0: perform measurement manually, data=1: perform measurement automatically
|
||||
#define PMS_CMD_TRIG_MANUAL 0xE2 // trigger a manual measurement
|
||||
#define PMS_CMD_ON_STANDBY 0xE4 // data=0: go to standby mode, data=1: go to normal mode
|
||||
|
||||
static const uint16_t PMS_STABILISING_MS = 30000; // time taken for the sensor to become stable after power on
|
||||
|
||||
enum PMSX003Type {
|
||||
PMSX003_TYPE_X003 = 0,
|
||||
PMSX003_TYPE_5003T,
|
||||
|
@ -14,6 +21,12 @@ enum PMSX003Type {
|
|||
PMSX003_TYPE_5003S,
|
||||
};
|
||||
|
||||
enum PMSX003State {
|
||||
PMSX003_STATE_IDLE = 0,
|
||||
PMSX003_STATE_STABILISING,
|
||||
PMSX003_STATE_WAITING,
|
||||
};
|
||||
|
||||
class PMSX003Component : public uart::UARTDevice, public Component {
|
||||
public:
|
||||
PMSX003Component() = default;
|
||||
|
@ -23,6 +36,8 @@ class PMSX003Component : public uart::UARTDevice, public Component {
|
|||
|
||||
void set_type(PMSX003Type type) { type_ = type; }
|
||||
|
||||
void set_update_interval(uint32_t val) { update_interval_ = val; };
|
||||
|
||||
void set_pm_1_0_std_sensor(sensor::Sensor *pm_1_0_std_sensor);
|
||||
void set_pm_2_5_std_sensor(sensor::Sensor *pm_2_5_std_sensor);
|
||||
void set_pm_10_0_std_sensor(sensor::Sensor *pm_10_0_std_sensor);
|
||||
|
@ -45,11 +60,17 @@ class PMSX003Component : public uart::UARTDevice, public Component {
|
|||
protected:
|
||||
optional<bool> check_byte_();
|
||||
void parse_data_();
|
||||
void send_command_(uint8_t cmd, uint16_t data);
|
||||
uint16_t get_16_bit_uint_(uint8_t start_index);
|
||||
|
||||
uint8_t data_[64];
|
||||
uint8_t data_index_{0};
|
||||
uint8_t initialised_{0};
|
||||
uint32_t fan_on_time_{0};
|
||||
uint32_t last_update_{0};
|
||||
uint32_t last_transmission_{0};
|
||||
uint32_t update_interval_{0};
|
||||
PMSX003State state_{PMSX003_STATE_IDLE};
|
||||
PMSX003Type type_;
|
||||
|
||||
// "Standard Particle"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, uart
|
||||
|
||||
from esphome.const import (
|
||||
CONF_FORMALDEHYDE,
|
||||
CONF_HUMIDITY,
|
||||
|
@ -17,6 +18,7 @@ from esphome.const import (
|
|||
CONF_PM_2_5UM,
|
||||
CONF_PM_5_0UM,
|
||||
CONF_PM_10_0UM,
|
||||
CONF_UPDATE_INTERVAL,
|
||||
CONF_TEMPERATURE,
|
||||
CONF_TYPE,
|
||||
DEVICE_CLASS_PM1,
|
||||
|
@ -44,6 +46,7 @@ TYPE_PMS5003ST = "PMS5003ST"
|
|||
TYPE_PMS5003S = "PMS5003S"
|
||||
|
||||
PMSX003Type = pmsx003_ns.enum("PMSX003Type")
|
||||
|
||||
PMSX003_TYPES = {
|
||||
TYPE_PMSX003: PMSX003Type.PMSX003_TYPE_X003,
|
||||
TYPE_PMS5003T: PMSX003Type.PMSX003_TYPE_5003T,
|
||||
|
@ -68,6 +71,17 @@ def validate_pmsx003_sensors(value):
|
|||
return value
|
||||
|
||||
|
||||
def validate_update_interval(value):
|
||||
value = cv.positive_time_period_milliseconds(value)
|
||||
if value == cv.time_period("0s"):
|
||||
return value
|
||||
if value < cv.time_period("30s"):
|
||||
raise cv.Invalid(
|
||||
"Update interval must be greater than or equal to 30 seconds if set."
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
|
@ -157,6 +171,7 @@ CONFIG_SCHEMA = (
|
|||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_UPDATE_INTERVAL, default="0s"): validate_update_interval,
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
|
@ -164,6 +179,17 @@ CONFIG_SCHEMA = (
|
|||
)
|
||||
|
||||
|
||||
def final_validate(config):
|
||||
require_tx = config[CONF_UPDATE_INTERVAL] > cv.time_period("0s")
|
||||
schema = uart.final_validate_device_schema(
|
||||
"pmsx003", baud_rate=9600, require_rx=True, require_tx=require_tx
|
||||
)
|
||||
schema(config)
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = final_validate
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
@ -230,3 +256,5 @@ async def to_code(config):
|
|||
if CONF_FORMALDEHYDE in config:
|
||||
sens = await sensor.new_sensor(config[CONF_FORMALDEHYDE])
|
||||
cg.add(var.set_formaldehyde_sensor(sens))
|
||||
|
||||
cg.add(var.set_update_interval(config[CONF_UPDATE_INTERVAL]))
|
||||
|
|
|
@ -266,6 +266,10 @@ uart:
|
|||
stop_bits: 2
|
||||
# Specifically added for testing debug with no options at all.
|
||||
debug:
|
||||
- id: uart8
|
||||
tx_pin: GPIO4
|
||||
rx_pin: GPIO5
|
||||
baud_rate: 9600
|
||||
|
||||
modbus:
|
||||
uart_id: uart1
|
||||
|
@ -559,7 +563,7 @@ sensor:
|
|||
name: 'AQI'
|
||||
calculation_type: 'AQI'
|
||||
- platform: pmsx003
|
||||
uart_id: uart2
|
||||
uart_id: uart8
|
||||
type: PMSX003
|
||||
pm_1_0:
|
||||
name: 'PM 1.0 Concentration'
|
||||
|
@ -585,8 +589,9 @@ sensor:
|
|||
name: 'Particulate Count >5.0um'
|
||||
pm_10_0um:
|
||||
name: 'Particulate Count >10.0um'
|
||||
update_interval: 30s
|
||||
- platform: pmsx003
|
||||
uart_id: uart2
|
||||
uart_id: uart5
|
||||
type: PMS5003T
|
||||
pm_2_5:
|
||||
name: 'PM 2.5 Concentration'
|
||||
|
@ -595,7 +600,7 @@ sensor:
|
|||
humidity:
|
||||
name: 'PMS Humidity'
|
||||
- platform: pmsx003
|
||||
uart_id: uart2
|
||||
uart_id: uart6
|
||||
type: PMS5003ST
|
||||
pm_1_0:
|
||||
name: 'PM 1.0 Concentration'
|
||||
|
|
Loading…
Reference in a new issue