From de749054364c91f7422faf5533fd04c90b40e36c Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 7 May 2026 22:37:38 +0200 Subject: [PATCH] trying out polling ads131m08 sensor --- ads131m08/ads131m08.cpp | 94 +++++++++++++++++++++------ ads131m08/ads131m08.h | 3 + ads131m08/sensor/__init__.py | 83 +++++++++++++++++------ ads131m08/sensor/ads131m08_sensor.cpp | 23 ++++++- ads131m08/sensor/ads131m08_sensor.h | 6 +- ads131m08/sensor_rms/__init__.py | 18 ++--- 6 files changed, 176 insertions(+), 51 deletions(-) diff --git a/ads131m08/ads131m08.cpp b/ads131m08/ads131m08.cpp index 05b49dc..a80ac19 100644 --- a/ads131m08/ads131m08.cpp +++ b/ads131m08/ads131m08.cpp @@ -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 } diff --git a/ads131m08/ads131m08.h b/ads131m08/ads131m08.h index 43ad596..6f84bbd 100644 --- a/ads131m08/ads131m08.h +++ b/ads131m08/ads131m08.h @@ -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}; diff --git a/ads131m08/sensor/__init__.py b/ads131m08/sensor/__init__.py index 682c07a..801b00c 100644 --- a/ads131m08/sensor/__init__.py +++ b/ads131m08/sensor/__init__.py @@ -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) diff --git a/ads131m08/sensor/ads131m08_sensor.cpp b/ads131m08/sensor/ads131m08_sensor.cpp index a9c0c03..6958f20 100644 --- a/ads131m08/sensor/ads131m08_sensor.cpp +++ b/ads131m08/sensor/ads131m08_sensor.cpp @@ -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 diff --git a/ads131m08/sensor/ads131m08_sensor.h b/ads131m08/sensor/ads131m08_sensor.h index b4231b9..d0feb1e 100644 --- a/ads131m08/sensor/ads131m08_sensor.h +++ b/ads131m08/sensor/ads131m08_sensor.h @@ -13,12 +13,13 @@ namespace esphome { namespace ads131m08 { class Channel : public sensor::Sensor, - public Component, + public PollingComponent, public voltage_sampler::VoltageSampler, public Parented { 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, diff --git a/ads131m08/sensor_rms/__init__.py b/ads131m08/sensor_rms/__init__.py index e3036d8..a3f8651 100644 --- a/ads131m08/sensor_rms/__init__.py +++ b/ads131m08/sensor_rms/__init__.py @@ -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 \ No newline at end of file + 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 \ No newline at end of file