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()
{
// Check the semaphore (0 timeout means non-blocking)
@ -335,7 +344,7 @@ void ADS131M08Hub::loop()
// }
// ESP_LOGD(TAG, "spi_freq: %u", freq);
//}
if (rms_calc_req_) {
if (false /*rms_calc_req_*/) {
float_str result = read_multi();
num_samples = result[0];
crc_errors = result[1];
@ -375,13 +384,29 @@ void ADS131M08Hub::loop()
}
if (crc_errors > 30) {
ESP_LOGW(TAG, "High CRC error rate.");
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
// update_adc_word_length();
// spiframe recoverframe(40, 0);
// read_array(recoverframe);
// ESP_LOGI(TAG, "WL: %u Frame: %s", this->adc_word_length_, frame_to_string(recoverframe).c_str());
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;
}
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);
}
@ -629,7 +654,7 @@ bool ADS131M08Hub::adc_soft_reset() {
for (; index < framelength; index++) {
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);
}
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) {
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());
}
@ -759,14 +789,17 @@ void ADS131M08Hub::read_single() {
int32_t raw = get_sign_ext_frame_word(rx_frame, i + 1);
float value = this->conversion_factor_ * raw;
this->sampled_values_[i] = value;
bool ac_sensor_exist = this->sensors_ac[i] != nullptr;
bool dc_sensor_exist = this->sensors_dc[i] != nullptr;
if (ac_sensor_exist || dc_sensor_exist) {
if (ac_sensor_exist)
this->sensors_ac[i]->publish_state(value);
if (dc_sensor_exist)
this->sensors_dc[i]->publish_state(value);
}
this->raw_sampled_values_[i] = raw;
//bool ac_sensor_exist = this->sensors_ac[i] != nullptr;
//bool dc_sensor_exist = this->sensors_dc[i] != nullptr;
//if (ac_sensor_exist || dc_sensor_exist) {
// if (ac_sensor_exist) {
// this->sensors_ac[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());
status = get_unsigned_frame_word(rx_frame, 0, true);
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_LOGW(TAG, "ADC reset detected. Trying recover");
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");
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]() {
ESP_LOGE(TAG, "ADC reset detected. Trying to 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]);
}
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());
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_measure_rms(uint8_t channel, bool enable);
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();
//float get_average(uint8_t channel, bool read_ac);
bool set_reg_osr();
@ -138,6 +140,7 @@ class ADS131M08Hub : public Component,
InternalGPIOPin *sync_reset_pin_ = {nullptr};
sensor::Sensor *sensors_ac[MAX_CHANNELS] = {nullptr};
sensor::Sensor *sensors_dc[MAX_CHANNELS] = {nullptr};
uint32_t raw_sampled_values_[ADC_CHANNELS];
float sampled_values_[ADC_CHANNELS];
bool rms_enabled_[MAX_CHANNELS];
bool rms_calc_req_{false};

View File

@ -1,7 +1,17 @@
import esphome.codegen as cg
import esphome.config_validation as cv
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 ..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
)
CONFIG_SCHEMA = 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,
}
).extend(
{
cv.GenerateID(): cv.declare_id(Channel)
}
).extend(cv.COMPONENT_SCHEMA)
#CONFIG_SCHEMA = 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.Optional(CONF_NAME): cv.string,
# }
#).extend(
# {
# cv.GenerateID(): cv.declare_id(Channel)
# }
#).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:
# 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
@ -89,8 +128,10 @@ async def to_code(config):
cg.add(channel_sensor.set_phase_calibration(phase_cal))
dc_block = config[CONF_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)
if dc_sensor := await to_code_dc(config):
await cg.register_parented(dc_sensor, channel_sensor)
if ac_sensor := await to_code_ac(config):
await cg.register_parented(ac_sensor, channel_sensor)
#if dc_sensor := await to_code_dc(config):
# await cg.register_parented(dc_sensor, channel_sensor)
#if ac_sensor := await to_code_ac(config):
# await cg.register_parented(ac_sensor, channel_sensor)

View File

@ -25,7 +25,28 @@ void Channel::loop()
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

View File

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

View File

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