cogito/esphome_dev/esphome/components/nau7802/nau7802.cpp

321 lines
8.6 KiB
C++

#include "nau7802.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace nau7802 {
static const char *const TAG = "nau7802";
// Only define what we need
static const uint8_t READ_BIT = 0x01;
static const uint8_t PU_CTRL_REG = 0x00;
static const uint8_t PU_CTRL_REGISTER_RESET = 0x01;
static const uint8_t PU_CTRL_POWERUP_DIGITAL = 0x02;
static const uint8_t PU_CTRL_POWERUP_ANALOG = 0x04;
static const uint8_t PU_CTRL_POWERUP_READY = 0x08;
static const uint8_t PU_CTRL_CYCLE_START = 0x10;
static const uint8_t PU_CTRL_CYCLE_READY = 0x20;
static const uint8_t PU_CTRL_AVDD_EXTERNAL = 0x80;
static const uint8_t CTRL1_REG = 0x01;
static const uint8_t CTRL1_LDO_SHIFT = 3;
static const uint8_t CTRL1_LDO_MASK = (0x7 << CTRL1_LDO_SHIFT);
static const uint8_t CTRL1_GAIN_MASK = 0x7;
static const uint8_t CTRL2_REG = 0x02;
static const uint8_t CTRL2_CRS_SHIFT = 4;
static const uint8_t CTRL2_CRS_MASK = (0x7 << CTRL2_CRS_SHIFT);
static const uint8_t CTRL2_CALS = 0x04;
static const uint8_t CTRL2_CAL_ERR = 0x08;
static const uint8_t CTRL2_GAIN_CALIBRATION = 0x03;
static const uint8_t CTRL2_CONFIG_MASK = 0xF0;
static const uint8_t OCAL1_B2_REG = 0x03;
static const uint8_t GCAL1_B3_REG = 0x06;
static const uint8_t GCAL1_FRACTIONAL = 23;
// only need the first data register for sequential read method
static const uint8_t ADCO_B2_REG = 0x12;
static const uint8_t ADC_REG = 0x15;
static const uint8_t ADC_CHPS_DISABLE = 0x30;
static const uint8_t PGA_REG = 0x1B;
static const uint8_t PGA_LDOMODE_ESR = 0x40;
static const uint8_t POWER_REG = 0x1C;
static const uint8_t POWER_PGA_CAP_EN = 0x80;
static const uint8_t DEVICE_REV = 0x1F;
void NAU7802Sensor::setup() {
ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->name_.c_str());
i2c::I2CRegister pu_ctrl = this->reg(PU_CTRL_REG);
uint8_t rev;
if (this->read_register(DEVICE_REV | READ_BIT, &rev, 1)) {
ESP_LOGE(TAG, "Failed I2C read during setup()");
this->mark_failed();
return;
}
ESP_LOGI(TAG, "Setting up NAU7802 Rev %d", rev);
// reset
pu_ctrl |= PU_CTRL_REGISTER_RESET;
delay(10);
pu_ctrl &= ~PU_CTRL_REGISTER_RESET;
// power up digital hw
pu_ctrl |= PU_CTRL_POWERUP_DIGITAL;
delay(1);
if (!(pu_ctrl.get() & PU_CTRL_POWERUP_READY)) {
ESP_LOGE(TAG, "Failed to reset sensor during setup()");
this->mark_failed();
return;
}
uint32_t gcal = (uint32_t) (round(this->gain_calibration_ * (1 << GCAL1_FRACTIONAL)));
this->write_value_(OCAL1_B2_REG, 3, this->offset_calibration_);
this->write_value_(GCAL1_B3_REG, 4, gcal);
// turn on AFE
pu_ctrl |= PU_CTRL_POWERUP_ANALOG;
auto f = std::bind(&NAU7802Sensor::complete_setup_, this);
this->set_timeout(600, f);
}
void NAU7802Sensor::complete_setup_() {
i2c::I2CRegister pu_ctrl = this->reg(PU_CTRL_REG);
i2c::I2CRegister ctrl1 = this->reg(CTRL1_REG);
i2c::I2CRegister ctrl2 = this->reg(CTRL2_REG);
pu_ctrl |= PU_CTRL_CYCLE_START;
// set gain
ctrl1 &= ~CTRL1_GAIN_MASK;
ctrl1 |= this->gain_;
// enable internal LDO
if (this->ldo_ != NAU7802_LDO_EXTERNAL) {
pu_ctrl |= PU_CTRL_AVDD_EXTERNAL;
ctrl1 &= ~CTRL1_LDO_MASK;
ctrl1 |= this->ldo_ << CTRL1_LDO_SHIFT;
}
// set sps
ctrl2 &= ~CTRL2_CRS_MASK;
ctrl2 |= this->sps_ << CTRL2_CRS_SHIFT;
// disable ADC chopper clock
i2c::I2CRegister adc_reg = this->reg(ADC_REG);
adc_reg |= ADC_CHPS_DISABLE;
// use low ESR caps
i2c::I2CRegister pga_reg = this->reg(PGA_REG);
pga_reg &= ~PGA_LDOMODE_ESR;
// PGA stabilizer cap on output
i2c::I2CRegister pwr_reg = this->reg(POWER_REG);
pwr_reg |= POWER_PGA_CAP_EN;
}
void NAU7802Sensor::dump_config() {
LOG_SENSOR("", "NAU7802", this);
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL_FOR, this->get_name().c_str());
return;
}
// Note these may differ from the values on the device if calbration has been run
ESP_LOGCONFIG(TAG,
" Offset Calibration: %s\n"
" Gain Calibration: %f",
to_string(this->offset_calibration_).c_str(), this->gain_calibration_);
std::string voltage = "unknown";
switch (this->ldo_) {
case NAU7802_LDO_2V4:
voltage = "2.4V";
break;
case NAU7802_LDO_2V7:
voltage = "2.7V";
break;
case NAU7802_LDO_3V0:
voltage = "3.0V";
break;
case NAU7802_LDO_3V3:
voltage = "3.3V";
break;
case NAU7802_LDO_3V6:
voltage = "3.6V";
break;
case NAU7802_LDO_3V9:
voltage = "3.9V";
break;
case NAU7802_LDO_4V2:
voltage = "4.2V";
break;
case NAU7802_LDO_4V5:
voltage = "4.5V";
break;
case NAU7802_LDO_EXTERNAL:
voltage = "External";
break;
}
ESP_LOGCONFIG(TAG, " LDO Voltage: %s", voltage.c_str());
int gain = 0;
switch (this->gain_) {
case NAU7802_GAIN_128:
gain = 128;
break;
case NAU7802_GAIN_64:
gain = 64;
break;
case NAU7802_GAIN_32:
gain = 32;
break;
case NAU7802_GAIN_16:
gain = 16;
break;
case NAU7802_GAIN_8:
gain = 8;
break;
case NAU7802_GAIN_4:
gain = 4;
break;
case NAU7802_GAIN_2:
gain = 2;
break;
case NAU7802_GAIN_1:
gain = 1;
break;
}
ESP_LOGCONFIG(TAG, " Gain: %dx", gain);
int sps = 0;
switch (this->sps_) {
case NAU7802_SPS_320:
sps = 320;
break;
case NAU7802_SPS_80:
sps = 80;
break;
case NAU7802_SPS_40:
sps = 40;
break;
case NAU7802_SPS_20:
sps = 20;
break;
case NAU7802_SPS_10:
sps = 10;
break;
}
ESP_LOGCONFIG(TAG, " Samples Per Second: %d", sps);
LOG_UPDATE_INTERVAL(this);
}
void NAU7802Sensor::write_value_(uint8_t start_reg, size_t size, int32_t value) {
uint8_t data[4];
for (int i = 0; i < size; i++) {
data[i] = 0xFF & (value >> (size - 1 - i) * 8);
}
this->write_register(start_reg, data, size);
}
int32_t NAU7802Sensor::read_value_(uint8_t start_reg, size_t size) {
uint8_t data[4];
this->read_register(start_reg, data, size);
int32_t result = 0;
for (int i = 0; i < size; i++) {
result |= data[i] << (size - 1 - i) * 8;
}
// extend sign bit
if (result & 0x800000 && size == 3) {
result |= 0xFF000000;
}
return result;
}
bool NAU7802Sensor::calibrate_(enum NAU7802CalibrationModes mode) {
// check if already calbrating
if (this->state_ != CalibrationState::INACTIVE) {
ESP_LOGW(TAG, "Calibration already in progress");
return false;
}
this->state_ = mode == NAU7802_CALIBRATE_GAIN ? CalibrationState::GAIN : CalibrationState::OFFSET;
i2c::I2CRegister ctrl2 = this->reg(CTRL2_REG);
// clear calibraye registers
ctrl2 &= CTRL2_CONFIG_MASK;
// Calibrate
ctrl2 |= mode;
ctrl2 |= CTRL2_CALS;
return true;
}
void NAU7802Sensor::set_calibration_failure_(bool failed) {
switch (this->state_) {
case CalibrationState::GAIN:
this->gain_calibration_failed_ = failed;
break;
case CalibrationState::OFFSET:
this->offset_calibration_failed_ = failed;
break;
case CalibrationState::INACTIVE:
// shouldn't happen
break;
}
}
void NAU7802Sensor::loop() {
i2c::I2CRegister ctrl2 = this->reg(CTRL2_REG);
if (this->state_ != CalibrationState::INACTIVE && !(ctrl2.get() & CTRL2_CALS)) {
if (ctrl2.get() & CTRL2_CAL_ERR) {
this->set_calibration_failure_(true);
this->state_ = CalibrationState::INACTIVE;
ESP_LOGE(TAG, "Failed to calibrate sensor");
this->status_set_error("Calibration Failed");
return;
}
this->set_calibration_failure_(false);
this->state_ = CalibrationState::INACTIVE;
if (!this->offset_calibration_failed_ && !this->gain_calibration_failed_)
this->status_clear_error();
int32_t ocal = this->read_value_(OCAL1_B2_REG, 3);
ESP_LOGI(TAG, "New Offset: %s", to_string(ocal).c_str());
uint32_t gcal = this->read_value_(GCAL1_B3_REG, 4);
float gcal_f = ((float) gcal / (float) (1 << GCAL1_FRACTIONAL));
ESP_LOGI(TAG, "New Gain: %f", gcal_f);
}
}
float NAU7802Sensor::get_setup_priority() const { return setup_priority::DATA; }
void NAU7802Sensor::update() {
if (!this->is_data_ready_()) {
ESP_LOGW(TAG, "No measurements ready!");
this->status_set_warning();
return;
}
this->status_clear_warning();
// Get the most recent sample to publish
int32_t result = this->read_value_(ADCO_B2_REG, 3);
ESP_LOGD(TAG, "'%s': Got value %" PRId32, this->name_.c_str(), result);
this->publish_state(result);
}
bool NAU7802Sensor::is_data_ready_() { return this->reg(PU_CTRL_REG).get() & PU_CTRL_CYCLE_READY; }
} // namespace nau7802
} // namespace esphome