ESPHome  2024.4.0
dallas_component.cpp
Go to the documentation of this file.
1 #include "dallas_component.h"
2 #include "esphome/core/log.h"
3 
4 namespace esphome {
5 namespace dallas {
6 
7 static const char *const TAG = "dallas.sensor";
8 
9 static const uint8_t DALLAS_MODEL_DS18S20 = 0x10;
10 static const uint8_t DALLAS_MODEL_DS1822 = 0x22;
11 static const uint8_t DALLAS_MODEL_DS18B20 = 0x28;
12 static const uint8_t DALLAS_MODEL_DS1825 = 0x3B;
13 static const uint8_t DALLAS_MODEL_DS28EA00 = 0x42;
14 static const uint8_t DALLAS_COMMAND_START_CONVERSION = 0x44;
15 static const uint8_t DALLAS_COMMAND_READ_SCRATCH_PAD = 0xBE;
16 static const uint8_t DALLAS_COMMAND_WRITE_SCRATCH_PAD = 0x4E;
17 
19  switch (this->resolution_) {
20  case 9:
21  return 94;
22  case 10:
23  return 188;
24  case 11:
25  return 375;
26  default:
27  return 750;
28  }
29 }
30 
32  ESP_LOGCONFIG(TAG, "Setting up DallasComponent...");
33 
34  pin_->setup();
35 
36  // clear bus with 480µs high, otherwise initial reset in search_vec() fails
37  pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
38  delayMicroseconds(480);
39 
40  one_wire_ = new ESPOneWire(pin_); // NOLINT(cppcoreguidelines-owning-memory)
41 
42  std::vector<uint64_t> raw_sensors;
43  raw_sensors = this->one_wire_->search_vec();
44 
45  for (auto &address : raw_sensors) {
46  auto *address8 = reinterpret_cast<uint8_t *>(&address);
47  if (crc8(address8, 7) != address8[7]) {
48  ESP_LOGW(TAG, "Dallas device 0x%s has invalid CRC.", format_hex(address).c_str());
49  continue;
50  }
51  if (address8[0] != DALLAS_MODEL_DS18S20 && address8[0] != DALLAS_MODEL_DS1822 &&
52  address8[0] != DALLAS_MODEL_DS18B20 && address8[0] != DALLAS_MODEL_DS1825 &&
53  address8[0] != DALLAS_MODEL_DS28EA00) {
54  ESP_LOGW(TAG, "Unknown device type 0x%02X.", address8[0]);
55  continue;
56  }
57  this->found_sensors_.push_back(address);
58  }
59 
60  for (auto *sensor : this->sensors_) {
61  if (sensor->get_index().has_value()) {
62  if (*sensor->get_index() >= this->found_sensors_.size()) {
63  this->status_set_error("Sensor configured by index but not found");
64  continue;
65  }
66  sensor->set_address(this->found_sensors_[*sensor->get_index()]);
67  }
68 
69  if (!sensor->setup_sensor()) {
70  this->status_set_error();
71  }
72  }
73 }
75  ESP_LOGCONFIG(TAG, "DallasComponent:");
76  LOG_PIN(" Pin: ", this->pin_);
77  LOG_UPDATE_INTERVAL(this);
78 
79  if (this->found_sensors_.empty()) {
80  ESP_LOGW(TAG, " Found no sensors!");
81  } else {
82  ESP_LOGD(TAG, " Found sensors:");
83  for (auto &address : this->found_sensors_) {
84  ESP_LOGD(TAG, " 0x%s", format_hex(address).c_str());
85  }
86  }
87 
88  for (auto *sensor : this->sensors_) {
89  LOG_SENSOR(" ", "Device", sensor);
90  if (sensor->get_index().has_value()) {
91  ESP_LOGCONFIG(TAG, " Index %u", *sensor->get_index());
92  if (*sensor->get_index() >= this->found_sensors_.size()) {
93  ESP_LOGE(TAG, "Couldn't find sensor by index - not connected. Proceeding without it.");
94  continue;
95  }
96  }
97  ESP_LOGCONFIG(TAG, " Address: %s", sensor->get_address_name().c_str());
98  ESP_LOGCONFIG(TAG, " Resolution: %u", sensor->get_resolution());
99  }
100 }
101 
102 void DallasComponent::register_sensor(DallasTemperatureSensor *sensor) { this->sensors_.push_back(sensor); }
104  this->status_clear_warning();
105 
106  bool result;
107  {
108  InterruptLock lock;
109  result = this->one_wire_->reset();
110  }
111  if (!result) {
112  if (!this->found_sensors_.empty()) {
113  // Only log error if at the start sensors were found (and thus are disconnected during uptime)
114  ESP_LOGE(TAG, "Requesting conversion failed");
115  this->status_set_warning();
116  }
117 
118  for (auto *sensor : this->sensors_) {
119  sensor->publish_state(NAN);
120  }
121  return;
122  }
123 
124  {
125  InterruptLock lock;
126  this->one_wire_->skip();
127  this->one_wire_->write8(DALLAS_COMMAND_START_CONVERSION);
128  }
129 
130  for (auto *sensor : this->sensors_) {
131  if (sensor->get_address() == 0) {
132  ESP_LOGV(TAG, "'%s' - Indexed sensor not found at startup, skipping update", sensor->get_name().c_str());
133  sensor->publish_state(NAN);
134  continue;
135  }
136 
137  this->set_timeout(sensor->get_address_name(), sensor->millis_to_wait_for_conversion(), [this, sensor] {
138  bool res = sensor->read_scratch_pad();
139 
140  if (!res) {
141  ESP_LOGW(TAG, "'%s' - Resetting bus for read failed!", sensor->get_name().c_str());
142  sensor->publish_state(NAN);
143  this->status_set_warning();
144  return;
145  }
146  if (!sensor->check_scratch_pad()) {
147  sensor->publish_state(NAN);
148  this->status_set_warning();
149  return;
150  }
151 
152  float tempc = sensor->get_temp_c();
153  ESP_LOGD(TAG, "'%s': Got Temperature=%.1f°C", sensor->get_name().c_str(), tempc);
154  sensor->publish_state(tempc);
155  });
156  }
157 }
158 
159 void DallasTemperatureSensor::set_address(uint64_t address) { this->address_ = address; }
160 uint8_t DallasTemperatureSensor::get_resolution() const { return this->resolution_; }
161 void DallasTemperatureSensor::set_resolution(uint8_t resolution) { this->resolution_ = resolution; }
163 void DallasTemperatureSensor::set_index(uint8_t index) { this->index_ = index; }
164 uint8_t *DallasTemperatureSensor::get_address8() { return reinterpret_cast<uint8_t *>(&this->address_); }
165 uint64_t DallasTemperatureSensor::get_address() { return this->address_; }
166 
168  if (this->address_name_.empty()) {
169  this->address_name_ = std::string("0x") + format_hex(this->address_);
170  }
171 
172  return this->address_name_;
173 }
175  auto *wire = this->parent_->one_wire_;
176 
177  {
178  InterruptLock lock;
179 
180  if (!wire->reset()) {
181  return false;
182  }
183 
184  wire->select(this->address_);
185  wire->write8(DALLAS_COMMAND_READ_SCRATCH_PAD);
186 
187  for (unsigned char &i : this->scratch_pad_) {
188  i = wire->read8();
189  }
190  }
191 
192  return true;
193 }
195  bool r = this->read_scratch_pad();
196 
197  if (!r) {
198  ESP_LOGE(TAG, "Reading scratchpad failed: reset");
199  return false;
200  }
201  if (!this->check_scratch_pad())
202  return false;
203 
204  if (this->scratch_pad_[4] == this->resolution_)
205  return false;
206 
207  if (this->get_address8()[0] == DALLAS_MODEL_DS18S20) {
208  // DS18S20 doesn't support resolution.
209  ESP_LOGW(TAG, "DS18S20 doesn't support setting resolution.");
210  return false;
211  }
212 
213  switch (this->resolution_) {
214  case 12:
215  this->scratch_pad_[4] = 0x7F;
216  break;
217  case 11:
218  this->scratch_pad_[4] = 0x5F;
219  break;
220  case 10:
221  this->scratch_pad_[4] = 0x3F;
222  break;
223  case 9:
224  default:
225  this->scratch_pad_[4] = 0x1F;
226  break;
227  }
228 
229  auto *wire = this->parent_->one_wire_;
230  {
231  InterruptLock lock;
232  if (wire->reset()) {
233  wire->select(this->address_);
234  wire->write8(DALLAS_COMMAND_WRITE_SCRATCH_PAD);
235  wire->write8(this->scratch_pad_[2]); // high alarm temp
236  wire->write8(this->scratch_pad_[3]); // low alarm temp
237  wire->write8(this->scratch_pad_[4]); // resolution
238  wire->reset();
239 
240  // write value to EEPROM
241  wire->select(this->address_);
242  wire->write8(0x48);
243  }
244  }
245 
246  delay(20); // allow it to finish operation
247  wire->reset();
248  return true;
249 }
251  bool chksum_validity = (crc8(this->scratch_pad_, 8) == this->scratch_pad_[8]);
252  bool config_validity = false;
253 
254  switch (this->get_address8()[0]) {
255  case DALLAS_MODEL_DS18B20:
256  config_validity = ((this->scratch_pad_[4] & 0x9F) == 0x1F);
257  break;
258  default:
259  config_validity = ((this->scratch_pad_[4] & 0x10) == 0x10);
260  }
261 
262 #ifdef ESPHOME_LOG_LEVEL_VERY_VERBOSE
263  ESP_LOGVV(TAG, "Scratch pad: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X (%02X)", this->scratch_pad_[0],
264  this->scratch_pad_[1], this->scratch_pad_[2], this->scratch_pad_[3], this->scratch_pad_[4],
265  this->scratch_pad_[5], this->scratch_pad_[6], this->scratch_pad_[7], this->scratch_pad_[8],
266  crc8(this->scratch_pad_, 8));
267 #endif
268  if (!chksum_validity) {
269  ESP_LOGW(TAG, "'%s' - Scratch pad checksum invalid!", this->get_name().c_str());
270  } else if (!config_validity) {
271  ESP_LOGW(TAG, "'%s' - Scratch pad config register invalid!", this->get_name().c_str());
272  }
273  return chksum_validity && config_validity;
274 }
276  int16_t temp = (int16_t(this->scratch_pad_[1]) << 11) | (int16_t(this->scratch_pad_[0]) << 3);
277  if (this->get_address8()[0] == DALLAS_MODEL_DS18S20) {
278  int diff = (this->scratch_pad_[7] - this->scratch_pad_[6]) << 7;
279  temp = ((temp & 0xFFF0) << 3) - 16 + (diff / this->scratch_pad_[7]);
280  }
281 
282  return temp / 128.0f;
283 }
284 std::string DallasTemperatureSensor::unique_id() { return "dallas-" + str_lower_case(format_hex(this->address_)); }
285 
286 } // namespace dallas
287 } // namespace esphome
optional< uint8_t > get_index() const
Get the index of this sensor. (0 if using address.)
void set_address(uint64_t address)
Set the 64-bit unsigned address for this sensor.
Internal class that helps us create multiple sensors for one Dallas hub.
std::string format_hex(const uint8_t *data, size_t length)
Format the byte array data of length len in lowercased hex.
Definition: helpers.cpp:349
uint8_t crc8(uint8_t *data, uint8_t len)
Calculate a CRC-8 checksum of data with size len.
Definition: helpers.cpp:96
uint8_t get_resolution() const
Get the set resolution for this sensor.
const std::string & get_address_name()
Helper to create (and cache) the name for this sensor. For example "0xfe0000031f1eaf29".
uint16_t millis_to_wait_for_conversion() const
Get the number of milliseconds we have to wait for the conversion phase.
std::string str_lower_case(const std::string &str)
Convert the string to lower case.
Definition: helpers.cpp:279
uint8_t * get_address8()
Helper to get a pointer to the address as uint8_t.
Helper class to disable interrupts.
Definition: helpers.h:587
void set_resolution(uint8_t resolution)
Set the resolution for this sensor.
void register_sensor(DallasTemperatureSensor *sensor)
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition: core.cpp:28
void set_index(uint8_t index)
Set the index of this sensor. If using index, address will be set after setup.
const StringRef & get_name() const
Definition: entity_base.cpp:10
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26