ESPHome 2025.6.3
Loading...
Searching...
No Matches
ccs811.cpp
Go to the documentation of this file.
1#include "ccs811.h"
2#include "esphome/core/hal.h"
4#include "esphome/core/log.h"
5
6namespace esphome {
7namespace ccs811 {
8
9static const char *const TAG = "ccs811";
10
11// based on
12// - https://cdn.sparkfun.com/datasheets/BreakoutBoards/CCS811_Programming_Guide.pdf
13
14#define CHECK_TRUE(f, error_code) \
15 if (!(f)) { \
16 this->mark_failed(); \
17 this->error_code_ = (error_code); \
18 return; \
19 }
20
21#define CHECKED_IO(f) CHECK_TRUE(f, COMMUNICATION_FAILED)
22
24 // page 9 programming guide - hwid is always 0x81
25 uint8_t hw_id;
26 CHECKED_IO(this->read_byte(0x20, &hw_id))
27 CHECK_TRUE(hw_id == 0x81, INVALID_ID)
28
29 // software reset, page 3 - allowed to fail
30 this->write_bytes(0xFF, {0x11, 0xE5, 0x72, 0x8A});
31 delay(5);
32
33 // page 10, APP_START
34 CHECK_TRUE(!this->status_has_error_(), SENSOR_REPORTED_ERROR)
35 CHECK_TRUE(this->status_app_is_valid_(), APP_INVALID)
36 CHECK_TRUE(this->write_bytes(0xF4, {}), APP_START_FAILED)
37 // App setup, wait for it to load
38 delay(1);
39
40 // set MEAS_MODE (page 5)
41 uint8_t meas_mode = 0;
42 uint32_t interval = this->get_update_interval();
43 if (interval >= 60 * 1000) {
44 meas_mode = 3 << 4; // sensor takes a reading every 60 seconds
45 } else if (interval >= 10 * 1000) {
46 meas_mode = 2 << 4; // sensor takes a reading every 10 seconds
47 } else if (interval >= 1 * 1000) {
48 meas_mode = 1 << 4; // sensor takes a reading every second
49 } else {
50 meas_mode = 4 << 4; // sensor takes a reading every 250ms
51 }
52
53 CHECKED_IO(this->write_byte(0x01, meas_mode))
54
55 if (this->baseline_.has_value()) {
56 // baseline available, write to sensor
57 this->write_bytes(0x11, decode_value(*this->baseline_));
58 }
59
60 auto hardware_version_data = this->read_bytes<1>(0x21);
61 auto bootloader_version_data = this->read_bytes<2>(0x23);
62 auto application_version_data = this->read_bytes<2>(0x24);
63
64 uint8_t hardware_version = 0;
65 uint16_t bootloader_version = 0;
66 uint16_t application_version = 0;
67
68 if (hardware_version_data.has_value()) {
69 hardware_version = (*hardware_version_data)[0];
70 }
71
72 if (bootloader_version_data.has_value()) {
73 bootloader_version = encode_uint16((*bootloader_version_data)[0], (*bootloader_version_data)[1]);
74 }
75
76 if (application_version_data.has_value()) {
77 application_version = encode_uint16((*application_version_data)[0], (*application_version_data)[1]);
78 }
79
80 ESP_LOGD(TAG, "hardware_version=0x%x bootloader_version=0x%x application_version=0x%x\n", hardware_version,
81 bootloader_version, application_version);
82 if (this->version_ != nullptr) {
83 char version[20]; // "15.15.15 (0xffff)" is 17 chars, plus NUL, plus wiggle room
84 sprintf(version, "%d.%d.%d (0x%02x)", (application_version >> 12 & 15), (application_version >> 8 & 15),
85 (application_version >> 4 & 15), application_version);
86 ESP_LOGD(TAG, "publishing version state: %s", version);
87 this->version_->publish_state(version);
88 }
89}
91 if (!this->status_has_data_()) {
92 ESP_LOGD(TAG, "Status indicates no data ready!");
93 this->status_set_warning();
94 return;
95 }
96
97 // page 12 - alg result data
98 auto alg_data = this->read_bytes<4>(0x02);
99 if (!alg_data.has_value()) {
100 ESP_LOGW(TAG, "Reading CCS811 data failed!");
101 this->status_set_warning();
102 return;
103 }
104 auto res = *alg_data;
105 uint16_t co2 = encode_uint16(res[0], res[1]);
106 uint16_t tvoc = encode_uint16(res[2], res[3]);
107
108 // also print baseline
109 auto baseline_data = this->read_bytes<2>(0x11);
110 uint16_t baseline = 0;
111 if (baseline_data.has_value()) {
112 baseline = encode_uint16((*baseline_data)[0], (*baseline_data)[1]);
113 }
114
115 ESP_LOGD(TAG, "Got co2=%u ppm, tvoc=%u ppb, baseline=0x%04X", co2, tvoc, baseline);
116
117 if (this->co2_ != nullptr)
118 this->co2_->publish_state(co2);
119 if (this->tvoc_ != nullptr)
120 this->tvoc_->publish_state(tvoc);
121
122 this->status_clear_warning();
123
124 this->send_env_data_();
125}
127 if (this->humidity_ == nullptr && this->temperature_ == nullptr)
128 return;
129
130 float humidity = NAN;
131 if (this->humidity_ != nullptr)
132 humidity = this->humidity_->state;
133 if (std::isnan(humidity) || humidity < 0 || humidity > 100)
134 humidity = 50;
135 float temperature = NAN;
136 if (this->temperature_ != nullptr)
137 temperature = this->temperature_->state;
139 temperature = 25;
140 // temperature has a 25° offset to allow negative temperatures
141 temperature += 25;
142
143 // At page 18 of:
144 // https://cdn.sparkfun.com/datasheets/BreakoutBoards/CCS811_Programming_Guide.pdf
145 // Reference code:
146 // https://github.com/adafruit/Adafruit_CCS811/blob/0990f5c620354d8bc087c4706bec091d8e6e5dfd/Adafruit_CCS811.cpp#L135-L142
147 uint16_t hum_conv = static_cast<uint16_t>(lroundf(humidity * 512.0f + 0.5f));
148 uint16_t temp_conv = static_cast<uint16_t>(lroundf(temperature * 512.0f + 0.5f));
149 this->write_bytes(0x05, {(uint8_t) ((hum_conv >> 8) & 0xff), (uint8_t) ((hum_conv & 0xff)),
150 (uint8_t) ((temp_conv >> 8) & 0xff), (uint8_t) ((temp_conv & 0xff))});
151}
153 ESP_LOGCONFIG(TAG, "CCS811");
154 LOG_I2C_DEVICE(this)
155 LOG_UPDATE_INTERVAL(this)
156 LOG_SENSOR(" ", "CO2 Sensor", this->co2_)
157 LOG_SENSOR(" ", "TVOC Sensor", this->tvoc_)
158 LOG_TEXT_SENSOR(" ", "Firmware Version Sensor", this->version_)
159 if (this->baseline_) {
160 ESP_LOGCONFIG(TAG, " Baseline: %04X", *this->baseline_);
161 } else {
162 ESP_LOGCONFIG(TAG, " Baseline: NOT SET");
163 }
164 if (this->is_failed()) {
165 switch (this->error_code_) {
167 ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL);
168 break;
169 case INVALID_ID:
170 ESP_LOGW(TAG, "Sensor reported an invalid ID. Is this a CCS811?");
171 break;
173 ESP_LOGW(TAG, "Sensor reported internal error");
174 break;
175 case APP_INVALID:
176 ESP_LOGW(TAG, "Sensor reported invalid APP installed.");
177 break;
178 case APP_START_FAILED:
179 ESP_LOGW(TAG, "Sensor reported APP start failed.");
180 break;
181 case UNKNOWN:
182 default:
183 ESP_LOGW(TAG, "Unknown setup error!");
184 break;
185 }
186 }
187}
188
189} // namespace ccs811
190} // namespace esphome
bool is_failed() const
void status_set_warning(const char *message="unspecified")
void status_clear_warning()
virtual uint32_t get_update_interval() const
Get the update interval in ms of this sensor.
enum esphome::ccs811::CCS811Component::ErrorCode UNKNOWN
optional< uint16_t > baseline_
Definition ccs811.h:49
text_sensor::TextSensor * version_
Definition ccs811.h:48
sensor::Sensor * temperature_
Input sensor for temperature reading.
Definition ccs811.h:53
sensor::Sensor * humidity_
Input sensor for humidity reading.
Definition ccs811.h:51
void setup() override
Setup the sensor and test for a connection.
Definition ccs811.cpp:23
void update() override
Schedule temperature+pressure readings.
Definition ccs811.cpp:90
bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len, bool stop=true)
Definition i2c.h:252
bool write_byte(uint8_t a_register, uint8_t data, bool stop=true)
Definition i2c.h:266
bool read_byte(uint8_t a_register, uint8_t *data, bool stop=true)
Definition i2c.h:239
bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len)
Compat APIs All methods below have been added for compatibility reasons.
Definition i2c.h:216
bool has_value() const
Definition optional.h:87
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:39
float state
This member variable stores the last state that has passed through all filters.
Definition sensor.h:136
void publish_state(const std::string &state)
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
constexpr14 std::array< uint8_t, sizeof(T)> decode_value(T val)
Decode a value into its constituent bytes (from most to least significant).
Definition helpers.h:222
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
Definition helpers.h:192
void IRAM_ATTR HOT delay(uint32_t ms)
Definition core.cpp:29
uint16_t temperature
Definition sun_gtil2.cpp:12