ESPHome  2024.4.0
mhz19.cpp
Go to the documentation of this file.
1 #include "mhz19.h"
2 #include "esphome/core/log.h"
3 
4 namespace esphome {
5 namespace mhz19 {
6 
7 static const char *const TAG = "mhz19";
8 static const uint8_t MHZ19_REQUEST_LENGTH = 8;
9 static const uint8_t MHZ19_RESPONSE_LENGTH = 9;
10 static const uint8_t MHZ19_COMMAND_GET_PPM[] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00};
11 static const uint8_t MHZ19_COMMAND_ABC_ENABLE[] = {0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00};
12 static const uint8_t MHZ19_COMMAND_ABC_DISABLE[] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00};
13 static const uint8_t MHZ19_COMMAND_CALIBRATE_ZERO[] = {0xFF, 0x01, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00};
14 
15 uint8_t mhz19_checksum(const uint8_t *command) {
16  uint8_t sum = 0;
17  for (uint8_t i = 1; i < MHZ19_REQUEST_LENGTH; i++) {
18  sum += command[i];
19  }
20  return 0xFF - sum + 0x01;
21 }
22 
24  if (this->abc_boot_logic_ == MHZ19_ABC_ENABLED) {
25  this->abc_enable();
26  } else if (this->abc_boot_logic_ == MHZ19_ABC_DISABLED) {
27  this->abc_disable();
28  }
29 }
30 
32  uint32_t now_ms = millis();
33  uint32_t warmup_ms = this->warmup_seconds_ * 1000;
34  if (now_ms < warmup_ms) {
35  ESP_LOGW(TAG, "MHZ19 warming up, %ds left", (warmup_ms - now_ms) / 1000);
36  this->status_set_warning();
37  return;
38  }
39 
40  uint8_t response[MHZ19_RESPONSE_LENGTH];
41  if (!this->mhz19_write_command_(MHZ19_COMMAND_GET_PPM, response)) {
42  ESP_LOGW(TAG, "Reading data from MHZ19 failed!");
43  this->status_set_warning();
44  return;
45  }
46 
47  if (response[0] != 0xFF || response[1] != 0x86) {
48  ESP_LOGW(TAG, "Invalid preamble from MHZ19!");
49  this->status_set_warning();
50  return;
51  }
52 
53  uint8_t checksum = mhz19_checksum(response);
54  if (response[8] != checksum) {
55  ESP_LOGW(TAG, "MHZ19 Checksum doesn't match: 0x%02X!=0x%02X", response[8], checksum);
56  this->status_set_warning();
57  return;
58  }
59 
60  this->status_clear_warning();
61  const uint16_t ppm = (uint16_t(response[2]) << 8) | response[3];
62  const int temp = int(response[4]) - 40;
63  const uint8_t status = response[5];
64 
65  ESP_LOGD(TAG, "MHZ19 Received CO₂=%uppm Temperature=%d°C Status=0x%02X", ppm, temp, status);
66  if (this->co2_sensor_ != nullptr)
67  this->co2_sensor_->publish_state(ppm);
68  if (this->temperature_sensor_ != nullptr)
70 }
71 
73  ESP_LOGD(TAG, "MHZ19 Calibrating zero point");
74  this->mhz19_write_command_(MHZ19_COMMAND_CALIBRATE_ZERO, nullptr);
75 }
76 
78  ESP_LOGD(TAG, "MHZ19 Enabling automatic baseline calibration");
79  this->mhz19_write_command_(MHZ19_COMMAND_ABC_ENABLE, nullptr);
80 }
81 
83  ESP_LOGD(TAG, "MHZ19 Disabling automatic baseline calibration");
84  this->mhz19_write_command_(MHZ19_COMMAND_ABC_DISABLE, nullptr);
85 }
86 
87 bool MHZ19Component::mhz19_write_command_(const uint8_t *command, uint8_t *response) {
88  // Empty RX Buffer
89  while (this->available())
90  this->read();
91  this->write_array(command, MHZ19_REQUEST_LENGTH);
92  this->write_byte(mhz19_checksum(command));
93  this->flush();
94 
95  if (response == nullptr)
96  return true;
97 
98  return this->read_array(response, MHZ19_RESPONSE_LENGTH);
99 }
102  ESP_LOGCONFIG(TAG, "MH-Z19:");
103  LOG_SENSOR(" ", "CO2", this->co2_sensor_);
104  LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
105  this->check_uart_settings(9600);
106 
107  if (this->abc_boot_logic_ == MHZ19_ABC_ENABLED) {
108  ESP_LOGCONFIG(TAG, " Automatic baseline calibration enabled on boot");
109  } else if (this->abc_boot_logic_ == MHZ19_ABC_DISABLED) {
110  ESP_LOGCONFIG(TAG, " Automatic baseline calibration disabled on boot");
111  }
112 
113  ESP_LOGCONFIG(TAG, " Warmup seconds: %ds", this->warmup_seconds_);
114 }
115 
116 } // namespace mhz19
117 } // namespace esphome
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:19
void update() override
Definition: mhz19.cpp:31
optional< std::array< uint8_t, N > > read_array()
Definition: uart.h:33
void write_array(const uint8_t *data, size_t len)
Definition: uart.h:21
void write_byte(uint8_t data)
Definition: uart.h:19
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
sensor::Sensor * temperature_sensor_
Definition: mhz19.h:33
sensor::Sensor * co2_sensor_
Definition: mhz19.h:34
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
void check_uart_settings(uint32_t baud_rate, uint8_t stop_bits=1, UARTParityOptions parity=UART_CONFIG_PARITY_NONE, uint8_t data_bits=8)
Check that the configuration of the UART bus matches the provided values and otherwise print a warnin...
Definition: uart.cpp:13
void status_clear_warning()
Definition: component.cpp:166
void dump_config() override
Definition: mhz19.cpp:101
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
bool mhz19_write_command_(const uint8_t *command, uint8_t *response)
Definition: mhz19.cpp:87
uint8_t checksum
Definition: bl0939.h:35
void setup() override
Definition: mhz19.cpp:23
uint8_t status
Definition: bl0942.h:23
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
MHZ19ABCLogic abc_boot_logic_
Definition: mhz19.h:35
float get_setup_priority() const override
Definition: mhz19.cpp:100
uint8_t mhz19_checksum(const uint8_t *command)
Definition: mhz19.cpp:15