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