nau7802: new component (#6291)

This commit is contained in:
Curtis Malainey 2024-10-06 20:08:56 -07:00 committed by GitHub
parent b2bf2bc448
commit ea23f49e90
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 628 additions and 0 deletions

View file

@ -283,6 +283,7 @@ esphome/components/mopeka_std_check/* @Fabian-Schmidt
esphome/components/mpl3115a2/* @kbickar
esphome/components/mpu6886/* @fabaff
esphome/components/ms8607/* @e28eta
esphome/components/nau7802/* @cujomalainey
esphome/components/network/* @esphome/core
esphome/components/nextion/* @edwardtfn @senexcrenshaw
esphome/components/nextion/binary_sensor/* @senexcrenshaw

View file

View file

@ -0,0 +1,323 @@
#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() {
i2c::I2CRegister pu_ctrl = this->reg(PU_CTRL_REG);
ESP_LOGCONFIG(TAG, "Setting up NAU7802 '%s'...", this->name_.c_str());
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;
this->setup_complete_ = true;
}
void NAU7802Sensor::dump_config() {
LOG_SENSOR("", "NAU7802", this);
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with NAU7802 failed earlier, during setup");
return;
}
// Note these may differ from the values on the device if calbration has been run
ESP_LOGCONFIG(TAG, " Offset Calibration: %s", to_string(this->offset_calibration_).c_str());
ESP_LOGCONFIG(TAG, " Gain Calibration: %f", 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; }
bool NAU7802Sensor::can_proceed() { return this->setup_complete_; }
} // namespace nau7802
} // namespace esphome

View file

@ -0,0 +1,121 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
#include <cinttypes>
namespace esphome {
namespace nau7802 {
enum NAU7802Gain {
NAU7802_GAIN_128 = 0b111,
NAU7802_GAIN_64 = 0b110,
NAU7802_GAIN_32 = 0b101,
NAU7802_GAIN_16 = 0b100,
NAU7802_GAIN_8 = 0b011,
NAU7802_GAIN_4 = 0b010,
NAU7802_GAIN_2 = 0b001,
NAU7802_GAIN_1 = 0b000,
};
enum NAU7802SPS {
NAU7802_SPS_320 = 0b111,
NAU7802_SPS_80 = 0b011,
NAU7802_SPS_40 = 0b010,
NAU7802_SPS_20 = 0b001,
NAU7802_SPS_10 = 0b000,
};
enum NAU7802LDO {
NAU7802_LDO_2V4 = 0b111,
NAU7802_LDO_2V7 = 0b110,
NAU7802_LDO_3V0 = 0b101,
NAU7802_LDO_3V3 = 0b100,
NAU7802_LDO_3V6 = 0b011,
NAU7802_LDO_3V9 = 0b010,
NAU7802_LDO_4V2 = 0b001,
NAU7802_LDO_4V5 = 0b000,
// Never write this to a register
NAU7802_LDO_EXTERNAL = 0b1000,
};
enum NAU7802CalibrationModes {
NAU7802_CALIBRATE_EXTERNAL_OFFSET = 0b10,
NAU7802_CALIBRATE_INTERNAL_OFFSET = 0b00,
NAU7802_CALIBRATE_GAIN = 0b11,
};
class NAU7802Sensor : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice {
public:
void set_samples_per_second(NAU7802SPS sps) { this->sps_ = sps; }
void set_ldo_voltage(NAU7802LDO ldo) { this->ldo_ = ldo; }
void set_gain(NAU7802Gain gain) { this->gain_ = gain; }
void set_gain_calibration(float gain_calibration) { this->gain_calibration_ = gain_calibration; }
void set_offset_calibration(int32_t offset_calibration) { this->offset_calibration_ = offset_calibration; }
bool calibrate_external_offset() { return this->calibrate_(NAU7802_CALIBRATE_EXTERNAL_OFFSET); }
bool calibrate_internal_offset() { return this->calibrate_(NAU7802_CALIBRATE_INTERNAL_OFFSET); }
bool calibrate_gain() { return this->calibrate_(NAU7802_CALIBRATE_GAIN); }
void setup() override;
void loop() override;
bool can_proceed() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
protected:
//
// Internal state
//
enum class CalibrationState : uint8_t {
INACTIVE,
OFFSET,
GAIN,
} state_{CalibrationState::INACTIVE};
float gain_calibration_;
int32_t offset_calibration_;
bool offset_calibration_failed_ = false;
bool gain_calibration_failed_ = false;
bool setup_complete_ = false;
//
// Config values
//
NAU7802LDO ldo_;
NAU7802SPS sps_;
NAU7802Gain gain_;
//
// Internal Methods
//
bool calibrate_(enum NAU7802CalibrationModes mode);
void complete_setup_();
void write_value_(uint8_t start_reg, size_t size, int32_t value);
int32_t read_value_(uint8_t start_reg, size_t size);
bool is_data_ready_();
void set_calibration_failure_(bool failed);
};
template<typename... Ts>
class NAU7802CalbrateExternalOffsetAction : public Action<Ts...>, public Parented<NAU7802Sensor> {
public:
void play(Ts... x) override { this->parent_->calibrate_external_offset(); }
};
template<typename... Ts>
class NAU7802CalbrateInternalOffsetAction : public Action<Ts...>, public Parented<NAU7802Sensor> {
public:
void play(Ts... x) override { this->parent_->calibrate_internal_offset(); }
};
template<typename... Ts> class NAU7802CalbrateGainAction : public Action<Ts...>, public Parented<NAU7802Sensor> {
public:
void play(Ts... x) override { this->parent_->calibrate_gain(); }
};
} // namespace nau7802
} // namespace esphome

View file

@ -0,0 +1,134 @@
from esphome import automation
from esphome.automation import maybe_simple_id
import esphome.codegen as cg
from esphome.components import i2c, sensor
import esphome.config_validation as cv
from esphome.const import CONF_GAIN, CONF_ID, ICON_SCALE, STATE_CLASS_MEASUREMENT
CODEOWNERS = ["@cujomalainey"]
DEPENDENCIES = ["i2c"]
CONF_GAIN_CALIBRATION = "gain_calibration"
CONF_OFFSET_CALIBRATION = "offset_calibration"
CONF_LDO_VOLTAGE = "ldo_voltage"
CONF_SAMPLES_PER_SECOND = "samples_per_second"
nau7802_ns = cg.esphome_ns.namespace("nau7802")
NAU7802Sensor = nau7802_ns.class_(
"NAU7802Sensor", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice
)
NAU7802CalbrateExternalOffsetAction = nau7802_ns.class_(
"NAU7802CalbrateExternalOffsetAction",
automation.Action,
cg.Parented.template(NAU7802Sensor),
)
NAU7802CalbrateInternalOffsetAction = nau7802_ns.class_(
"NAU7802CalbrateInternalOffsetAction",
automation.Action,
cg.Parented.template(NAU7802Sensor),
)
NAU7802CalbrateGainAction = nau7802_ns.class_(
"NAU7802CalbrateGainAction", automation.Action, cg.Parented.template(NAU7802Sensor)
)
NAU7802Gain = nau7802_ns.enum("NAU7802Gain")
GAINS = {
128: NAU7802Gain.NAU7802_GAIN_128,
64: NAU7802Gain.NAU7802_GAIN_64,
32: NAU7802Gain.NAU7802_GAIN_32,
16: NAU7802Gain.NAU7802_GAIN_16,
8: NAU7802Gain.NAU7802_GAIN_8,
4: NAU7802Gain.NAU7802_GAIN_4,
2: NAU7802Gain.NAU7802_GAIN_2,
1: NAU7802Gain.NAU7802_GAIN_1,
}
NAU7802SPS = nau7802_ns.enum("NAU7802SPS")
SAMPLES_PER_SECOND = {
320: NAU7802SPS.NAU7802_SPS_320,
80: NAU7802SPS.NAU7802_SPS_80,
40: NAU7802SPS.NAU7802_SPS_40,
20: NAU7802SPS.NAU7802_SPS_20,
10: NAU7802SPS.NAU7802_SPS_10,
}
NAU7802LDO = nau7802_ns.enum("NAU7802LDO")
LDO = {
"2.4V": NAU7802LDO.NAU7802_LDO_2V4,
"2.7V": NAU7802LDO.NAU7802_LDO_2V7,
"3.0V": NAU7802LDO.NAU7802_LDO_3V0,
"3.3V": NAU7802LDO.NAU7802_LDO_3V3,
"3.6V": NAU7802LDO.NAU7802_LDO_3V6,
"3.9V": NAU7802LDO.NAU7802_LDO_3V9,
"4.2V": NAU7802LDO.NAU7802_LDO_4V2,
"4.5V": NAU7802LDO.NAU7802_LDO_4V5,
"EXTERNAL": NAU7802LDO.NAU7802_LDO_EXTERNAL,
"EXT": NAU7802LDO.NAU7802_LDO_EXTERNAL,
}
CONFIG_SCHEMA = (
sensor.sensor_schema(
NAU7802Sensor,
icon=ICON_SCALE,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend(
{
cv.Optional(CONF_LDO_VOLTAGE, default="3.0V"): cv.enum(LDO, upper=True),
cv.Optional(CONF_SAMPLES_PER_SECOND, default=10): cv.enum(
SAMPLES_PER_SECOND, int=True
),
cv.Optional(CONF_GAIN, default=128): cv.enum(GAINS, int=True),
cv.Optional(CONF_OFFSET_CALIBRATION, default=0): cv.int_range(
min=-8388608, max=8388607
),
cv.Optional(CONF_GAIN_CALIBRATION, default=1.0): cv.float_range(
min=0, max=511.9999998807907
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x2A))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
await sensor.register_sensor(var, config)
cg.add(var.set_samples_per_second(config[CONF_SAMPLES_PER_SECOND]))
cg.add(var.set_ldo_voltage(config[CONF_LDO_VOLTAGE]))
cg.add(var.set_gain(config[CONF_GAIN]))
cg.add(var.set_gain_calibration(config[CONF_GAIN_CALIBRATION]))
cg.add(var.set_offset_calibration(config[CONF_OFFSET_CALIBRATION]))
NAU7802_CALIBRATE_SCHEMA = maybe_simple_id(
{
cv.GenerateID(CONF_ID): cv.use_id(NAU7802Sensor),
}
)
@automation.register_action(
"nau7802.calibrate_internal_offset",
NAU7802CalbrateInternalOffsetAction,
NAU7802_CALIBRATE_SCHEMA,
)
@automation.register_action(
"nau7802.calibrate_external_offset",
NAU7802CalbrateExternalOffsetAction,
NAU7802_CALIBRATE_SCHEMA,
)
@automation.register_action(
"nau7802.calibrate_gain",
NAU7802CalbrateGainAction,
NAU7802_CALIBRATE_SCHEMA,
)
async def nau7802_calibrate_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
return var

View file

@ -0,0 +1,13 @@
sensor:
- platform: nau7802
id: test_id
name: weight
i2c_id: i2c_nau7802
gain: 32
ldo_voltage: "3.0v"
samples_per_second: 10
on_value:
then:
- nau7802.calibrate_external_offset: test_id
- nau7802.calibrate_internal_offset: test_id
- nau7802.calibrate_gain: test_id

View file

@ -0,0 +1,6 @@
i2c:
- id: i2c_nau7802
scl: 16
sda: 17
<<: !include common.yaml

View file

@ -0,0 +1,6 @@
i2c:
- id: i2c_nau7802
scl: 5
sda: 4
<<: !include common.yaml

View file

@ -0,0 +1,6 @@
i2c:
- id: i2c_nau7802
scl: 5
sda: 4
<<: !include common.yaml

View file

@ -0,0 +1,6 @@
i2c:
- id: i2c_nau7802
scl: 16
sda: 17
<<: !include common.yaml

View file

@ -0,0 +1,6 @@
i2c:
- id: i2c_nau7802
scl: 5
sda: 4
<<: !include common.yaml

View file

@ -0,0 +1,6 @@
i2c:
- id: i2c_nau7802
scl: 5
sda: 4
<<: !include common.yaml