ESPHome  2022.12.8
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 
173  {
174  InterruptLock lock;
175 
176  wire->select(this->address_);
177  wire->write8(DALLAS_COMMAND_READ_SCRATCH_PAD);
178 
179  for (unsigned char &i : this->scratch_pad_) {
180  i = wire->read8();
181  }
182  }
183 
184  return true;
185 }
187  bool r = this->read_scratch_pad();
188 
189  if (!r) {
190  ESP_LOGE(TAG, "Reading scratchpad failed: reset");
191  return false;
192  }
193  if (!this->check_scratch_pad())
194  return false;
195 
196  if (this->scratch_pad_[4] == this->resolution_)
197  return false;
198 
199  if (this->get_address8()[0] == DALLAS_MODEL_DS18S20) {
200  // DS18S20 doesn't support resolution.
201  ESP_LOGW(TAG, "DS18S20 doesn't support setting resolution.");
202  return false;
203  }
204 
205  switch (this->resolution_) {
206  case 12:
207  this->scratch_pad_[4] = 0x7F;
208  break;
209  case 11:
210  this->scratch_pad_[4] = 0x5F;
211  break;
212  case 10:
213  this->scratch_pad_[4] = 0x3F;
214  break;
215  case 9:
216  default:
217  this->scratch_pad_[4] = 0x1F;
218  break;
219  }
220 
221  auto *wire = this->parent_->one_wire_;
222  {
223  InterruptLock lock;
224  if (wire->reset()) {
225  wire->select(this->address_);
226  wire->write8(DALLAS_COMMAND_WRITE_SCRATCH_PAD);
227  wire->write8(this->scratch_pad_[2]); // high alarm temp
228  wire->write8(this->scratch_pad_[3]); // low alarm temp
229  wire->write8(this->scratch_pad_[4]); // resolution
230  wire->reset();
231 
232  // write value to EEPROM
233  wire->select(this->address_);
234  wire->write8(0x48);
235  }
236  }
237 
238  delay(20); // allow it to finish operation
239  wire->reset();
240  return true;
241 }
243  bool chksum_validity = (crc8(this->scratch_pad_, 8) == this->scratch_pad_[8]);
244  bool config_validity = false;
245 
246  switch (this->get_address8()[0]) {
247  case DALLAS_MODEL_DS18B20:
248  config_validity = ((this->scratch_pad_[4] & 0x9F) == 0x1F);
249  break;
250  default:
251  config_validity = ((this->scratch_pad_[4] & 0x10) == 0x10);
252  }
253 
254 #ifdef ESPHOME_LOG_LEVEL_VERY_VERBOSE
255  ESP_LOGVV(TAG, "Scratch pad: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X (%02X)", this->scratch_pad_[0],
256  this->scratch_pad_[1], this->scratch_pad_[2], this->scratch_pad_[3], this->scratch_pad_[4],
257  this->scratch_pad_[5], this->scratch_pad_[6], this->scratch_pad_[7], this->scratch_pad_[8],
258  crc8(this->scratch_pad_, 8));
259 #endif
260  if (!chksum_validity) {
261  ESP_LOGW(TAG, "'%s' - Scratch pad checksum invalid!", this->get_name().c_str());
262  } else if (!config_validity) {
263  ESP_LOGW(TAG, "'%s' - Scratch pad config register invalid!", this->get_name().c_str());
264  }
265  return chksum_validity && config_validity;
266 }
268  int16_t temp = (int16_t(this->scratch_pad_[1]) << 11) | (int16_t(this->scratch_pad_[0]) << 3);
269  if (this->get_address8()[0] == DALLAS_MODEL_DS18S20) {
270  int diff = (this->scratch_pad_[7] - this->scratch_pad_[6]) << 7;
271  temp = ((temp & 0xFFF0) << 3) - 16 + (diff / this->scratch_pad_[7]);
272  }
273 
274  return temp / 128.0f;
275 }
276 std::string DallasTemperatureSensor::unique_id() { return "dallas-" + str_lower_case(format_hex(this->address_)); }
277 
278 } // namespace dallas
279 } // 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:225
uint8_t crc8(uint8_t *data, uint8_t len)
Calculate a CRC-8 checksum of data with size len.
Definition: helpers.cpp:56
uint8_t get_resolution() const
Get the set resolution for this sensor.
const std::string & get_name() const
Definition: entity_base.cpp:11
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:158
uint8_t * get_address8()
Helper to get a pointer to the address as uint8_t.
Helper class to disable interrupts.
Definition: helpers.h:539
void set_resolution(uint8_t resolution)
Set the resolution for this sensor.
void register_sensor(DallasTemperatureSensor *sensor)
Definition: a4988.cpp:4
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition: core.cpp:29
void set_index(uint8_t index)
Set the index of this sensor. If using index, address will be set after setup.
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:27