ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
mhz19.cpp
Go to the documentation of this file.
1#include "mhz19.h"
2#include "esphome/core/log.h"
3
4#include <cinttypes>
5
6namespace esphome::mhz19 {
7
8ESPHOME_LOG_TAG(TAG, "mhz19");
9static const uint8_t MHZ19_REQUEST_LENGTH = 8;
10static const uint8_t MHZ19_RESPONSE_LENGTH = 9;
11static const uint8_t MHZ19_COMMAND_GET_PPM[] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00};
12static const uint8_t MHZ19_COMMAND_ABC_ENABLE[] = {0xFF, 0x01, 0x79, 0xA0, 0x00, 0x00, 0x00, 0x00};
13static const uint8_t MHZ19_COMMAND_ABC_DISABLE[] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00};
14static const uint8_t MHZ19_COMMAND_CALIBRATE_ZERO[] = {0xFF, 0x01, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00};
15static const uint8_t MHZ19_COMMAND_DETECTION_RANGE_0_2000PPM[] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x07, 0xD0};
16static const uint8_t MHZ19_COMMAND_DETECTION_RANGE_0_5000PPM[] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x13, 0x88};
17static const uint8_t MHZ19_COMMAND_DETECTION_RANGE_0_10000PPM[] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x27, 0x10};
18
19#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
20static const LogString *detection_range_to_log_string(MHZ19DetectionRange range) {
21 switch (range) {
23 return LOG_STR("0-2000 ppm");
25 return LOG_STR("0-5000 ppm");
27 return LOG_STR("0-10000 ppm");
28 default:
29 return LOG_STR("default");
30 }
31}
32#endif
33
34uint8_t mhz19_checksum(const uint8_t *command) { return -sum_bytes(command + 1, MHZ19_REQUEST_LENGTH - 1); }
35
38 this->abc_enable();
39 } else if (this->abc_boot_logic_ == MHZ19_ABC_DISABLED) {
40 this->abc_disable();
41 }
42
43 this->range_set(this->detection_range_);
44}
45
47 uint32_t now_ms = millis();
48 uint32_t warmup_ms = this->warmup_seconds_ * 1000;
49 if (now_ms < warmup_ms) {
50 ESP_LOGW(TAG, "MHZ19 warming up, %" PRIu32 " s left", (warmup_ms - now_ms) / 1000);
51 this->status_set_warning();
52 return;
53 }
54
55 uint8_t response[MHZ19_RESPONSE_LENGTH];
56 if (!this->mhz19_write_command_(MHZ19_COMMAND_GET_PPM, response)) {
57 ESP_LOGW(TAG, "Reading data from MHZ19 failed!");
58 this->status_set_warning();
59 return;
60 }
61
62 if (response[0] != 0xFF || response[1] != 0x86) {
63 ESP_LOGW(TAG, "Invalid preamble from MHZ19!");
64 this->status_set_warning();
65 return;
66 }
67
68 uint8_t checksum = mhz19_checksum(response);
69 if (!this->status_comm_result(response[8] == checksum)) {
70 ESP_LOGW(TAG, "MHZ19 Checksum doesn't match: 0x%02X!=0x%02X", response[8], checksum);
71 return;
72 }
73
74 const uint16_t ppm = (uint16_t(response[2]) << 8) | response[3];
75 const int temp = int(response[4]) - 40;
76 const uint8_t status = response[5];
77
78 ESP_LOGD(TAG, "MHZ19 Received CO₂=%uppm Temperature=%d°C Status=0x%02X", ppm, temp, status);
79 if (this->co2_sensor_ != nullptr)
80 this->co2_sensor_->publish_state(ppm);
81 if (this->temperature_sensor_ != nullptr)
83}
84
86 ESP_LOGD(TAG, "MHZ19 Calibrating zero point");
87 this->mhz19_write_command_(MHZ19_COMMAND_CALIBRATE_ZERO, nullptr);
88}
89
91 ESP_LOGD(TAG, "MHZ19 Enabling automatic baseline calibration");
92 this->mhz19_write_command_(MHZ19_COMMAND_ABC_ENABLE, nullptr);
93}
94
96 ESP_LOGD(TAG, "MHZ19 Disabling automatic baseline calibration");
97 this->mhz19_write_command_(MHZ19_COMMAND_ABC_DISABLE, nullptr);
98}
99
101 const uint8_t *command;
102 switch (detection_range) {
104 command = MHZ19_COMMAND_DETECTION_RANGE_0_2000PPM;
105 break;
107 command = MHZ19_COMMAND_DETECTION_RANGE_0_5000PPM;
108 break;
110 command = MHZ19_COMMAND_DETECTION_RANGE_0_10000PPM;
111 break;
112 default:
113 ESP_LOGV(TAG, "Using previously set detection range (no change)");
114 return;
115 }
116 ESP_LOGD(TAG, "Setting detection range to %s", LOG_STR_ARG(detection_range_to_log_string(detection_range)));
117 this->mhz19_write_command_(command, nullptr);
118}
119
120bool MHZ19Component::mhz19_write_command_(const uint8_t *command, uint8_t *response) {
121 this->discard_available();
122 this->write_array(command, MHZ19_REQUEST_LENGTH);
123 this->write_byte(mhz19_checksum(command));
124 this->flush();
125
126 if (response == nullptr)
127 return true;
128
129 return this->read_array(response, MHZ19_RESPONSE_LENGTH);
130}
131
133 ESP_LOGCONFIG(TAG, "MH-Z19:");
134 LOG_SENSOR(" ", "CO2", this->co2_sensor_);
135 LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
136 if (this->abc_boot_logic_ == MHZ19_ABC_ENABLED) {
137 ESP_LOGCONFIG(TAG, " Automatic baseline calibration enabled on boot");
138 } else if (this->abc_boot_logic_ == MHZ19_ABC_DISABLED) {
139 ESP_LOGCONFIG(TAG, " Automatic baseline calibration disabled on boot");
140 }
141
142 ESP_LOGCONFIG(TAG, " Warmup time: %" PRIu32 " s", this->warmup_seconds_);
143 ESP_LOGCONFIG(TAG, " Detection range: %s", LOG_STR_ARG(detection_range_to_log_string(this->detection_range_)));
144}
145
146} // namespace esphome::mhz19
uint8_t checksum
Definition bl0906.h:3
uint8_t status
Definition bl0942.h:8
bool status_comm_result(bool ok)
Clears the warning when ok, else sets it with message (default "Communication failed"); returns ok.
void range_set(MHZ19DetectionRange detection_range)
Definition mhz19.cpp:100
void dump_config() override
Definition mhz19.cpp:132
MHZ19DetectionRange detection_range_
Definition mhz19.h:48
sensor::Sensor * temperature_sensor_
Definition mhz19.h:42
sensor::Sensor * co2_sensor_
Definition mhz19.h:43
bool mhz19_write_command_(const uint8_t *command, uint8_t *response)
Definition mhz19.cpp:120
MHZ19ABCLogic abc_boot_logic_
Definition mhz19.h:44
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:65
UARTFlushResult flush()
Definition uart.h:96
optional< std::array< uint8_t, N > > read_array()
Definition uart.h:43
void discard_available()
Read and drop every byte waiting in the receive buffer.
Definition uart.cpp:36
void write_byte(uint8_t data)
Definition uart.h:22
void write_array(const uint8_t *data, size_t len)
Definition uart.h:30
Range range
Definition msa3xx.h:0
@ MHZ19_ABC_DISABLED
Definition mhz19.h:12
@ MHZ19_ABC_ENABLED
Definition mhz19.h:11
ESPHOME_LOG_TAG(TAG, "mhz19")
uint8_t mhz19_checksum(const uint8_t *command)
Definition mhz19.cpp:34
MHZ19DetectionRange
Definition mhz19.h:15
@ MHZ19_DETECTION_RANGE_0_10000PPM
Definition mhz19.h:19
@ MHZ19_DETECTION_RANGE_0_5000PPM
Definition mhz19.h:18
@ MHZ19_DETECTION_RANGE_0_2000PPM
Definition mhz19.h:17
constexpr uint8_t sum_bytes(const uint8_t *data, size_t len, uint8_t sum=0)
Add up the bytes of data with size len, truncated to 8 bits: the checksum many serial and IR protocol...
Definition helpers.h:894
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:25
static void uint32_t