ESPHome  2024.11.0
am2315c.cpp
Go to the documentation of this file.
1 // MIT License
2 //
3 // Copyright (c) 2023-2024 Rob Tillaart
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in all
13 // copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 // SOFTWARE.
22 #include "am2315c.h"
23 #include "esphome/core/hal.h"
24 #include "esphome/core/helpers.h"
25 #include "esphome/core/log.h"
26 
27 namespace esphome {
28 namespace am2315c {
29 
30 static const char *const TAG = "am2315c";
31 
32 uint8_t AM2315C::crc8_(uint8_t *data, uint8_t len) {
33  uint8_t crc = 0xFF;
34  while (len--) {
35  crc ^= *data++;
36  for (uint8_t i = 0; i < 8; i++) {
37  if (crc & 0x80) {
38  crc <<= 1;
39  crc ^= 0x31;
40  } else {
41  crc <<= 1;
42  }
43  }
44  }
45  return crc;
46 }
47 
49  // code based on demo code sent by www.aosong.com
50  // no further documentation.
51  // 0x1B returned 18, 0, 4
52  // 0x1C returned 18, 65, 0
53  // 0x1E returned 18, 8, 0
54  // 18 seems to be status register
55  // other values unknown.
56  uint8_t data[3];
57  data[0] = reg;
58  data[1] = 0;
59  data[2] = 0;
60  ESP_LOGD(TAG, "Reset register: 0x%02x", reg);
61  if (this->write(data, 3) != i2c::ERROR_OK) {
62  ESP_LOGE(TAG, "Write failed!");
63  this->mark_failed();
64  return false;
65  }
66  delay(5);
67  if (this->read(data, 3) != i2c::ERROR_OK) {
68  ESP_LOGE(TAG, "Read failed!");
69  this->mark_failed();
70  return false;
71  }
72  delay(10);
73  data[0] = 0xB0 | reg;
74  if (this->write(data, 3) != i2c::ERROR_OK) {
75  ESP_LOGE(TAG, "Write failed!");
76  this->mark_failed();
77  return false;
78  }
79  delay(5);
80  return true;
81 }
82 
83 bool AM2315C::convert_(uint8_t *data, float &humidity, float &temperature) {
84  uint32_t raw;
85  raw = (data[1] << 12) | (data[2] << 4) | (data[3] >> 4);
86  humidity = raw * 9.5367431640625e-5;
87  raw = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
88  temperature = raw * 1.9073486328125e-4 - 50;
89  return this->crc8_(data, 6) == data[6];
90 }
91 
93  ESP_LOGCONFIG(TAG, "Setting up AM2315C...");
94 
95  // get status
96  uint8_t status = 0;
97  if (this->read(&status, 1) != i2c::ERROR_OK) {
98  ESP_LOGE(TAG, "Read failed!");
99  this->mark_failed();
100  return;
101  }
102 
103  // reset registers if required, according to the datasheet
104  // this can be required after power on, although this was
105  // never required during testing
106  if ((status & 0x18) != 0x18) {
107  ESP_LOGD(TAG, "Resetting AM2315C registers");
108  if (!this->reset_register_(0x1B)) {
109  this->mark_failed();
110  return;
111  }
112  if (!this->reset_register_(0x1C)) {
113  this->mark_failed();
114  return;
115  }
116  if (!this->reset_register_(0x1E)) {
117  this->mark_failed();
118  return;
119  }
120  }
121 }
122 
124  // request measurement
125  uint8_t data[3];
126  data[0] = 0xAC;
127  data[1] = 0x33;
128  data[2] = 0x00;
129  if (this->write(data, 3) != i2c::ERROR_OK) {
130  ESP_LOGE(TAG, "Write failed!");
131  this->mark_failed();
132  return;
133  }
134 
135  // wait for hw to complete measurement
136  set_timeout(160, [this]() {
137  // check status
138  uint8_t status = 0;
139  if (this->read(&status, 1) != i2c::ERROR_OK) {
140  ESP_LOGE(TAG, "Read failed!");
141  this->mark_failed();
142  return;
143  }
144  if ((status & 0x80) == 0x80) {
145  ESP_LOGE(TAG, "HW still busy!");
146  this->mark_failed();
147  return;
148  }
149 
150  // read
151  uint8_t data[7];
152  if (this->read(data, 7) != i2c::ERROR_OK) {
153  ESP_LOGE(TAG, "Read failed!");
154  this->mark_failed();
155  return;
156  }
157 
158  // check for all zeros
159  bool zeros = true;
160  for (uint8_t i : data) {
161  zeros = zeros && (i == 0);
162  }
163  if (zeros) {
164  ESP_LOGW(TAG, "Data all zeros!");
165  this->status_set_warning();
166  return;
167  }
168 
169  // convert
170  float temperature = 0.0;
171  float humidity = 0.0;
172  if (this->convert_(data, humidity, temperature)) {
173  if (this->temperature_sensor_ != nullptr) {
174  this->temperature_sensor_->publish_state(temperature);
175  }
176  if (this->humidity_sensor_ != nullptr) {
177  this->humidity_sensor_->publish_state(humidity);
178  }
179  this->status_clear_warning();
180  } else {
181  ESP_LOGW(TAG, "CRC failed!");
182  this->status_set_warning();
183  }
184  });
185 }
186 
188  ESP_LOGCONFIG(TAG, "AM2315C:");
189  LOG_I2C_DEVICE(this);
190  if (this->is_failed()) {
191  ESP_LOGE(TAG, "Communication with AM2315C failed!");
192  }
193  LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
194  LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
195 }
196 
198 
199 } // namespace am2315c
200 } // namespace esphome
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:19
uint8_t raw[35]
Definition: bl0939.h:19
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
I2CRegister reg(uint8_t a_register)
calls the I2CRegister constructor
Definition: i2c.h:149
bool is_failed() const
Definition: component.cpp:143
ErrorCode read(uint8_t *data, size_t len)
reads an array of bytes from the device using an I2CBus
Definition: i2c.h:160
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
Definition: component.cpp:69
void dump_config() override
Definition: am2315c.cpp:187
ErrorCode write(const uint8_t *data, size_t len, bool stop=true)
writes an array of bytes to a device using an I2CBus
Definition: i2c.h:186
bool convert_(uint8_t *data, float &humidity, float &temperature)
Definition: am2315c.cpp:83
sensor::Sensor * temperature_sensor_
Definition: am2315c.h:46
No error found during execution of method.
Definition: i2c_bus.h:13
void status_clear_warning()
Definition: component.cpp:166
float get_setup_priority() const override
Definition: am2315c.cpp:197
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
uint16_t temperature
Definition: sun_gtil2.cpp:26
void setup() override
Definition: am2315c.cpp:92
sensor::Sensor * humidity_sensor_
Definition: am2315c.h:47
uint8_t status
Definition: bl0942.h:74
std::string size_t len
Definition: helpers.h:293
bool reset_register_(uint8_t reg)
Definition: am2315c.cpp:48
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:118
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
void update() override
Definition: am2315c.cpp:123
uint8_t crc8_(uint8_t *data, uint8_t len)
Definition: am2315c.cpp:32
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26