ESPHome  2024.12.0
nau7802.cpp
Go to the documentation of this file.
1 #include "nau7802.h"
2 #include "esphome/core/log.h"
3 #include "esphome/core/hal.h"
4 
5 namespace esphome {
6 namespace nau7802 {
7 
8 static const char *const TAG = "nau7802";
9 
10 // Only define what we need
11 
12 static const uint8_t READ_BIT = 0x01;
13 
14 static const uint8_t PU_CTRL_REG = 0x00;
15 static const uint8_t PU_CTRL_REGISTER_RESET = 0x01;
16 static const uint8_t PU_CTRL_POWERUP_DIGITAL = 0x02;
17 static const uint8_t PU_CTRL_POWERUP_ANALOG = 0x04;
18 static const uint8_t PU_CTRL_POWERUP_READY = 0x08;
19 static const uint8_t PU_CTRL_CYCLE_START = 0x10;
20 static const uint8_t PU_CTRL_CYCLE_READY = 0x20;
21 static const uint8_t PU_CTRL_AVDD_EXTERNAL = 0x80;
22 
23 static const uint8_t CTRL1_REG = 0x01;
24 static const uint8_t CTRL1_LDO_SHIFT = 3;
25 static const uint8_t CTRL1_LDO_MASK = (0x7 << CTRL1_LDO_SHIFT);
26 static const uint8_t CTRL1_GAIN_MASK = 0x7;
27 
28 static const uint8_t CTRL2_REG = 0x02;
29 static const uint8_t CTRL2_CRS_SHIFT = 4;
30 static const uint8_t CTRL2_CRS_MASK = (0x7 << CTRL2_CRS_SHIFT);
31 static const uint8_t CTRL2_CALS = 0x04;
32 static const uint8_t CTRL2_CAL_ERR = 0x08;
33 static const uint8_t CTRL2_GAIN_CALIBRATION = 0x03;
34 static const uint8_t CTRL2_CONFIG_MASK = 0xF0;
35 
36 static const uint8_t OCAL1_B2_REG = 0x03;
37 static const uint8_t GCAL1_B3_REG = 0x06;
38 static const uint8_t GCAL1_FRACTIONAL = 23;
39 
40 // only need the first data register for sequential read method
41 static const uint8_t ADCO_B2_REG = 0x12;
42 
43 static const uint8_t ADC_REG = 0x15;
44 static const uint8_t ADC_CHPS_DISABLE = 0x30;
45 
46 static const uint8_t PGA_REG = 0x1B;
47 static const uint8_t PGA_LDOMODE_ESR = 0x40;
48 
49 static const uint8_t POWER_REG = 0x1C;
50 static const uint8_t POWER_PGA_CAP_EN = 0x80;
51 
52 static const uint8_t DEVICE_REV = 0x1F;
53 
55  i2c::I2CRegister pu_ctrl = this->reg(PU_CTRL_REG);
56  ESP_LOGCONFIG(TAG, "Setting up NAU7802 '%s'...", this->name_.c_str());
57  uint8_t rev;
58 
59  if (this->read_register(DEVICE_REV | READ_BIT, &rev, 1)) {
60  ESP_LOGE(TAG, "Failed I2C read during setup()");
61  this->mark_failed();
62  return;
63  }
64  ESP_LOGI(TAG, "Setting up NAU7802 Rev %d", rev);
65 
66  // reset
67  pu_ctrl |= PU_CTRL_REGISTER_RESET;
68  delay(10);
69  pu_ctrl &= ~PU_CTRL_REGISTER_RESET;
70 
71  // power up digital hw
72  pu_ctrl |= PU_CTRL_POWERUP_DIGITAL;
73 
74  delay(1);
75  if (!(pu_ctrl.get() & PU_CTRL_POWERUP_READY)) {
76  ESP_LOGE(TAG, "Failed to reset sensor during setup()");
77  this->mark_failed();
78  return;
79  }
80 
81  uint32_t gcal = (uint32_t) (round(this->gain_calibration_ * (1 << GCAL1_FRACTIONAL)));
82  this->write_value_(OCAL1_B2_REG, 3, this->offset_calibration_);
83  this->write_value_(GCAL1_B3_REG, 4, gcal);
84 
85  // turn on AFE
86  pu_ctrl |= PU_CTRL_POWERUP_ANALOG;
87  auto f = std::bind(&NAU7802Sensor::complete_setup_, this);
88  this->set_timeout(600, f);
89 }
90 
92  i2c::I2CRegister pu_ctrl = this->reg(PU_CTRL_REG);
93  i2c::I2CRegister ctrl1 = this->reg(CTRL1_REG);
94  i2c::I2CRegister ctrl2 = this->reg(CTRL2_REG);
95  pu_ctrl |= PU_CTRL_CYCLE_START;
96 
97  // set gain
98  ctrl1 &= ~CTRL1_GAIN_MASK;
99  ctrl1 |= this->gain_;
100 
101  // enable internal LDO
102  if (this->ldo_ != NAU7802_LDO_EXTERNAL) {
103  pu_ctrl |= PU_CTRL_AVDD_EXTERNAL;
104  ctrl1 &= ~CTRL1_LDO_MASK;
105  ctrl1 |= this->ldo_ << CTRL1_LDO_SHIFT;
106  }
107 
108  // set sps
109  ctrl2 &= ~CTRL2_CRS_MASK;
110  ctrl2 |= this->sps_ << CTRL2_CRS_SHIFT;
111 
112  // disable ADC chopper clock
113  i2c::I2CRegister adc_reg = this->reg(ADC_REG);
114  adc_reg |= ADC_CHPS_DISABLE;
115 
116  // use low ESR caps
117  i2c::I2CRegister pga_reg = this->reg(PGA_REG);
118  pga_reg &= ~PGA_LDOMODE_ESR;
119 
120  // PGA stabilizer cap on output
121  i2c::I2CRegister pwr_reg = this->reg(POWER_REG);
122  pwr_reg |= POWER_PGA_CAP_EN;
123 
124  this->setup_complete_ = true;
125 }
126 
128  LOG_SENSOR("", "NAU7802", this);
129  LOG_I2C_DEVICE(this);
130 
131  if (this->is_failed()) {
132  ESP_LOGE(TAG, "Communication with NAU7802 failed earlier, during setup");
133  return;
134  }
135  // Note these may differ from the values on the device if calbration has been run
136  ESP_LOGCONFIG(TAG, " Offset Calibration: %s", to_string(this->offset_calibration_).c_str());
137  ESP_LOGCONFIG(TAG, " Gain Calibration: %f", this->gain_calibration_);
138 
139  std::string voltage = "unknown";
140  switch (this->ldo_) {
141  case NAU7802_LDO_2V4:
142  voltage = "2.4V";
143  break;
144  case NAU7802_LDO_2V7:
145  voltage = "2.7V";
146  break;
147  case NAU7802_LDO_3V0:
148  voltage = "3.0V";
149  break;
150  case NAU7802_LDO_3V3:
151  voltage = "3.3V";
152  break;
153  case NAU7802_LDO_3V6:
154  voltage = "3.6V";
155  break;
156  case NAU7802_LDO_3V9:
157  voltage = "3.9V";
158  break;
159  case NAU7802_LDO_4V2:
160  voltage = "4.2V";
161  break;
162  case NAU7802_LDO_4V5:
163  voltage = "4.5V";
164  break;
166  voltage = "External";
167  break;
168  }
169  ESP_LOGCONFIG(TAG, " LDO Voltage: %s", voltage.c_str());
170  int gain = 0;
171  switch (this->gain_) {
172  case NAU7802_GAIN_128:
173  gain = 128;
174  break;
175  case NAU7802_GAIN_64:
176  gain = 64;
177  break;
178  case NAU7802_GAIN_32:
179  gain = 32;
180  break;
181  case NAU7802_GAIN_16:
182  gain = 16;
183  break;
184  case NAU7802_GAIN_8:
185  gain = 8;
186  break;
187  case NAU7802_GAIN_4:
188  gain = 4;
189  break;
190  case NAU7802_GAIN_2:
191  gain = 2;
192  break;
193  case NAU7802_GAIN_1:
194  gain = 1;
195  break;
196  }
197  ESP_LOGCONFIG(TAG, " Gain: %dx", gain);
198  int sps = 0;
199  switch (this->sps_) {
200  case NAU7802_SPS_320:
201  sps = 320;
202  break;
203  case NAU7802_SPS_80:
204  sps = 80;
205  break;
206  case NAU7802_SPS_40:
207  sps = 40;
208  break;
209  case NAU7802_SPS_20:
210  sps = 20;
211  break;
212  case NAU7802_SPS_10:
213  sps = 10;
214  break;
215  }
216  ESP_LOGCONFIG(TAG, " Samples Per Second: %d", sps);
217  LOG_UPDATE_INTERVAL(this);
218 }
219 
220 void NAU7802Sensor::write_value_(uint8_t start_reg, size_t size, int32_t value) {
221  uint8_t data[4];
222  for (int i = 0; i < size; i++) {
223  data[i] = 0xFF & (value >> (size - 1 - i) * 8);
224  }
225  this->write_register(start_reg, data, size);
226 }
227 
228 int32_t NAU7802Sensor::read_value_(uint8_t start_reg, size_t size) {
229  uint8_t data[4];
230  this->read_register(start_reg, data, size);
231  int32_t result = 0;
232  for (int i = 0; i < size; i++) {
233  result |= data[i] << (size - 1 - i) * 8;
234  }
235  // extend sign bit
236  if (result & 0x800000 && size == 3) {
237  result |= 0xFF000000;
238  }
239  return result;
240 }
241 
243  // check if already calbrating
244  if (this->state_ != CalibrationState::INACTIVE) {
245  ESP_LOGW(TAG, "Calibration already in progress");
246  return false;
247  }
248 
250 
251  i2c::I2CRegister ctrl2 = this->reg(CTRL2_REG);
252  // clear calibraye registers
253  ctrl2 &= CTRL2_CONFIG_MASK;
254  // Calibrate
255  ctrl2 |= mode;
256  ctrl2 |= CTRL2_CALS;
257  return true;
258 }
259 
261  switch (this->state_) {
263  this->gain_calibration_failed_ = failed;
264  break;
266  this->offset_calibration_failed_ = failed;
267  break;
269  // shouldn't happen
270  break;
271  }
272 }
273 
275  i2c::I2CRegister ctrl2 = this->reg(CTRL2_REG);
276 
277  if (this->state_ != CalibrationState::INACTIVE && !(ctrl2.get() & CTRL2_CALS)) {
278  if (ctrl2.get() & CTRL2_CAL_ERR) {
279  this->set_calibration_failure_(true);
280  this->state_ = CalibrationState::INACTIVE;
281  ESP_LOGE(TAG, "Failed to calibrate sensor");
282  this->status_set_error("Calibration Failed");
283  return;
284  }
285 
286  this->set_calibration_failure_(false);
287  this->state_ = CalibrationState::INACTIVE;
288 
290  this->status_clear_error();
291 
292  int32_t ocal = this->read_value_(OCAL1_B2_REG, 3);
293  ESP_LOGI(TAG, "New Offset: %s", to_string(ocal).c_str());
294  uint32_t gcal = this->read_value_(GCAL1_B3_REG, 4);
295  float gcal_f = ((float) gcal / (float) (1 << GCAL1_FRACTIONAL));
296  ESP_LOGI(TAG, "New Gain: %f", gcal_f);
297  }
298 }
299 
301 
303  if (!this->is_data_ready_()) {
304  ESP_LOGW(TAG, "No measurements ready!");
305  this->status_set_warning();
306  return;
307  }
308 
309  this->status_clear_warning();
310 
311  // Get the most recent sample to publish
312  int32_t result = this->read_value_(ADCO_B2_REG, 3);
313 
314  ESP_LOGD(TAG, "'%s': Got value %" PRId32, this->name_.c_str(), result);
315  this->publish_state(result);
316 }
317 
318 bool NAU7802Sensor::is_data_ready_() { return this->reg(PU_CTRL_REG).get() & PU_CTRL_CYCLE_READY; }
319 
321 
322 } // namespace nau7802
323 } // namespace esphome
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:19
ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len, bool stop=true)
reads an array of bytes from a specific register in the I²C device
Definition: i2c.cpp:10
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
uint8_t get() const
returns the register value
Definition: i2c.cpp:75
void set_calibration_failure_(bool failed)
Definition: nau7802.cpp:260
bool is_failed() const
Definition: component.cpp:143
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
AlsGain501 gain
float get_setup_priority() const override
Definition: nau7802.cpp:300
void status_set_error(const char *message="unspecified")
Definition: component.cpp:159
void status_clear_warning()
Definition: component.cpp:166
BedjetMode mode
BedJet operating mode.
Definition: bedjet_codec.h:183
bool calibrate_(enum NAU7802CalibrationModes mode)
Definition: nau7802.cpp:242
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
void write_value_(uint8_t start_reg, size_t size, int32_t value)
Definition: nau7802.cpp:220
constexpr const char * c_str() const
Definition: string_ref.h:68
void status_clear_error()
Definition: component.cpp:172
std::string to_string(int value)
Definition: helpers.cpp:81
This class is used to create I2CRegister objects that act as proxies to read/write internal registers...
Definition: i2c.h:33
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:118
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len, bool stop=true)
writes an array of bytes to a specific register in the I²C device
Definition: i2c.cpp:25
int32_t read_value_(uint8_t start_reg, size_t size)
Definition: nau7802.cpp:228
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26