ESPHome  2024.6.1
mhz19.cpp
Go to the documentation of this file.
1 #include "mhz19.h"
2 #include "esphome/core/log.h"
3 
4 #include <cinttypes>
5 
6 namespace esphome {
7 namespace mhz19 {
8 
9 static const char *const TAG = "mhz19";
10 static const uint8_t MHZ19_REQUEST_LENGTH = 8;
11 static const uint8_t MHZ19_RESPONSE_LENGTH = 9;
12 static const uint8_t MHZ19_COMMAND_GET_PPM[] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00};
13 static const uint8_t MHZ19_COMMAND_ABC_ENABLE[] = {0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00};
14 static const uint8_t MHZ19_COMMAND_ABC_DISABLE[] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00};
15 static const uint8_t MHZ19_COMMAND_CALIBRATE_ZERO[] = {0xFF, 0x01, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00};
16 
17 uint8_t mhz19_checksum(const uint8_t *command) {
18  uint8_t sum = 0;
19  for (uint8_t i = 1; i < MHZ19_REQUEST_LENGTH; i++) {
20  sum += command[i];
21  }
22  return 0xFF - sum + 0x01;
23 }
24 
26  if (this->abc_boot_logic_ == MHZ19_ABC_ENABLED) {
27  this->abc_enable();
28  } else if (this->abc_boot_logic_ == MHZ19_ABC_DISABLED) {
29  this->abc_disable();
30  }
31 }
32 
34  uint32_t now_ms = millis();
35  uint32_t warmup_ms = this->warmup_seconds_ * 1000;
36  if (now_ms < warmup_ms) {
37  ESP_LOGW(TAG, "MHZ19 warming up, %" PRIu32 " s left", (warmup_ms - now_ms) / 1000);
38  this->status_set_warning();
39  return;
40  }
41 
42  uint8_t response[MHZ19_RESPONSE_LENGTH];
43  if (!this->mhz19_write_command_(MHZ19_COMMAND_GET_PPM, response)) {
44  ESP_LOGW(TAG, "Reading data from MHZ19 failed!");
45  this->status_set_warning();
46  return;
47  }
48 
49  if (response[0] != 0xFF || response[1] != 0x86) {
50  ESP_LOGW(TAG, "Invalid preamble from MHZ19!");
51  this->status_set_warning();
52  return;
53  }
54 
55  uint8_t checksum = mhz19_checksum(response);
56  if (response[8] != checksum) {
57  ESP_LOGW(TAG, "MHZ19 Checksum doesn't match: 0x%02X!=0x%02X", response[8], checksum);
58  this->status_set_warning();
59  return;
60  }
61 
62  this->status_clear_warning();
63  const uint16_t ppm = (uint16_t(response[2]) << 8) | response[3];
64  const int temp = int(response[4]) - 40;
65  const uint8_t status = response[5];
66 
67  ESP_LOGD(TAG, "MHZ19 Received CO₂=%uppm Temperature=%d°C Status=0x%02X", ppm, temp, status);
68  if (this->co2_sensor_ != nullptr)
69  this->co2_sensor_->publish_state(ppm);
70  if (this->temperature_sensor_ != nullptr)
72 }
73 
75  ESP_LOGD(TAG, "MHZ19 Calibrating zero point");
76  this->mhz19_write_command_(MHZ19_COMMAND_CALIBRATE_ZERO, nullptr);
77 }
78 
80  ESP_LOGD(TAG, "MHZ19 Enabling automatic baseline calibration");
81  this->mhz19_write_command_(MHZ19_COMMAND_ABC_ENABLE, nullptr);
82 }
83 
85  ESP_LOGD(TAG, "MHZ19 Disabling automatic baseline calibration");
86  this->mhz19_write_command_(MHZ19_COMMAND_ABC_DISABLE, nullptr);
87 }
88 
89 bool MHZ19Component::mhz19_write_command_(const uint8_t *command, uint8_t *response) {
90  // Empty RX Buffer
91  while (this->available())
92  this->read();
93  this->write_array(command, MHZ19_REQUEST_LENGTH);
94  this->write_byte(mhz19_checksum(command));
95  this->flush();
96 
97  if (response == nullptr)
98  return true;
99 
100  return this->read_array(response, MHZ19_RESPONSE_LENGTH);
101 }
104  ESP_LOGCONFIG(TAG, "MH-Z19:");
105  LOG_SENSOR(" ", "CO2", this->co2_sensor_);
106  LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
107  this->check_uart_settings(9600);
108 
109  if (this->abc_boot_logic_ == MHZ19_ABC_ENABLED) {
110  ESP_LOGCONFIG(TAG, " Automatic baseline calibration enabled on boot");
111  } else if (this->abc_boot_logic_ == MHZ19_ABC_DISABLED) {
112  ESP_LOGCONFIG(TAG, " Automatic baseline calibration disabled on boot");
113  }
114 
115  ESP_LOGCONFIG(TAG, " Warmup time: %" PRIu32 " s", this->warmup_seconds_);
116 }
117 
118 } // namespace mhz19
119 } // 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:33
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:103
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:89
uint8_t checksum
Definition: bl0939.h:35
void setup() override
Definition: mhz19.cpp:25
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:102
uint8_t mhz19_checksum(const uint8_t *command)
Definition: mhz19.cpp:17