ESPHome  2025.4.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 
126  LOG_SENSOR("", "NAU7802", this);
127  LOG_I2C_DEVICE(this);
128 
129  if (this->is_failed()) {
130  ESP_LOGE(TAG, "Communication with NAU7802 failed earlier, during setup");
131  return;
132  }
133  // Note these may differ from the values on the device if calbration has been run
134  ESP_LOGCONFIG(TAG, " Offset Calibration: %s", to_string(this->offset_calibration_).c_str());
135  ESP_LOGCONFIG(TAG, " Gain Calibration: %f", this->gain_calibration_);
136 
137  std::string voltage = "unknown";
138  switch (this->ldo_) {
139  case NAU7802_LDO_2V4:
140  voltage = "2.4V";
141  break;
142  case NAU7802_LDO_2V7:
143  voltage = "2.7V";
144  break;
145  case NAU7802_LDO_3V0:
146  voltage = "3.0V";
147  break;
148  case NAU7802_LDO_3V3:
149  voltage = "3.3V";
150  break;
151  case NAU7802_LDO_3V6:
152  voltage = "3.6V";
153  break;
154  case NAU7802_LDO_3V9:
155  voltage = "3.9V";
156  break;
157  case NAU7802_LDO_4V2:
158  voltage = "4.2V";
159  break;
160  case NAU7802_LDO_4V5:
161  voltage = "4.5V";
162  break;
164  voltage = "External";
165  break;
166  }
167  ESP_LOGCONFIG(TAG, " LDO Voltage: %s", voltage.c_str());
168  int gain = 0;
169  switch (this->gain_) {
170  case NAU7802_GAIN_128:
171  gain = 128;
172  break;
173  case NAU7802_GAIN_64:
174  gain = 64;
175  break;
176  case NAU7802_GAIN_32:
177  gain = 32;
178  break;
179  case NAU7802_GAIN_16:
180  gain = 16;
181  break;
182  case NAU7802_GAIN_8:
183  gain = 8;
184  break;
185  case NAU7802_GAIN_4:
186  gain = 4;
187  break;
188  case NAU7802_GAIN_2:
189  gain = 2;
190  break;
191  case NAU7802_GAIN_1:
192  gain = 1;
193  break;
194  }
195  ESP_LOGCONFIG(TAG, " Gain: %dx", gain);
196  int sps = 0;
197  switch (this->sps_) {
198  case NAU7802_SPS_320:
199  sps = 320;
200  break;
201  case NAU7802_SPS_80:
202  sps = 80;
203  break;
204  case NAU7802_SPS_40:
205  sps = 40;
206  break;
207  case NAU7802_SPS_20:
208  sps = 20;
209  break;
210  case NAU7802_SPS_10:
211  sps = 10;
212  break;
213  }
214  ESP_LOGCONFIG(TAG, " Samples Per Second: %d", sps);
215  LOG_UPDATE_INTERVAL(this);
216 }
217 
218 void NAU7802Sensor::write_value_(uint8_t start_reg, size_t size, int32_t value) {
219  uint8_t data[4];
220  for (int i = 0; i < size; i++) {
221  data[i] = 0xFF & (value >> (size - 1 - i) * 8);
222  }
223  this->write_register(start_reg, data, size);
224 }
225 
226 int32_t NAU7802Sensor::read_value_(uint8_t start_reg, size_t size) {
227  uint8_t data[4];
228  this->read_register(start_reg, data, size);
229  int32_t result = 0;
230  for (int i = 0; i < size; i++) {
231  result |= data[i] << (size - 1 - i) * 8;
232  }
233  // extend sign bit
234  if (result & 0x800000 && size == 3) {
235  result |= 0xFF000000;
236  }
237  return result;
238 }
239 
241  // check if already calbrating
242  if (this->state_ != CalibrationState::INACTIVE) {
243  ESP_LOGW(TAG, "Calibration already in progress");
244  return false;
245  }
246 
248 
249  i2c::I2CRegister ctrl2 = this->reg(CTRL2_REG);
250  // clear calibraye registers
251  ctrl2 &= CTRL2_CONFIG_MASK;
252  // Calibrate
253  ctrl2 |= mode;
254  ctrl2 |= CTRL2_CALS;
255  return true;
256 }
257 
259  switch (this->state_) {
261  this->gain_calibration_failed_ = failed;
262  break;
264  this->offset_calibration_failed_ = failed;
265  break;
267  // shouldn't happen
268  break;
269  }
270 }
271 
273  i2c::I2CRegister ctrl2 = this->reg(CTRL2_REG);
274 
275  if (this->state_ != CalibrationState::INACTIVE && !(ctrl2.get() & CTRL2_CALS)) {
276  if (ctrl2.get() & CTRL2_CAL_ERR) {
277  this->set_calibration_failure_(true);
278  this->state_ = CalibrationState::INACTIVE;
279  ESP_LOGE(TAG, "Failed to calibrate sensor");
280  this->status_set_error("Calibration Failed");
281  return;
282  }
283 
284  this->set_calibration_failure_(false);
285  this->state_ = CalibrationState::INACTIVE;
286 
288  this->status_clear_error();
289 
290  int32_t ocal = this->read_value_(OCAL1_B2_REG, 3);
291  ESP_LOGI(TAG, "New Offset: %s", to_string(ocal).c_str());
292  uint32_t gcal = this->read_value_(GCAL1_B3_REG, 4);
293  float gcal_f = ((float) gcal / (float) (1 << GCAL1_FRACTIONAL));
294  ESP_LOGI(TAG, "New Gain: %f", gcal_f);
295  }
296 }
297 
299 
301  if (!this->is_data_ready_()) {
302  ESP_LOGW(TAG, "No measurements ready!");
303  this->status_set_warning();
304  return;
305  }
306 
307  this->status_clear_warning();
308 
309  // Get the most recent sample to publish
310  int32_t result = this->read_value_(ADCO_B2_REG, 3);
311 
312  ESP_LOGD(TAG, "'%s': Got value %" PRId32, this->name_.c_str(), result);
313  this->publish_state(result);
314 }
315 
316 bool NAU7802Sensor::is_data_ready_() { return this->reg(PU_CTRL_REG).get() & PU_CTRL_CYCLE_READY; }
317 
318 } // namespace nau7802
319 } // 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:258
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:298
void status_set_error(const char *message="unspecified")
Definition: component.cpp:159
void status_clear_warning()
Definition: component.cpp:168
BedjetMode mode
BedJet operating mode.
Definition: bedjet_codec.h:183
bool calibrate_(enum NAU7802CalibrationModes mode)
Definition: nau7802.cpp:240
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:218
constexpr const char * c_str() const
Definition: string_ref.h:68
void status_clear_error()
Definition: component.cpp:174
std::string to_string(int value)
Definition: helpers.cpp:82
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:226
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26