ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
cm1106.cpp
Go to the documentation of this file.
1#include "cm1106.h"
2#include "esphome/core/log.h"
3
4#include <cinttypes>
5
6namespace esphome::cm1106 {
7
8ESPHOME_LOG_TAG(TAG, "cm1106");
9static constexpr uint8_t C_M1106_CMD_GET_CO2[4] = {0x11, 0x01, 0x01, 0xED};
10static constexpr uint8_t C_M1106_CMD_SET_CO2_CALIB[6] = {0x11, 0x03, 0x03, 0x00, 0x00, 0x00};
11static constexpr uint8_t C_M1106_CMD_SET_CO2_CALIB_RESPONSE[4] = {0x16, 0x01, 0x03, 0xE6};
12// The factory default ABC calibration cycle differs between CM1106 models, so
13// every ABC write applies the configured cycle and baseline explicitly.
14static constexpr uint8_t C_M1106_CMD_SET_ABC_STATUS[10] = {0x11, 0x07, 0x10, 0x64, 0x00, 0x0F, 0x01, 0x90, 0x64, 0x00};
15static constexpr uint8_t C_M1106_CMD_SET_ABC_STATUS_RESPONSE[4] = {0x16, 0x01, 0x10, 0xD9};
16
17static constexpr uint8_t CM1106_ABC_FLAG_ENABLE = 0x0;
18static constexpr uint8_t CM1106_ABC_FLAG_DISABLE = 0x2;
19uint8_t cm1106_checksum(const uint8_t *response, size_t len) {
20 uint8_t crc = 0;
21 for (size_t i = 0; i < len - 1; i++) {
22 crc -= response[i];
23 }
24 return crc;
25}
26
28 uint8_t response[8] = {0};
29 if (!this->cm1106_write_command_(C_M1106_CMD_GET_CO2, sizeof(C_M1106_CMD_GET_CO2), response, sizeof(response))) {
30 this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
31 return;
32 }
33
34 if (this->abc_boot_logic_ != CM1106_ABC_NONE) {
36 }
37}
38
40 uint8_t response[8] = {0};
41 if (!this->cm1106_write_command_(C_M1106_CMD_GET_CO2, sizeof(C_M1106_CMD_GET_CO2), response, sizeof(response))) {
42 ESP_LOGW(TAG, "Reading data failed!");
43 this->status_set_warning();
44 return;
45 }
46
47 if (response[0] != 0x16 || response[1] != 0x05 || response[2] != 0x01) {
48 ESP_LOGW(TAG, "Unexpected response: %02X %02X %02X %02X", response[0], response[1], response[2], response[3]);
49 this->status_set_warning();
50 return;
51 }
52
53 uint8_t checksum = cm1106_checksum(response, sizeof(response));
54 if (response[7] != checksum) {
55 ESP_LOGW(TAG, "Checksum doesn't match: 0x%02X!=0x%02X", response[7], checksum);
56 this->status_set_warning();
57 return;
58 }
59
60 const uint16_t ppm = response[3] << 8 | response[4];
61 const uint8_t status = response[5];
62 if (status & 0x1) {
63 ESP_LOGW(TAG, "Preheating; CO₂=%uppm", ppm);
64 this->status_set_warning();
65 return;
66 }
67 if (status) {
68 ESP_LOGW(TAG, "Status: %02X", status);
69 }
71
72 ESP_LOGD(TAG, "CO₂=%uppm DF4=%02X", ppm, response[6]);
73 if (this->co2_sensor_ != nullptr)
74 this->co2_sensor_->publish_state(ppm);
75}
76
78 uint8_t cmd[6];
79 memcpy(cmd, C_M1106_CMD_SET_CO2_CALIB, sizeof(cmd));
80 cmd[3] = ppm >> 8;
81 cmd[4] = ppm & 0xFF;
82 if (this->send_command_expect_ack_(cmd, sizeof(cmd), C_M1106_CMD_SET_CO2_CALIB_RESPONSE)) {
83 ESP_LOGD(TAG, "Successfully calibrated sensor to %uppm", ppm);
84 }
85}
86
87void CM1106Component::abc_set_(bool enabled) {
88 ESP_LOGD(TAG, "%sabling automatic baseline calibration", enabled ? LOG_STR_LITERAL("En") : LOG_STR_LITERAL("Dis"));
89 uint8_t cmd[10];
90 memcpy(cmd, C_M1106_CMD_SET_ABC_STATUS, sizeof(cmd));
91 cmd[4] = enabled ? CM1106_ABC_FLAG_ENABLE : CM1106_ABC_FLAG_DISABLE;
92 cmd[5] = this->abc_cycle_;
93 cmd[6] = this->abc_baseline_ >> 8;
94 cmd[7] = this->abc_baseline_ & 0xFF;
95 if (this->send_command_expect_ack_(cmd, sizeof(cmd), C_M1106_CMD_SET_ABC_STATUS_RESPONSE)) {
96 ESP_LOGD(TAG, "Successfully set ABC status");
97 }
98}
99
100bool CM1106Component::send_command_expect_ack_(const uint8_t *command, size_t command_len, const uint8_t *ack) {
101 uint8_t response[4] = {0};
102 if (!this->cm1106_write_command_(command, command_len, response, sizeof(response))) {
103 ESP_LOGW(TAG, "Command %02X failed", command[2]);
104 this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
105 return false;
106 }
107 if (!this->status_comm_result(memcmp(response, ack, sizeof(response)) == 0)) {
108 ESP_LOGW(TAG, "Unexpected response: %02X %02X %02X %02X", response[0], response[1], response[2], response[3]);
109 return false;
110 }
111 return true;
112}
113
114bool CM1106Component::cm1106_write_command_(const uint8_t *command, size_t command_len, uint8_t *response,
115 size_t response_len) {
116 this->discard_available();
117 this->write_array(command, command_len - 1);
118 this->write_byte(cm1106_checksum(command, command_len));
119 this->flush();
120
121 if (response == nullptr)
122 return true;
123
124 return this->read_array(response, response_len);
125}
126
128 ESP_LOGCONFIG(TAG, "CM1106:");
129 LOG_SENSOR(" ", "CO2", this->co2_sensor_);
130 if (this->abc_boot_logic_ != CM1106_ABC_NONE) {
131 ESP_LOGCONFIG(TAG,
132 " Automatic baseline calibration on boot: %s\n"
133 " ABC calibration cycle: %u days\n"
134 " ABC baseline: %uppm",
135 ONOFF(this->abc_boot_logic_ == CM1106_ABC_ENABLED), this->abc_cycle_, this->abc_baseline_);
136 }
137}
138
139} // namespace esphome::cm1106
uint8_t checksum
Definition bl0906.h:3
uint8_t status
Definition bl0942.h:8
void mark_failed()
Mark this component as failed.
bool status_comm_result(bool ok)
Clears the warning when ok, else sets it with message (default "Communication failed"); returns ok.
void status_clear_warning()
Definition component.h:296
bool send_command_expect_ack_(const uint8_t *command, size_t command_len, const uint8_t *ack)
Sends a command and checks the sensor's four byte acknowledgement, updating the warning status.
Definition cm1106.cpp:100
void calibrate_zero(uint16_t ppm)
Definition cm1106.cpp:77
void abc_set_(bool enabled)
Definition cm1106.cpp:87
CM1106ABCLogic abc_boot_logic_
Definition cm1106.h:41
bool cm1106_write_command_(const uint8_t *command, size_t command_len, uint8_t *response, size_t response_len)
Definition cm1106.cpp:114
sensor::Sensor * co2_sensor_
Definition cm1106.h:40
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
uint8_t cm1106_checksum(const uint8_t *response, size_t len)
Definition cm1106.cpp:19
@ CM1106_ABC_ENABLED
Definition cm1106.h:13
ESPHOME_LOG_TAG(TAG, "cm1106")
const void size_t len
Definition hal.h:68
uint8_t ack