trying out polling ads131m08 sensor

This commit is contained in:
Chris Stuurman 2026-05-07 22:37:38 +02:00
parent 799407c6be
commit de74905436
6 changed files with 176 additions and 51 deletions

View File

@ -312,6 +312,15 @@ void ADS131M08Hub::isr_handler(ADS131M08Hub *arg) {
} }
} }
float ADS131M08Hub::get_sample(int32_t& raw_value, uint8_t channel, uint8_t gain, ADC_INPUT_CHANNEL_MUX input, uint32_t phase_calibration, uint32_t offset_calibration, uint32_t gain_calibration, bool dc_block)
{
if(channel < ADC_CHANNELS) {
raw_value = raw_sampled_values_[channel];
return sampled_values_[channel];
}
return NAN;
}
void ADS131M08Hub::loop() void ADS131M08Hub::loop()
{ {
// Check the semaphore (0 timeout means non-blocking) // Check the semaphore (0 timeout means non-blocking)
@ -335,7 +344,7 @@ void ADS131M08Hub::loop()
// } // }
// ESP_LOGD(TAG, "spi_freq: %u", freq); // ESP_LOGD(TAG, "spi_freq: %u", freq);
//} //}
if (rms_calc_req_) { if (false /*rms_calc_req_*/) {
float_str result = read_multi(); float_str result = read_multi();
num_samples = result[0]; num_samples = result[0];
crc_errors = result[1]; crc_errors = result[1];
@ -375,13 +384,29 @@ void ADS131M08Hub::loop()
} }
if (crc_errors > 30) { if (crc_errors > 30) {
ESP_LOGW(TAG, "High CRC error rate."); ESP_LOGW(TAG, "High CRC error rate.");
int i = 4; if (!this->adc_initialize(DEFAULT_WORD_LENGTH)) {
while(i-- > 0 && !adc_set_word_length(DEFAULT_WORD_LENGTH)); // for some reason the adc occasionally reverts to 24bits; this will ESP_LOGE(TAG, "ADC reset failed!");
// reset the word length to what it should be this->mark_failed(LOG_STR("Initialisation failed."));
// update_adc_word_length(); this->adc_init_ = 0;
// spiframe recoverframe(40, 0); return;
// read_array(recoverframe); }
// ESP_LOGI(TAG, "WL: %u Frame: %s", this->adc_word_length_, frame_to_string(recoverframe).c_str()); set_reg_osr();
if (!adc_lock(true)) {
ESP_LOGE(TAG, "ADC lock failed!");
this->mark_failed(LOG_STR("ADC lock failed."));
this->adc_init_ = 0;
return;
}
if (!adc_lock(false)) {
ESP_LOGE(TAG, "ADC unlock failed!");
this->mark_failed(LOG_STR("ADC unlock failed."));
this->adc_init_ = 0;
return;
}
//int i = 4;
//while(i-- > 0 && !adc_set_word_length(DEFAULT_WORD_LENGTH)); // for some reason the adc occasionally reverts to 24bits; this will
// // reset the word length to what it should be
} }
// ESP_LOGW(TAG, "%llu ms (%llu us), max samples: %u", (end - start)/1000, (end - start), num_samples); // ESP_LOGW(TAG, "%llu ms (%llu us), max samples: %u", (end - start)/1000, (end - start), num_samples);
} }
@ -629,7 +654,7 @@ bool ADS131M08Hub::adc_soft_reset() {
for (; index < framelength; index++) { for (; index < framelength; index++) {
rx_frame[index] = 0; rx_frame[index] = 0;
} }
ESP_LOGVV(TAG, "Sent Frame: %s", frame_to_string(rx_frame).c_str()); ESP_LOGW(TAG, "Sent Frame: %s", frame_to_string(rx_frame).c_str());
transfer_array(rx_frame); transfer_array(rx_frame);
} }
delay_microseconds_safe(T_REGACQ); delay_microseconds_safe(T_REGACQ);
@ -723,6 +748,11 @@ uint16_t ADS131M08Hub::crc(uint16_t crc_register, uint8_t data) {
void ADS131M08Hub::transfer_array(spiframe &frame) { void ADS131M08Hub::transfer_array(spiframe &frame) {
CHIP_SELECT CHIP_SELECT
//uint16_t cmd = get_unsigned_frame_word(rx_frame, 0, true);
//if(cmd == CMD_RESET) {
// ESP_LOGW(TAG, "Reset cmd sent : %s", command_to_string(cmd).c_str());
// ESP_LOGW(TAG, "reset cmd frame:%s", frame_to_string(rx_frame).c_str());
//}
Base::transfer_array(frame.data(), (size_t)frame.size()); Base::transfer_array(frame.data(), (size_t)frame.size());
} }
@ -759,14 +789,17 @@ void ADS131M08Hub::read_single() {
int32_t raw = get_sign_ext_frame_word(rx_frame, i + 1); int32_t raw = get_sign_ext_frame_word(rx_frame, i + 1);
float value = this->conversion_factor_ * raw; float value = this->conversion_factor_ * raw;
this->sampled_values_[i] = value; this->sampled_values_[i] = value;
bool ac_sensor_exist = this->sensors_ac[i] != nullptr; this->raw_sampled_values_[i] = raw;
bool dc_sensor_exist = this->sensors_dc[i] != nullptr; //bool ac_sensor_exist = this->sensors_ac[i] != nullptr;
if (ac_sensor_exist || dc_sensor_exist) { //bool dc_sensor_exist = this->sensors_dc[i] != nullptr;
if (ac_sensor_exist) //if (ac_sensor_exist || dc_sensor_exist) {
this->sensors_ac[i]->publish_state(value); // if (ac_sensor_exist) {
if (dc_sensor_exist) // this->sensors_ac[i]->publish_state(value);
this->sensors_dc[i]->publish_state(value); // }
} // if (dc_sensor_exist) {
// this->sensors_dc[i]->publish_state(value);
// }
//}
} }
} }
} }
@ -810,13 +843,36 @@ float_str ADS131M08Hub::read_multi() {
//ESP_LOGD(TAG, "frame_word%d: %s", 0, frame_words_to_string(rx_frame, 0, 1).c_str()); //ESP_LOGD(TAG, "frame_word%d: %s", 0, frame_words_to_string(rx_frame, 0, 1).c_str());
status = get_unsigned_frame_word(rx_frame, 0, true); status = get_unsigned_frame_word(rx_frame, 0, true);
update_adc_word_length(status); update_adc_word_length(status);
if(status & MASK_STATUS_RESET) { if(status == CMD_RESET) {
ESP_LOGI(TAG,"Reset frame: %s", frame_to_string(rx_frame).c_str()); ESP_LOGI(TAG,"Reset frame: %s", frame_to_string(rx_frame).c_str());
ESP_LOGW(TAG, "ADC reset detected. Trying recover"); ESP_LOGW(TAG, "ADC reset detected. Trying recover");
if (!adc_register_write(REG_MODE, MODE_MASK_RESET_HAPPENED & reg_mode_cfg32, __LINE__)) { if (!adc_register_write(REG_MODE, MODE_MASK_RESET_HAPPENED & reg_mode_cfg32, __LINE__)) {
ESP_LOGE(TAG, "MODE register write / read to set word size to 32bits failed"); ESP_LOGE(TAG, "MODE register write / read to set word size to 32bits failed");
return result; return result;
} }
if (!this->adc_initialize(DEFAULT_WORD_LENGTH)) {
ESP_LOGE(TAG, "ADC reset failed!");
this->mark_failed(LOG_STR("Initialisation failed."));
this->adc_init_ = 0;
return result;
}
set_reg_osr();
if (!adc_lock(true)) {
ESP_LOGE(TAG, "ADC lock failed!");
this->mark_failed(LOG_STR("ADC lock failed."));
this->adc_init_ = 0;
return result;
}
if (!adc_lock(false)) {
ESP_LOGE(TAG, "ADC unlock failed!");
this->mark_failed(LOG_STR("ADC unlock failed."));
this->adc_init_ = 0;
return result;
}
this->set_timeout("reset detected", 250, [this, result]() { this->set_timeout("reset detected", 250, [this, result]() {
ESP_LOGE(TAG, "ADC reset detected. Trying to restore registers"); ESP_LOGE(TAG, "ADC reset detected. Trying to restore registers");
if(this->adc_restore_registers()) { if(this->adc_restore_registers()) {
@ -1147,7 +1203,7 @@ bool ADS131M08Hub::adc_register_write(uint16_t start_address, const uint16_str &
set_frame_word(rx_frame, i + 1, data[i]); set_frame_word(rx_frame, i + 1, data[i]);
} }
add_crc(rx_frame); add_crc(rx_frame);
ESP_LOGD(TAG, "%s\n", rwreg_command_frame_to_string(rx_frame).c_str()); //ESP_LOGD(TAG, "%s\n", rwreg_command_frame_to_string(rx_frame).c_str());
//ESP_LOGD(TAG, "Send Frame: %s", frame_to_string(rx_frame).c_str()); //ESP_LOGD(TAG, "Send Frame: %s", frame_to_string(rx_frame).c_str());
transfer_array(rx_frame); // WREG transfer_array(rx_frame); // WREG
} }

View File

@ -122,6 +122,8 @@ class ADS131M08Hub : public Component,
bool set_channel_gain(uint8_t channel, uint8_t gain); bool set_channel_gain(uint8_t channel, uint8_t gain);
bool set_measure_rms(uint8_t channel, bool enable); bool set_measure_rms(uint8_t channel, bool enable);
float get_sampled_value(uint8_t channel) { return sampled_values_[channel]; } float get_sampled_value(uint8_t channel) { return sampled_values_[channel]; }
float get_sample(int32_t& raw_value, uint8_t channel_, uint8_t gain_, ADC_INPUT_CHANNEL_MUX input,
uint32_t phase_calibration, uint32_t offset_calibration, uint32_t gain_calibration, bool dc_block);
uint32_t get_filter_settling_time(); uint32_t get_filter_settling_time();
//float get_average(uint8_t channel, bool read_ac); //float get_average(uint8_t channel, bool read_ac);
bool set_reg_osr(); bool set_reg_osr();
@ -138,6 +140,7 @@ class ADS131M08Hub : public Component,
InternalGPIOPin *sync_reset_pin_ = {nullptr}; InternalGPIOPin *sync_reset_pin_ = {nullptr};
sensor::Sensor *sensors_ac[MAX_CHANNELS] = {nullptr}; sensor::Sensor *sensors_ac[MAX_CHANNELS] = {nullptr};
sensor::Sensor *sensors_dc[MAX_CHANNELS] = {nullptr}; sensor::Sensor *sensors_dc[MAX_CHANNELS] = {nullptr};
uint32_t raw_sampled_values_[ADC_CHANNELS];
float sampled_values_[ADC_CHANNELS]; float sampled_values_[ADC_CHANNELS];
bool rms_enabled_[MAX_CHANNELS]; bool rms_enabled_[MAX_CHANNELS];
bool rms_calc_req_{false}; bool rms_calc_req_{false};

View File

@ -1,7 +1,17 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor, voltage_sampler from esphome.components import sensor, voltage_sampler
from esphome.const import CONF_ID, CONF_GAIN, CONF_CHANNEL from esphome.const import (
CONF_ID,
CONF_GAIN,
CONF_CHANNEL,
CONF_NAME,
DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT,
ICON_CURRENT_AC,
UNIT_VOLT,
)
from .. import CONF_ADS131M08_ID, ADS131M08Hub, ads131m08_ns from .. import CONF_ADS131M08_ID, ADS131M08Hub, ads131m08_ns
from ..sensor_rms import CONFIG_SCHEMA_RMS_SENSORS, to_code_ac, to_code_dc from ..sensor_rms import CONFIG_SCHEMA_RMS_SENSORS, to_code_ac, to_code_dc
@ -52,22 +62,51 @@ Channel = ads131m08_ns.class_(
"Channel", sensor.Sensor, cg.Component, voltage_sampler.VoltageSampler "Channel", sensor.Sensor, cg.Component, voltage_sampler.VoltageSampler
) )
CONFIG_SCHEMA = CONFIG_SCHEMA_RMS_SENSORS.extend( #CONFIG_SCHEMA = CONFIG_SCHEMA_RMS_SENSORS.extend(
{ # {
cv.GenerateID(CONF_ADS131M08_ID): cv.use_id(ADS131M08Hub), # cv.GenerateID(CONF_ADS131M08_ID): cv.use_id(ADS131M08Hub),
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=MAX_CHANNELS), # cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=MAX_CHANNELS),
cv.Optional(CONF_GAIN, default=1): cv.enum(ALLOWED_GAINS, int=True), # cv.Optional(CONF_GAIN, default=1): cv.enum(ALLOWED_GAINS, int=True),
cv.Optional(CONF_OFFSET_CALIBRATION, default=0): cv.int_range(min=-8388608, max=8388607), # should use volts, but need to figure out conversion function first # cv.Optional(CONF_OFFSET_CALIBRATION, default=0): cv.int_range(min=-8388608, max=8388607), # should use volts, but need to figure out conversion function first
cv.Optional(CONF_GAIN_CALIBRATION, default=1): cv.float_range(min=0, max=2), # cv.Optional(CONF_GAIN_CALIBRATION, default=1): cv.float_range(min=0, max=2),
cv.Optional(CONF_PHASE_CALIBRATION, default=0): cv.int_range(min=-512, max=511), # should use degrees, but need to figure out conversion function first # cv.Optional(CONF_PHASE_CALIBRATION, default=0): cv.int_range(min=-512, max=511), # should use degrees, but need to figure out conversion function first
cv.Optional(CONF_INPUT_SELECT, default='normal'): cv.enum(ALLOWED_MUX_INP, int=False), # cv.Optional(CONF_INPUT_SELECT, default='normal'): cv.enum(ALLOWED_MUX_INP, int=False),
cv.Optional(CONF_DC_BLOCK, default=False): cv.boolean, # cv.Optional(CONF_DC_BLOCK, default=False): cv.boolean,
} # cv.Optional(CONF_NAME): cv.string,
).extend( # }
{ #).extend(
cv.GenerateID(): cv.declare_id(Channel) # {
} # cv.GenerateID(): cv.declare_id(Channel)
).extend(cv.COMPONENT_SCHEMA) # }
#).extend(cv.polling_component_schema("2s"))
CONFIG_SCHEMA = (
sensor.sensor_schema(
Channel,
accuracy_decimals=6,
icon=ICON_CURRENT_DC,
unit_of_measurement=UNIT_VOLT,
device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT,
#).extend(
# CONFIG_SCHEMA_RMS_SENSORS
)
.extend(
{
cv.GenerateID(CONF_ADS131M08_ID): cv.use_id(ADS131M08Hub),
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=MAX_CHANNELS),
cv.Optional(CONF_GAIN, default=1): cv.enum(ALLOWED_GAINS, int=True),
cv.Optional(CONF_OFFSET_CALIBRATION, default=0): cv.int_range(min=-8388608, max=8388607), # should use volts, but need to figure out conversion function first
cv.Optional(CONF_GAIN_CALIBRATION, default=1): cv.float_range(min=0, max=2),
cv.Optional(CONF_PHASE_CALIBRATION, default=0): cv.int_range(min=-512, max=511), # should use degrees, but need to figure out conversion function first
cv.Optional(CONF_INPUT_SELECT, default='normal'): cv.enum(ALLOWED_MUX_INP, int=False),
cv.Optional(CONF_DC_BLOCK, default=False): cv.boolean,
#cv.Required(CONF_NAME): cv.string,
}
)
.extend(cv.polling_component_schema("1s"))
)
# we are using 3 sensors: # we are using 3 sensors:
# 1. channel_sensor: this represents 1 of the 8 ads131m08 channels and is used to program the adc channel gain, offset calibration, etc. This sensor publishes instantaneous sampled value # 1. channel_sensor: this represents 1 of the 8 ads131m08 channels and is used to program the adc channel gain, offset calibration, etc. This sensor publishes instantaneous sampled value
# 2. dc_sensor: to publish averaged dc value # 2. dc_sensor: to publish averaged dc value
@ -89,8 +128,10 @@ async def to_code(config):
cg.add(channel_sensor.set_phase_calibration(phase_cal)) cg.add(channel_sensor.set_phase_calibration(phase_cal))
dc_block = config[CONF_DC_BLOCK] dc_block = config[CONF_DC_BLOCK]
cg.add(channel_sensor.set_dc_block(dc_block)) cg.add(channel_sensor.set_dc_block(dc_block))
await sensor.register_sensor(channel_sensor, config)
await cg.register_component(channel_sensor, config) await cg.register_component(channel_sensor, config)
if dc_sensor := await to_code_dc(config): #if dc_sensor := await to_code_dc(config):
await cg.register_parented(dc_sensor, channel_sensor) # await cg.register_parented(dc_sensor, channel_sensor)
if ac_sensor := await to_code_ac(config): #if ac_sensor := await to_code_ac(config):
await cg.register_parented(ac_sensor, channel_sensor) # await cg.register_parented(ac_sensor, channel_sensor)

View File

@ -25,7 +25,28 @@ void Channel::loop()
float Channel::sample() float Channel::sample()
{ {
return (this->parent_ == nullptr) ? NAN : this->parent_->get_sampled_value(this->channel_); int32_t raw_value = -1000;
return sample(raw_value);
}
float Channel::sample(int32_t& raw_value)
{
return (this->parent_ == nullptr) ? NAN : this->parent_->get_sample(raw_value, this->channel_, this->gain_, this->input_,
this->normalised_phase_calibration(), this->normalised_offset_calibration(), this->normalised_gain_calibration(), !this->dc_block_
);
}
void Channel::update() {
int32_t raw_value = -1000;
float v = this->sample(raw_value);
if (!std::isnan(v)) {
// if(this->count_ % 10 == 0) {
// ESP_LOGI(TAG, "Ch%d: Raw=% 6d %fV", this->channel_, raw_value, v);
// }
this->count_++;
ESP_LOGD(TAG, "Ch%d: Got Voltage=%fV", this->channel_, v);
this->publish_state(v);
}
} }
// converts gain of 0.0 - 2.0 to the corresponding values that ADS131M08 expects // converts gain of 0.0 - 2.0 to the corresponding values that ADS131M08 expects

View File

@ -13,12 +13,13 @@ namespace esphome {
namespace ads131m08 { namespace ads131m08 {
class Channel : public sensor::Sensor, class Channel : public sensor::Sensor,
public Component, public PollingComponent,
public voltage_sampler::VoltageSampler, public voltage_sampler::VoltageSampler,
public Parented<ADS131M08Hub> { public Parented<ADS131M08Hub> {
public: public:
void loop() override; void loop() override;
float sample() override; float sample() override;
void update() override;
void set_gain(uint8_t value) { this->gain_ = value; } void set_gain(uint8_t value) { this->gain_ = value; }
void dump_config() override; void dump_config() override;
void set_channel_number(uint8_t channel) { this->channel_= channel; } void set_channel_number(uint8_t channel) { this->channel_= channel; }
@ -32,6 +33,7 @@ class Channel : public sensor::Sensor,
uint32_t normalised_gain_calibration() const; uint32_t normalised_gain_calibration() const;
uint32_t normalised_offset_calibration() const; uint32_t normalised_offset_calibration() const;
int16_t normalised_phase_calibration() const; int16_t normalised_phase_calibration() const;
float sample(int32_t& raw_value);
protected: protected:
uint8_t channel_; uint8_t channel_;
@ -43,7 +45,7 @@ class Channel : public sensor::Sensor,
uint8_t gain_; uint8_t gain_;
ADC_INPUT_CHANNEL_MUX input_; ADC_INPUT_CHANNEL_MUX input_;
bool first_reading_{true}; bool first_reading_{true};
uint64_t count_{0};
}; };
class RMS_Sensor : public sensor::Sensor, class RMS_Sensor : public sensor::Sensor,

View File

@ -18,7 +18,7 @@ AUTO_LOAD = ["sensor",]
DEPENDENCIES = ["ads131m08"] DEPENDENCIES = ["ads131m08"]
ads131m08_acdc_ns = cg.esphome_ns.namespace("ads131m08_acdc") #ads131m08_acdc_ns = cg.esphome_ns.namespace("ads131m08_acdc")
RMS_SENSOR = ads131m08_ns.class_( RMS_SENSOR = ads131m08_ns.class_(
"RMS_Sensor", sensor.Sensor, cg.Component "RMS_Sensor", sensor.Sensor, cg.Component
@ -26,7 +26,7 @@ RMS_SENSOR = ads131m08_ns.class_(
CONFIG_SCHEMA_RMS_SENSORS = cv.Schema( CONFIG_SCHEMA_RMS_SENSORS = cv.Schema(
{ {
cv.Required(CONF_DC): sensor.sensor_schema( cv.Optional(CONF_DC): sensor.sensor_schema(
RMS_SENSOR, RMS_SENSOR,
accuracy_decimals=6, accuracy_decimals=6,
icon=ICON_CURRENT_DC, icon=ICON_CURRENT_DC,
@ -52,6 +52,7 @@ CONFIG_SCHEMA_RMS_SENSORS = cv.Schema(
), ),
} }
). extend (cv.COMPONENT_SCHEMA) ). extend (cv.COMPONENT_SCHEMA)
async def to_code_ac(config): async def to_code_ac(config):
hub = await cg.get_variable(config[CONF_ADS131M08_ID]) hub = await cg.get_variable(config[CONF_ADS131M08_ID])
channel = config[CONF_CHANNEL] channel = config[CONF_CHANNEL]
@ -66,9 +67,10 @@ async def to_code_ac(config):
async def to_code_dc(config): async def to_code_dc(config):
hub = await cg.get_variable(config[CONF_ADS131M08_ID]) hub = await cg.get_variable(config[CONF_ADS131M08_ID])
channel = config[CONF_CHANNEL] channel = config[CONF_CHANNEL]
dc_config = config.get(CONF_DC) if dc_config := config.get(CONF_DC):
sens_dc = await sensor.new_sensor(dc_config) sens_dc = await sensor.new_sensor(dc_config)
cg.add(sens_dc, config.get(CONF_DC)) cg.add(sens_dc, config.get(CONF_DC))
cg.add(hub.register_sensor_dc(channel, sens_dc)) cg.add(hub.register_sensor_dc(channel, sens_dc))
await cg.register_component(sens_dc, config[CONF_DC]) await cg.register_component(sens_dc, config[CONF_DC])
return sens_dc return sens_dc
return None