ESPHome 2026.10.2
Loading...
Searching...
No Matches
tfluna.cpp
Go to the documentation of this file.
1#include "tfluna.h"
2#include <cstddef>
3#include "esphome/core/log.h"
5
6namespace esphome::tfluna {
7
8// see https://files.waveshare.com/upload/a/ac/SJ-PM-TF-Luna_A05_Product_Manual.pdf
9static constexpr uint8_t SAVE_REGISTER = 0x20;
10static constexpr uint8_t VERSION_REVISION_REGISTER = 0x0A;
11static constexpr uint8_t DISTANCE_LOW_REGISTER = 0x00;
12static constexpr uint8_t RESTORE_FACTORY_DEFAULTS_REGISTER = 0x29;
13static constexpr uint8_t SHUTDOWN_REBOOT_REGISTER = 0x21;
14static constexpr uint8_t MODE_REGISTER = 0x23;
15static constexpr uint8_t MODE_TRIGGER = 0x01;
16static constexpr uint8_t TRIGGER_ONESHOT_REGISTER = 0x24;
17static constexpr uint8_t MAX_READ_ATTEMPTS = 5;
18static constexpr uint8_t READ_RETRY_MS = 5;
19static const char *const TAG = "tfluna";
20static const char *const READ_RETRY_TIMEOUT = "read_retry";
21
22void TFLuna::dump_config() {
23 ESP_LOGCONFIG(TAG, "TF-Luna (i2c):");
24 LOG_I2C_DEVICE(this);
25 LOG_UPDATE_INTERVAL(this);
26 ESP_LOGCONFIG(TAG, " Firmware: %s", this->version_);
27
28#ifdef USE_SENSOR
29 LOG_SENSOR(" ", "Distance:", this->distance_sensor_);
30 LOG_SENSOR(" ", "Temperature:", this->temperature_sensor_);
31 LOG_SENSOR(" ", "Signal Strength:", this->signal_strength_sensor_);
32 LOG_SENSOR(" ", "Timestamp:", this->timestamp_sensor_);
33#endif
34#ifdef USE_TEXT_SENSOR
35 LOG_TEXT_SENSOR(" ", "Version:", this->version_text_sensor_);
36#endif
37#ifdef USE_BUTTON
38 LOG_BUTTON(" ", "Factory Reset:", this->reset_button_);
39 LOG_BUTTON(" ", "Restart:", this->restart_button_);
40#endif
41}
42
43void TFLuna::setup() {
44 uint8_t mode;
45 if (!this->read_byte(MODE_REGISTER, &mode)) {
46 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
47 this->mark_failed();
48 return;
49 }
50 if (mode != MODE_TRIGGER) {
51 if (!this->write_byte(MODE_REGISTER, MODE_TRIGGER)) {
52 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
53 this->mark_failed();
54 return;
55 }
56
57 if (!this->write_byte(SAVE_REGISTER, 1)) {
58 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
59 this->mark_failed();
60 return;
61 }
62 }
63
64 uint8_t raw_version[3];
65 if (!this->read_bytes(VERSION_REVISION_REGISTER, raw_version, sizeof(raw_version))) {
66 this->status_set_warning(ESP_LOG_MSG_COMM_FAIL);
67 return;
68 } else {
69 snprintf(this->version_, sizeof(this->version_), "%d.%d.%d", raw_version[2], raw_version[1], raw_version[0]);
70#ifdef USE_TEXT_SENSOR
71 if (this->version_text_sensor_ != nullptr) {
72 this->version_text_sensor_->publish_state(version_);
73 }
74#endif
75 }
76}
77
79 uint8_t buf[8];
80 if (!this->read_bytes(DISTANCE_LOW_REGISTER, buf, sizeof(buf))) {
81 this->status_set_warning(ESP_LOG_MSG_COMM_FAIL);
82 return false;
83 }
84 // Layout:
85 // buf[0..1] = distance (LE), buf[2..3] = signal (LE),
86 // buf[4..5] = temperature (LE), buf[6..7] = timestamp (LE)
87 uint16_t distance = encode_uint16(buf[1], buf[0]);
88 uint16_t signal_strength = encode_uint16(buf[3], buf[2]);
89 int16_t temperature_raw = encode_uint16(buf[5], buf[4]);
90 uint16_t timestamp = encode_uint16(buf[7], buf[6]);
91
92 if (timestamp == this->previous_timestamp_) {
93 return false;
94 }
95
96#ifdef USE_SENSOR
97 if (this->timestamp_sensor_ != nullptr) {
98 this->timestamp_sensor_->publish_state(timestamp);
99 }
100 if (this->distance_sensor_ != nullptr) {
101 // distance is unreliable when strength is below 100 or saturated at 0xFFFF
102 this->distance_sensor_->publish_state(signal_strength < 100 || signal_strength == 0xFFFF ? NAN : distance);
103 }
104
105 if (this->temperature_sensor_ != nullptr) {
106 this->temperature_sensor_->publish_state(temperature_raw / (float) 100);
107 }
108
109 if (this->signal_strength_sensor_ != nullptr) {
110 this->signal_strength_sensor_->publish_state(signal_strength);
111 }
112#endif
113 this->status_clear_warning();
115 return true;
116}
117
119 if (this->read_data_()) {
120 this->attempt_ = 0;
121 } else {
122 if (this->attempt_ < MAX_READ_ATTEMPTS) {
123 this->attempt_++;
124 this->set_timeout(READ_RETRY_TIMEOUT, READ_RETRY_MS, [this]() { this->read_data_timeout_(); });
125 } else {
126 this->status_set_warning("Hung device, restarting...");
127 this->restart();
128 }
129 }
130}
131
132void TFLuna::update() {
133 this->cancel_timeout(READ_RETRY_TIMEOUT);
134 this->attempt_ = 0;
135 if (!this->write_byte(TRIGGER_ONESHOT_REGISTER, 0x01)) {
136 this->status_set_warning(ESP_LOG_MSG_COMM_FAIL);
137 return;
138 }
139 this->read_data_timeout_();
140}
141
142void TFLuna::factory_reset() {
143 if (!this->write_byte(RESTORE_FACTORY_DEFAULTS_REGISTER, 1)) {
144 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
145 return;
146 }
147 this->status_set_warning("Factory reset issued; waiting for device to become ready");
148 this->set_timeout("_setup", 100, [this]() { this->setup(); });
149}
150
151void TFLuna::restart() {
152 if (!this->write_byte(SHUTDOWN_REBOOT_REGISTER, 0x02)) {
153 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
154 return;
155 }
156}
157
158} // namespace esphome::tfluna
BedjetMode mode
BedJet operating mode.
void mark_failed()
Mark this component as failed.
virtual void setup()
Where the component's initialization should happen.
Definition component.cpp:84
bool cancel_timeout(const char *name)
Cancel a timeout function.
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:289
bool write_byte(uint8_t a_register, uint8_t data) const
Definition i2c.h:265
bool read_byte(uint8_t a_register, uint8_t *data)
Definition i2c.h:240
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
uint16_t previous_timestamp_
Definition tfluna.h:48
const char *const TAG
Definition spi.cpp:7
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:978
uint16_t timestamp
Definition tt21100.cpp:2