ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
tmp102.cpp
Go to the documentation of this file.
1#include "tmp102.h"
2#include "esphome/core/log.h"
3#include "esphome/core/hal.h"
4#ifdef USE_TMP102_TEXT_SENSOR
6#endif
7
8#include <cmath>
9#ifdef USE_TMP102_TEXT_SENSOR
10#include <cstdio>
11#endif
12
13namespace esphome::tmp102 {
14
15ESPHOME_LOG_TAG(TAG, "tmp102");
16
17static constexpr uint32_t READ_TEMP_TIMEOUT_ID = 0;
18
19static constexpr uint8_t TMP102_REGISTER_TEMPERATURE = 0x00;
20static constexpr float TMP102_CONVERSION_FACTOR = 0.0625f;
21
36
37#ifdef USE_TMP102_TEXT_SENSOR
38PROGMEM_STRING_TABLE(ThresholdStatusStrings, "Not configured", "Setup failed", "OK", "Restored thresholds reset",
39 "Restore reset; save failed", "Preference save failed", "Configuration recovery failed",
40 "Configuration restored", "Write failed: high threshold", "Write failed: low threshold",
41 "Rejected: sensor setup failed");
42static_assert(ThresholdStatusStrings::COUNT == TMP102_THRESHOLD_STATUS_COUNT);
43#endif
44
45#if defined(USE_TMP102_CONFIGURE) || defined(USE_TMP102_BINARY_SENSOR)
46static constexpr uint8_t TMP102_REGISTER_CONFIGURATION = 0x01;
47static constexpr uint16_t TMP102_ALERT_BIT = 0x0020;
48static constexpr uint16_t TMP102_ALERT_POLARITY_BIT = 0x0400;
49#endif
50#ifdef USE_TMP102_CONFIGURE
51static constexpr uint8_t TMP102_REGISTER_LOW_LIMIT = 0x02;
52static constexpr uint8_t TMP102_REGISTER_HIGH_LIMIT = 0x03;
53#ifdef USE_TMP102_NUMBER
54static_assert(TMP102_REGISTER_LOW_LIMIT + TMP102_LIMIT_HIGH == TMP102_REGISTER_HIGH_LIMIT);
55#endif
56
57static constexpr uint16_t TMP102_CONFIGURATION_MASK = 0x1FD0;
58static constexpr uint16_t TMP102_RESET_CONFIGURATION = 0x60A0;
59static constexpr uint16_t TMP102_ONE_SHOT_BIT = 0x8000;
60static constexpr uint16_t TMP102_SHUTDOWN_BIT = 0x0100;
61static constexpr uint16_t TMP102_THERMOSTAT_MODE_BIT = 0x0200;
62static constexpr uint16_t TMP102_EXTENDED_MODE_BIT = 0x0010;
63static constexpr uint32_t TMP102_ONESHOT_DELAY_MS = 40;
64
65static float decode_temperature(uint16_t raw, bool extended) {
66 return (static_cast<int16_t>(raw) >> (extended ? 3 : 4)) * TMP102_CONVERSION_FACTOR;
67}
68
69#ifdef USE_TMP102_NUMBER
70static uint16_t encode_temperature(float temperature, bool extended) {
71 const int16_t steps = static_cast<int16_t>(std::lround(temperature / TMP102_CONVERSION_FACTOR));
72 return static_cast<uint16_t>(static_cast<uint32_t>(static_cast<uint16_t>(steps)) << (extended ? 3 : 4));
73}
74#endif
75#endif
76
77#if defined(USE_TMP102_CONFIGURE) || defined(USE_TMP102_TEXT_SENSOR)
79#ifdef USE_TMP102_CONFIGURE
80 if (this->configure_) {
81 this->setup_configured_();
82 return;
83 }
84#endif
86}
87#endif
88
89#ifdef USE_TMP102_CONFIGURE
91 bool corrected = false;
92#ifdef USE_TMP102_NUMBER
93 float restored[2]{};
94 bool restore_corrected[2]{};
95 corrected = this->prepare_number_limits_(restored, restore_corrected);
96#endif
97 if (!this->write_configuration_()) {
99 this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
100 return;
101 }
102#ifdef USE_TMP102_NUMBER
103 bool saved = true;
104 for (uint8_t limit = 0; limit < 2; ++limit) {
105 TMP102LimitNumber *control = this->limit_controls_[limit];
106 if (control == nullptr)
107 continue;
108 const float value = this->get_limit_temperature_(static_cast<TMP102LimitType>(limit));
109 if (restore_corrected[limit] || value != restored[limit])
110 saved &= control->save_value_(value);
111 control->publish_state(value);
112 }
113 if (!saved) {
114 ESP_LOGW(TAG, "Failed to save threshold preferences");
116 return;
117 }
118#endif
120}
121#endif
122
124 ESP_LOGCONFIG(TAG, "TMP102:");
125 LOG_I2C_DEVICE(this);
126 LOG_UPDATE_INTERVAL(this);
127 LOG_SENSOR(" ", "Temperature", this);
128#ifdef USE_TMP102_BINARY_SENSOR
129 LOG_BINARY_SENSOR(" ", "Alert", this->alert_binary_sensor_);
130#endif
131#ifdef USE_TMP102_TEXT_SENSOR
132 LOG_TEXT_SENSOR(" ", "Threshold Status", this->threshold_status_text_sensor_);
133#endif
134#ifdef USE_TMP102_CONFIGURE
135 if (!this->configure_)
136 return;
137 const uint8_t rate = (this->config_ >> 6) & 0x03;
138 const LogString *rate_str = rate == 0 ? LOG_STR("0.25Hz")
139 : rate == 1 ? LOG_STR("1Hz")
140 : rate == 2 ? LOG_STR("4Hz")
141 : LOG_STR("8Hz");
142 const uint8_t fault_bits = (this->config_ >> 11) & 0x03;
143 const bool extended = this->config_ & TMP102_EXTENDED_MODE_BIT;
144 ESP_LOGCONFIG(
145 TAG,
146 " Extended Mode: %s\n"
147 " One-Shot Mode: %s\n"
148 " Conversion Rate: %s\n"
149 " Thermostat Mode: %s\n"
150 " Alert Polarity: %s\n"
151 " Fault Queue: %u\n"
152 " Temperature High: %.4f°C\n"
153 " Temperature Low: %.4f°C",
154 YESNO(extended), YESNO(this->config_ & TMP102_SHUTDOWN_BIT), LOG_STR_ARG(rate_str),
155 this->config_ & TMP102_THERMOSTAT_MODE_BIT ? LOG_STR_LITERAL("Interrupt") : LOG_STR_LITERAL("Comparator"),
156 this->config_ & TMP102_ALERT_POLARITY_BIT ? LOG_STR_LITERAL("Active High") : LOG_STR_LITERAL("Active Low"),
157 fault_bits == 3 ? 6 : 1U << fault_bits, decode_temperature(this->high_limit_, extended),
158 decode_temperature(this->low_limit_, extended));
159#ifdef USE_TMP102_NUMBER
160 LOG_NUMBER(" ", "High Limit", this->limit_controls_[TMP102_LIMIT_HIGH]);
161 LOG_NUMBER(" ", "Low Limit", this->limit_controls_[TMP102_LIMIT_LOW]);
162#endif
163 if (this->config_ & TMP102_THERMOSTAT_MODE_BIT) {
164 ESP_LOGW(TAG, "Register reads clear the interrupt-mode ALERT pin");
165 }
166#endif
167}
168
170#ifdef USE_TMP102_CONFIGURE
171 if (this->configure_) {
172 this->update_configured_();
173 return;
174 }
175#endif
176 if (this->write(&TMP102_REGISTER_TEMPERATURE, 1) != i2c::ERROR_OK) {
178 return;
179 }
180 this->set_timeout(READ_TEMP_TIMEOUT_ID, 50, [this]() {
181 int16_t raw_temperature;
182 if (this->read(reinterpret_cast<uint8_t *>(&raw_temperature), 2) != i2c::ERROR_OK) {
184 return;
185 }
186 raw_temperature = i2c::i2ctohs(raw_temperature);
187 raw_temperature = raw_temperature >> 4;
188 float temperature = raw_temperature * TMP102_CONVERSION_FACTOR;
189 ESP_LOGD(TAG, "Got Temperature=%.1f°C", temperature);
190
191 this->publish_state(temperature);
192#ifdef USE_TMP102_BINARY_SENSOR
193 if (this->alert_binary_sensor_ != nullptr && !this->read_alert_state_())
194 return;
195#endif
196 this->status_clear_warning();
197 });
198}
199
200#ifdef USE_TMP102_CONFIGURE
202 if (!this->check_configuration_())
203 return;
204 if (this->config_ & TMP102_SHUTDOWN_BIT) {
205 if (!this->write_register_(TMP102_REGISTER_CONFIGURATION, this->config_ | TMP102_ONE_SHOT_BIT)) {
207 return;
208 }
209 this->set_timeout(READ_TEMP_TIMEOUT_ID, TMP102_ONESHOT_DELAY_MS, [this]() { this->read_temperature_(); });
210 } else {
211 this->read_temperature_();
212 }
213}
214
215bool TMP102Component::write_register_(uint8_t reg, uint16_t value) {
216 uint8_t frame[3] = {reg, static_cast<uint8_t>(value >> 8), static_cast<uint8_t>(value)};
217 if (this->write(frame, 3) == i2c::ERROR_OK)
218 return true;
219 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
220 return false;
221}
222
224 return this->write_register_(TMP102_REGISTER_CONFIGURATION, this->config_) &&
225 this->write_register_(TMP102_REGISTER_HIGH_LIMIT, this->high_limit_) &&
226 this->write_register_(TMP102_REGISTER_LOW_LIMIT, this->low_limit_);
227}
228
230 uint16_t config;
231 if (!this->read_byte_16(TMP102_REGISTER_CONFIGURATION, &config)) {
233 return false;
234 }
235 bool matches = (config & TMP102_CONFIGURATION_MASK) == (this->config_ & TMP102_CONFIGURATION_MASK);
236 // A chip reset is invisible in the configuration register when it matches the reset default; check the limits.
237 if (matches &&
238 (this->config_ & TMP102_CONFIGURATION_MASK) == (TMP102_RESET_CONFIGURATION & TMP102_CONFIGURATION_MASK)) {
239 uint16_t high, low;
240 if (!this->read_byte_16(TMP102_REGISTER_HIGH_LIMIT, &high) ||
241 !this->read_byte_16(TMP102_REGISTER_LOW_LIMIT, &low)) {
243 return false;
244 }
245 matches = high == this->high_limit_ && low == this->low_limit_;
246 }
247 if (matches)
248 return true;
249
250 ESP_LOGW(TAG, "Configuration changed; restoring configured settings and thresholds");
252 if (!this->write_configuration_()) {
253 ESP_LOGW(TAG, "Failed to restore configuration and thresholds");
255 } else {
257 }
258 return false;
259}
260
262 uint16_t raw;
263 if (!this->read_byte_16(TMP102_REGISTER_TEMPERATURE, &raw)) {
265 return;
266 }
267 const float temperature = decode_temperature(raw, raw & 0x0001);
268 ESP_LOGD(TAG, "Got Temperature=%.2f°C", temperature);
269 this->publish_state(temperature);
270#ifdef USE_TMP102_BINARY_SENSOR
271 if (this->alert_binary_sensor_ != nullptr && !this->read_alert_state_())
272 return;
273#endif
274 this->status_clear_warning();
275}
276
277#ifdef USE_TMP102_NUMBER
279 this->limit_controls_[number->limit_type_] = number;
280}
281
282bool TMP102Component::prepare_number_limits_(float *restored, bool *restore_corrected) {
283 const bool extended = this->config_ & TMP102_EXTENDED_MODE_BIT;
284 uint16_t raw[2] = {this->low_limit_, this->high_limit_};
285 bool corrected = false;
286 for (uint8_t limit = 0; limit < 2; ++limit) {
287 TMP102LimitNumber *control = this->limit_controls_[limit];
288 if (control == nullptr)
289 continue;
290 restored[limit] = control->load_value_(decode_temperature(raw[limit], extended), restore_corrected[limit]);
291 corrected |= restore_corrected[limit];
292 raw[limit] = encode_temperature(restored[limit], extended);
293 }
294 if (static_cast<int16_t>(raw[TMP102_LIMIT_LOW]) > static_cast<int16_t>(raw[TMP102_LIMIT_HIGH])) {
295 ESP_LOGW(TAG, "Restored thresholds are inverted; using configured values");
296 return true;
297 }
298 this->low_limit_ = raw[TMP102_LIMIT_LOW];
299 this->high_limit_ = raw[TMP102_LIMIT_HIGH];
300 return corrected;
301}
302
304 const uint16_t raw = limit == TMP102_LIMIT_HIGH ? this->high_limit_ : this->low_limit_;
305 return decode_temperature(raw, this->config_ & TMP102_EXTENDED_MODE_BIT);
306}
307
309 const bool extended = this->config_ & TMP102_EXTENDED_MODE_BIT;
310 const uint16_t raw = encode_temperature(temperature, extended);
311 const bool high = limit == TMP102_LIMIT_HIGH;
312 const uint16_t other = high ? this->low_limit_ : this->high_limit_;
313 if (high ? static_cast<int16_t>(raw) < static_cast<int16_t>(other)
314 : static_cast<int16_t>(raw) > static_cast<int16_t>(other)) {
315 ESP_LOGW(TAG, "Rejected %s threshold %.4f°C: crosses %s threshold %.4f°C",
316 high ? LOG_STR_LITERAL("high") : LOG_STR_LITERAL("low"), decode_temperature(raw, extended),
317 high ? LOG_STR_LITERAL("low") : LOG_STR_LITERAL("high"), decode_temperature(other, extended));
318 this->publish_threshold_rejection_(high, decode_temperature(raw, extended), decode_temperature(other, extended));
319 return false;
320 }
321 if (!this->write_register_(static_cast<uint8_t>(TMP102_REGISTER_LOW_LIMIT + limit), raw)) {
322 this->status_set_warning();
325 return false;
326 }
327 (high ? this->high_limit_ : this->low_limit_) = raw;
328 return true;
329}
330#endif
331#endif
332
333#ifdef USE_TMP102_TEXT_SENSOR
335 if (this->threshold_status_text_sensor_ == nullptr)
336 return;
337 char text[40];
338 ESPHOME_strncpy_P(
339 text,
340 reinterpret_cast<ESPHOME_PGM_P>(ThresholdStatusStrings::get_progmem_str(status, TMP102_THRESHOLD_STATUS_OK)),
341 sizeof(text));
342 text[sizeof(text) - 1] = '\0';
343 this->threshold_status_text_sensor_->publish_state(text);
344}
345
346void TMP102Component::publish_threshold_rejection_(bool high, float value, float other) {
347 if (this->threshold_status_text_sensor_ == nullptr)
348 return;
349 char text[96];
350 if (high) {
351 ESPHOME_snprintf_P(text, sizeof(text), ESPHOME_PSTR("Rejected: high %.4f°C crosses low %.4f°C"), value, other);
352 } else {
353 ESPHOME_snprintf_P(text, sizeof(text), ESPHOME_PSTR("Rejected: low %.4f°C crosses high %.4f°C"), value, other);
354 }
355 this->threshold_status_text_sensor_->publish_state(text);
356}
357#endif
358
359#ifdef USE_TMP102_BINARY_SENSOR
361 if (this->alert_binary_sensor_ != nullptr)
362 this->alert_binary_sensor_->invalidate_state();
363}
364
366 uint16_t config;
367 if (!this->read_byte_16(TMP102_REGISTER_CONFIGURATION, &config)) {
369 ESP_LOGW(TAG, "Failed to read alert state");
370 return false;
371 }
372 // AL equals POL while the comparator status is active.
373 const bool alert = bool(config & TMP102_ALERT_BIT) == bool(config & TMP102_ALERT_POLARITY_BIT);
374 this->alert_binary_sensor_->publish_state(alert);
375 return true;
376}
377#endif
378
379#ifdef USE_TMP102_NUMBER
380float TMP102LimitNumber::load_value_(float fallback, bool &corrected) {
381 float value = fallback;
382 if (this->restore_value_) {
383 this->pref_ = this->make_entity_preference<float>();
384 this->pref_.load(&value);
385 }
386 if (!std::isfinite(value) || value < this->traits.get_min_value() || value > this->traits.get_max_value()) {
387 ESP_LOGW(TAG, "Invalid restored threshold; using configured value");
388 value = fallback;
389 corrected = true;
390 }
391 return value;
392}
393
394bool TMP102LimitNumber::save_value_(float value) { return !this->restore_value_ || this->pref_.save(&value); }
395
396void TMP102LimitNumber::control(float value) {
397 // A chip that failed setup holds nothing the entity could show
398 if (this->parent_->is_failed()) {
399 ESP_LOGW(TAG, "Rejected threshold change: sensor setup failed");
400 this->parent_->publish_threshold_status_(TMP102_THRESHOLD_STATUS_SENSOR_SETUP_FAILED);
401 return;
402 }
403 const bool accepted = this->parent_->set_limit_temperature_(this->limit_type_, value);
404 value = this->parent_->get_limit_temperature_(this->limit_type_);
405 this->publish_state(value);
406 if (accepted && !this->save_value_(value)) {
407 ESP_LOGW(TAG, "Failed to save threshold preference");
408 this->parent_->publish_threshold_status_(TMP102_THRESHOLD_STATUS_PREFERENCE_SAVE_FAILED);
409 } else if (accepted) {
410 this->parent_->publish_threshold_status_(TMP102_THRESHOLD_STATUS_OK);
411 }
412}
413#endif
414
415} // namespace esphome::tmp102
uint8_t raw[35]
Definition bl0939.h:0
uint8_t status
Definition bl0942.h:8
void mark_failed()
Mark this component as failed.
virtual void setup()
Where the component's initialization should happen.
Definition component.cpp:84
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 status_clear_warning()
Definition component.h:296
ESPPreferenceObject make_entity_preference(uint32_t version=0)
Create a preference object for storing this entity's state/settings.
ErrorCode write(const uint8_t *data, size_t len) const
writes an array of bytes to a device using an I2CBus
Definition i2c.h:195
ErrorCode read(uint8_t *data, size_t len) const
reads an array of bytes from the device using an I2CBus
Definition i2c.h:175
bool read_byte_16(uint8_t a_register, uint16_t *data)
Definition i2c.h:281
I2CRegister reg(uint8_t a_register)
calls the I2CRegister constructor
Definition i2c.h:164
void publish_state(float state)
Definition number.cpp:20
NumberTraits traits
Definition number.h:41
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:65
void publish_threshold_status_(uint8_t status)
Definition tmp102.cpp:334
bool write_register_(uint8_t reg, uint16_t value)
Definition tmp102.cpp:215
void publish_threshold_rejection_(bool high, float value, float other)
Definition tmp102.cpp:346
TMP102LimitNumber * limit_controls_[2]
Definition tmp102.h:94
float get_limit_temperature_(TMP102LimitType limit) const
Definition tmp102.cpp:303
bool set_limit_temperature_(TMP102LimitType limit, float temperature)
Definition tmp102.cpp:308
void set_limit_control(TMP102LimitNumber *number)
Definition tmp102.cpp:278
bool prepare_number_limits_(float *restored, bool *restore_corrected)
Definition tmp102.cpp:282
float load_value_(float fallback, bool &corrected)
Definition tmp102.cpp:380
ESPPreferenceObject pref_
Definition tmp102.h:118
void control(float value) override
Definition tmp102.cpp:396
uint16_t i2ctohs(uint16_t i2cshort)
Definition i2c.h:139
@ ERROR_OK
No error found during execution of method.
Definition i2c_bus.h:15
@ TMP102_THRESHOLD_STATUS_WRITE_LOW_FAILED
Definition tmp102.cpp:32
@ TMP102_THRESHOLD_STATUS_RECOVERED
Definition tmp102.cpp:30
@ TMP102_THRESHOLD_STATUS_PREFERENCE_SAVE_FAILED
Definition tmp102.cpp:28
@ TMP102_THRESHOLD_STATUS_NOT_CONFIGURED
Definition tmp102.cpp:23
@ TMP102_THRESHOLD_STATUS_COUNT
Definition tmp102.cpp:34
@ TMP102_THRESHOLD_STATUS_RESTORE_RESET_SAVE_FAILED
Definition tmp102.cpp:27
@ TMP102_THRESHOLD_STATUS_OK
Definition tmp102.cpp:25
@ TMP102_THRESHOLD_STATUS_SENSOR_SETUP_FAILED
Definition tmp102.cpp:33
@ TMP102_THRESHOLD_STATUS_WRITE_HIGH_FAILED
Definition tmp102.cpp:31
@ TMP102_THRESHOLD_STATUS_RECOVERY_FAILED
Definition tmp102.cpp:29
@ TMP102_THRESHOLD_STATUS_RESTORE_RESET
Definition tmp102.cpp:26
@ TMP102_THRESHOLD_STATUS_SETUP_FAILED
Definition tmp102.cpp:24
@ TMP102_LIMIT_HIGH
Definition tmp102.h:32
ESPHOME_LOG_TAG(TAG, "tmp102")
PROGMEM_STRING_TABLE(ThresholdStatusStrings, "Not configured", "Setup failed", "OK", "Restored thresholds reset", "Restore reset; save failed", "Preference save failed", "Configuration recovery failed", "Configuration restored", "Write failed: high threshold", "Write failed: low threshold", "Rejected: sensor setup failed")
static void uint32_t
uint16_t temperature
Definition sun_gtil2.cpp:12