ESPHome 2026.5.0b1
Loading...
Searching...
No Matches
ens210.cpp
Go to the documentation of this file.
1// ENS210 relative humidity and temperature sensor with I2C interface from ScioSense
2//
3// Datasheet: https://www.sciosense.com/wp-content/uploads/2021/01/ENS210.pdf
4//
5// Implementation based on:
6// https://github.com/maarten-pennings/ENS210
7// https://github.com/sciosense/ENS210_driver
8
9#include "ens210.h"
10#include "esphome/core/log.h"
11#include "esphome/core/hal.h"
12
13namespace esphome::ens210 {
14
15static const char *const TAG = "ens210";
16
17// ENS210 chip constants
18static const uint8_t ENS210_BOOTING_MS = 2; // Booting time in ms (also after reset, or going to high power)
19static const uint8_t ENS210_SINGLE_MEASURMENT_CONVERSION_TIME_MS =
20 130; // Conversion time in ms for single shot T/H measurement
21static const uint16_t ENS210_PART_ID = 0x0210; // The expected part id of the ENS210
22
23// Addresses of the ENS210 registers
24static const uint8_t ENS210_REGISTER_PART_ID = 0x00;
25static const uint8_t ENS210_REGISTER_UID = 0x04;
26static const uint8_t ENS210_REGISTER_SYS_CTRL = 0x10;
27static const uint8_t ENS210_REGISTER_SYS_STAT = 0x11;
28static const uint8_t ENS210_REGISTER_SENS_RUN = 0x21;
29static const uint8_t ENS210_REGISTER_SENS_START = 0x22;
30static const uint8_t ENS210_REGISTER_SENS_STOP = 0x23;
31static const uint8_t ENS210_REGISTER_SENS_STAT = 0x24;
32static const uint8_t ENS210_REGISTER_T_VAL = 0x30;
33static const uint8_t ENS210_REGISTER_H_VAL = 0x33;
34
35// CRC-7 constants
36static const uint8_t CRC7_WIDTH = 7; // A 7 bits CRC has polynomial of 7th order, which has 8 terms
37static const uint8_t CRC7_POLY = 0x89; // The 8 coefficients of the polynomial
38static const uint8_t CRC7_IVEC = 0x7F; // Initial vector has all 7 bits high
39
40// Payload data constants
41static const uint8_t DATA7_WIDTH = 17;
42static const uint32_t DATA7_MASK = ((1UL << DATA7_WIDTH) - 1); // 0b 0 1111 1111 1111 1111
43static const uint32_t DATA7_MSB = (1UL << (DATA7_WIDTH - 1)); // 0b 1 0000 0000 0000 0000
44
45// Converts a status to a human readable string
46static const LogString *ens210_status_to_human(int status) {
47 switch (status) {
49 return LOG_STR("I2C error - communication with ENS210 failed!");
51 return LOG_STR("CRC error");
53 return LOG_STR("Invalid data");
55 return LOG_STR("Status OK");
57 return LOG_STR("ENS210 has wrong chip ID! Is it a ENS210?");
58 default:
59 return LOG_STR("Unknown");
60 }
61}
62
63// Compute the CRC-7 of 'value' (should only have 17 bits)
64// https://en.wikipedia.org/wiki/Cyclic_redundancy_check#Computation
65static uint32_t crc7(uint32_t value) {
66 // Setup polynomial
67 uint32_t polynomial = CRC7_POLY;
68 // Align polynomial with data
69 polynomial = polynomial << (DATA7_WIDTH - CRC7_WIDTH - 1);
70 // Loop variable (indicates which bit to test, start with highest)
71 uint32_t bit = DATA7_MSB;
72 // Make room for CRC value
73 value = value << CRC7_WIDTH;
74 bit = bit << CRC7_WIDTH;
75 polynomial = polynomial << CRC7_WIDTH;
76 // Insert initial vector
77 value |= CRC7_IVEC;
78 // Apply division until all bits done
79 while (bit & (DATA7_MASK << CRC7_WIDTH)) {
80 if (bit & value)
81 value ^= polynomial;
82 bit >>= 1;
83 polynomial >>= 1;
84 }
85 return value;
86}
87
89 uint8_t data[2];
90 uint16_t part_id = 0;
91 // Reset
92 if (!this->write_byte(ENS210_REGISTER_SYS_CTRL, 0x80)) {
93 this->write_byte(ENS210_REGISTER_SYS_CTRL, 0x80);
94 this->error_code_ = ENS210_STATUS_I2C_ERROR;
95 this->mark_failed();
96 return;
97 }
98 // Wait to boot after reset
99 delay(ENS210_BOOTING_MS);
100 // Must disable low power to read PART_ID
101 if (!set_low_power_(false)) {
102 // Try to go back to default mode (low power enabled)
103 set_low_power_(true);
104 this->error_code_ = ENS210_STATUS_I2C_ERROR;
105 this->mark_failed();
106 return;
107 }
108 // Read the PART_ID
109 if (!this->read_bytes(ENS210_REGISTER_PART_ID, data, 2)) {
110 // Try to go back to default mode (low power enabled)
111 set_low_power_(true);
112 this->error_code_ = ENS210_STATUS_I2C_ERROR;
113 this->mark_failed();
114 return;
115 }
116 // Pack bytes into partid
117 part_id = data[1] * 256U + data[0] * 1U;
118 // Check expected part id of the ENS210
119 if (part_id != ENS210_PART_ID) {
120 this->error_code_ = ENS210_WRONG_CHIP_ID;
121 this->mark_failed();
122 }
123 // Set default power mode (low power enabled)
124 set_low_power_(true);
125}
126
128 ESP_LOGCONFIG(TAG, "ENS210:");
129 LOG_I2C_DEVICE(this);
130 if (this->is_failed()) {
131 ESP_LOGE(TAG, "%s", LOG_STR_ARG(ens210_status_to_human(this->error_code_)));
132 }
133 LOG_UPDATE_INTERVAL(this);
134 LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
135 LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
136}
137
139 // Execute a single measurement
140 if (!this->write_byte(ENS210_REGISTER_SENS_RUN, 0x00)) {
141 ESP_LOGE(TAG, "Starting single measurement failed!");
142 this->status_set_warning();
143 return;
144 }
145 // Trigger measurement
146 if (!this->write_byte(ENS210_REGISTER_SENS_START, 0x03)) {
147 ESP_LOGE(TAG, "Trigger of measurement failed!");
148 this->status_set_warning();
149 return;
150 }
151 // Wait for measurement to complete
152 this->set_timeout("data", uint32_t(ENS210_SINGLE_MEASURMENT_CONVERSION_TIME_MS), [this]() {
153 int temperature_data, temperature_status, humidity_data, humidity_status;
154 uint8_t data[6];
155 uint32_t h_val_data, t_val_data;
156 // Set default status for early bail out
157 temperature_status = ENS210_STATUS_I2C_ERROR;
158 humidity_status = ENS210_STATUS_I2C_ERROR;
159
160 // Read T_VAL and H_VAL
161 if (!this->read_bytes(ENS210_REGISTER_T_VAL, data, 6)) {
162 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
163 this->status_set_warning();
164 return;
165 }
166 // Pack bytes for humidity
167 h_val_data = (uint32_t) ((uint32_t) data[5] << 16 | (uint32_t) data[4] << 8 | (uint32_t) data[3]);
168 // Extract humidity data and update the status
169 extract_measurement_(h_val_data, &humidity_data, &humidity_status);
170
171 if (humidity_status == ENS210_STATUS_OK) {
172 if (this->humidity_sensor_ != nullptr) {
173 float humidity = (humidity_data & 0xFFFF) / 512.0;
174 this->humidity_sensor_->publish_state(humidity);
175 }
176 } else {
177 ESP_LOGW(TAG, "Humidity status failure: %s", LOG_STR_ARG(ens210_status_to_human(humidity_status)));
178 this->status_set_warning();
179 return;
180 }
181 // Pack bytes for temperature
182 t_val_data = (uint32_t) ((uint32_t) data[2] << 16 | (uint32_t) data[1] << 8 | (uint32_t) data[0]);
183 // Extract temperature data and update the status
184 extract_measurement_(t_val_data, &temperature_data, &temperature_status);
185
186 if (temperature_status == ENS210_STATUS_OK) {
187 if (this->temperature_sensor_ != nullptr) {
188 // Temperature in Celsius
189 float temperature = (temperature_data & 0xFFFF) / 64.0 - 27315L / 100.0;
190 this->temperature_sensor_->publish_state(temperature);
191 }
192 } else {
193 ESP_LOGW(TAG, "Temperature status failure: %s", LOG_STR_ARG(ens210_status_to_human(temperature_status)));
194 }
195 });
196}
197
198// Extracts measurement 'data' and 'status' from a 'val' obtained from measurement.
200 *data = (val >> 0) & 0xffff;
201 int valid = (val >> 16) & 0x1;
202 uint32_t crc = (val >> 17) & 0x7f;
203 uint32_t payload = (val >> 0) & 0x1ffff;
204 // Check CRC
205 uint8_t crc_ok = crc7(payload) == crc;
206
207 if (!crc_ok) {
209 } else if (!valid) {
211 } else {
213 }
214}
215
216// Sets ENS210 to low (true) or high (false) power. Returns false on I2C problems.
218 uint8_t low_power_cmd = enable ? 0x01 : 0x00;
219 ESP_LOGD(TAG, "Enable low power: %s", enable ? "true" : "false");
220 bool result = this->write_byte(ENS210_REGISTER_SYS_CTRL, low_power_cmd);
221 delay(ENS210_BOOTING_MS);
222 return result;
223}
224
225} // namespace esphome::ens210
uint8_t status
Definition bl0942.h:8
void mark_failed()
Mark this component as failed.
bool is_failed() const
Definition component.h:284
ESPDEPRECATED("Use const char* or uint32_t overload instead. Removed in 2026.7.0", "2026.1.0") void set_timeout(const std voi set_timeout)(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
Definition component.h:510
void extract_measurement_(uint32_t val, int *data, int *status)
Definition ens210.cpp:199
sensor::Sensor * humidity_sensor_
Definition ens210.h:33
bool set_low_power_(bool enable)
Definition ens210.cpp:217
enum esphome::ens210::ENS210Component::ErrorCode ENS210_STATUS_OK
sensor::Sensor * temperature_sensor_
Definition ens210.h:32
bool write_byte(uint8_t a_register, uint8_t data) const
Definition i2c.h:265
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:217
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:68
mopeka_std_values val[3]
void HOT delay(uint32_t ms)
Definition hal.cpp:82
bool valid
static void uint32_t
uint16_t temperature
Definition sun_gtil2.cpp:12