ESPHome  2024.3.1
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();
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  ESP_LOGE(TAG, "Requesting conversion failed");
113  this->status_set_warning();
114  for (auto *sensor : this->sensors_) {
115  sensor->publish_state(NAN);
116  }
117  return;
118  }
119 
120  {
121  InterruptLock lock;
122  this->one_wire_->skip();
123  this->one_wire_->write8(DALLAS_COMMAND_START_CONVERSION);
124  }
125 
126  for (auto *sensor : this->sensors_) {
127  this->set_timeout(sensor->get_address_name(), sensor->millis_to_wait_for_conversion(), [this, sensor] {
128  bool res = sensor->read_scratch_pad();
129 
130  if (!res) {
131  ESP_LOGW(TAG, "'%s' - Resetting bus for read failed!", sensor->get_name().c_str());
132  sensor->publish_state(NAN);
133  this->status_set_warning();
134  return;
135  }
136  if (!sensor->check_scratch_pad()) {
137  sensor->publish_state(NAN);
138  this->status_set_warning();
139  return;
140  }
141 
142  float tempc = sensor->get_temp_c();
143  ESP_LOGD(TAG, "'%s': Got Temperature=%.1f°C", sensor->get_name().c_str(), tempc);
144  sensor->publish_state(tempc);
145  });
146  }
147 }
148 
149 void DallasTemperatureSensor::set_address(uint64_t address) { this->address_ = address; }
150 uint8_t DallasTemperatureSensor::get_resolution() const { return this->resolution_; }
151 void DallasTemperatureSensor::set_resolution(uint8_t resolution) { this->resolution_ = resolution; }
153 void DallasTemperatureSensor::set_index(uint8_t index) { this->index_ = index; }
154 uint8_t *DallasTemperatureSensor::get_address8() { return reinterpret_cast<uint8_t *>(&this->address_); }
156  if (this->address_name_.empty()) {
157  this->address_name_ = std::string("0x") + format_hex(this->address_);
158  }
159 
160  return this->address_name_;
161 }
163  auto *wire = this->parent_->one_wire_;
164 
165  {
166  InterruptLock lock;
167 
168  if (!wire->reset()) {
169  return false;
170  }
171 
172  wire->select(this->address_);
173  wire->write8(DALLAS_COMMAND_READ_SCRATCH_PAD);
174 
175  for (unsigned char &i : this->scratch_pad_) {
176  i = wire->read8();
177  }
178  }
179 
180  return true;
181 }
183  bool r = this->read_scratch_pad();
184 
185  if (!r) {
186  ESP_LOGE(TAG, "Reading scratchpad failed: reset");
187  return false;
188  }
189  if (!this->check_scratch_pad())
190  return false;
191 
192  if (this->scratch_pad_[4] == this->resolution_)
193  return false;
194 
195  if (this->get_address8()[0] == DALLAS_MODEL_DS18S20) {
196  // DS18S20 doesn't support resolution.
197  ESP_LOGW(TAG, "DS18S20 doesn't support setting resolution.");
198  return false;
199  }
200 
201  switch (this->resolution_) {
202  case 12:
203  this->scratch_pad_[4] = 0x7F;
204  break;
205  case 11:
206  this->scratch_pad_[4] = 0x5F;
207  break;
208  case 10:
209  this->scratch_pad_[4] = 0x3F;
210  break;
211  case 9:
212  default:
213  this->scratch_pad_[4] = 0x1F;
214  break;
215  }
216 
217  auto *wire = this->parent_->one_wire_;
218  {
219  InterruptLock lock;
220  if (wire->reset()) {
221  wire->select(this->address_);
222  wire->write8(DALLAS_COMMAND_WRITE_SCRATCH_PAD);
223  wire->write8(this->scratch_pad_[2]); // high alarm temp
224  wire->write8(this->scratch_pad_[3]); // low alarm temp
225  wire->write8(this->scratch_pad_[4]); // resolution
226  wire->reset();
227 
228  // write value to EEPROM
229  wire->select(this->address_);
230  wire->write8(0x48);
231  }
232  }
233 
234  delay(20); // allow it to finish operation
235  wire->reset();
236  return true;
237 }
239  bool chksum_validity = (crc8(this->scratch_pad_, 8) == this->scratch_pad_[8]);
240  bool config_validity = false;
241 
242  switch (this->get_address8()[0]) {
243  case DALLAS_MODEL_DS18B20:
244  config_validity = ((this->scratch_pad_[4] & 0x9F) == 0x1F);
245  break;
246  default:
247  config_validity = ((this->scratch_pad_[4] & 0x10) == 0x10);
248  }
249 
250 #ifdef ESPHOME_LOG_LEVEL_VERY_VERBOSE
251  ESP_LOGVV(TAG, "Scratch pad: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X (%02X)", this->scratch_pad_[0],
252  this->scratch_pad_[1], this->scratch_pad_[2], this->scratch_pad_[3], this->scratch_pad_[4],
253  this->scratch_pad_[5], this->scratch_pad_[6], this->scratch_pad_[7], this->scratch_pad_[8],
254  crc8(this->scratch_pad_, 8));
255 #endif
256  if (!chksum_validity) {
257  ESP_LOGW(TAG, "'%s' - Scratch pad checksum invalid!", this->get_name().c_str());
258  } else if (!config_validity) {
259  ESP_LOGW(TAG, "'%s' - Scratch pad config register invalid!", this->get_name().c_str());
260  }
261  return chksum_validity && config_validity;
262 }
264  int16_t temp = (int16_t(this->scratch_pad_[1]) << 11) | (int16_t(this->scratch_pad_[0]) << 3);
265  if (this->get_address8()[0] == DALLAS_MODEL_DS18S20) {
266  int diff = (this->scratch_pad_[7] - this->scratch_pad_[6]) << 7;
267  temp = ((temp & 0xFFF0) << 3) - 16 + (diff / this->scratch_pad_[7]);
268  }
269 
270  return temp / 128.0f;
271 }
272 std::string DallasTemperatureSensor::unique_id() { return "dallas-" + str_lower_case(format_hex(this->address_)); }
273 
274 } // namespace dallas
275 } // 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