From 40c7cf58e617b2b5bf2dffc90f6802e4b6384a05 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 11 May 2026 22:28:13 +0200 Subject: [PATCH] Slowed down refresh rate of ac rms sensors with sliding_window_moving_average filter in yaml file. Added sample limit on rms sensor in read_multi procedure. Updated README.md file. --- ads131m08/README.md | 86 +++++++++++++++++---------- ads131m08/ads131m08.cpp | 60 +++++++++++++------ ads131m08/ads131m08.h | 5 +- ads131m08/sensor/ads131m08_sensor.cpp | 10 ++-- 4 files changed, 106 insertions(+), 55 deletions(-) diff --git a/ads131m08/README.md b/ads131m08/README.md index b58a6bb..1ad9627 100644 --- a/ads131m08/README.md +++ b/ads131m08/README.md @@ -15,12 +15,6 @@ substitutions: ADC_CS_PIN: GPIO41 ADC_DRDY_PIN: GPIO42 -external_components: - - source: - type: local - path: components # Path relative to this YAML file - components: [ ads131m08 ] - spi: - id: spi_bus0 clk_pin: ${ESP32_S3_SPI0_SCK} @@ -29,15 +23,25 @@ spi: interface: hardware ads131m08: - id: highres_adc - spi_id: spi_bus0 - cs_pin: ${ADC_CS_PIN} - drdy_pin: ${ADC_DRDY_PIN} - sync_reset_pin: ${ADC_SYNC_RESET_PIN} - reference_voltage: 1.25 - data_rate: 10MHz - clock_frequency: 8192kHz - oversampling_ratio: 512 + - id: highres_adc + spi_id: spi_bus0 + cs_pin: ${ADC_CS_PIN} + drdy_pin: ${ADC_DRDY_PIN} + sync_reset_pin: ${ADC_SYNC_RESET_PIN} + reference_voltage: 1.248 #1.25 + data_rate: 5MHz + clock_frequency: 8192kHz + oversampling_ratio: 512 + spi_mode: MODE1 + dcblock_filter: 5 # 10Hz + power_mode: high + global_chop: no + global_chop_delay: 1024 + current_detect: disable + current_detect_all: no + current_detect_number: 16 + current_detect_length: 768 + current_detect_threshold: -500 sensor: - platform: ads131m08 @@ -49,11 +53,14 @@ sensor: offset_calibration: 0 gain_calibration: 0.986 ac: - name: "Channel 0 AC" - id: ads_ch0_ac - dc: - name: "Channel 0 DC" - id: ads_ch0_dc + name: "ac_ch0" + id: ac_ch0 + filters: + - multiply: 2884 + - sliding_window_moving_average: + window_size: 50 + send_every: 25 + send_first_at: 10 - platform: ads131m08 ads131m08_id: highres_adc @@ -64,17 +71,34 @@ sensor: offset_calibration: 0 gain_calibration: 0.986 ac: - name: "Channel 1 AC" - id: ads_ch1_ac - accuracy_decimals: 6 - state_class: measurement - device_class: voltage - dc: - name: "Channel 1 DC" - id: ads_ch1_dc - accuracy_decimals: 6 - state_class: measurement - device_class: voltage + name: "ac_ch1" + id: ac_ch1 + filters: + - multiply: 2884 + - sliding_window_moving_average: + window_size: 50 + send_every: 25 + send_first_at: 10 + + - platform: ads131m08 + ads131m08_id: highres_adc + channel: 2 + id: ads_ch2 + gain: 1 + input_select: normal + offset_calibration: 0 + gain_calibration: 1.36153846 + dc_block: enable + ac: + name: "ac_ch2" + id: ac_ch2 + device_class: current + filters: + - multiply: 62.97 + - sliding_window_moving_average: + window_size: 50 + send_every: 25 + send_first_at: 10 ``` For more information, visit the [ESPHome documentation](https://esphome.io/). diff --git a/ads131m08/ads131m08.cpp b/ads131m08/ads131m08.cpp index eba6f7e..2b4a063 100644 --- a/ads131m08/ads131m08.cpp +++ b/ads131m08/ads131m08.cpp @@ -122,19 +122,30 @@ void ADS131M08Hub::dump_config() { this->power_mode_ == VERY_LOW_POWER ? "'very low power'" : this->power_mode_ == LOW_POWER ? "'low power'" : "'high resolution'"); - ESP_LOGCONFIG(TAG, " Global Chop: %s", this->global_chop_enable_ ? "enable" : "disable"); - ESP_LOGCONFIG(TAG, " Global Chop Delay: %u", 1 << (1 + this->global_chop_delay_)); - ESP_LOGCONFIG(TAG, " Current Detect: %s", this->current_detect_enable_ ? "enable" : "disable"); - ESP_LOGCONFIG(TAG, " Current Detect All: %s", this->current_detect_all_enable_ ? "YES" : "NO"); - ESP_LOGCONFIG(TAG, " Current Detect Number: %u", 1 << this->current_detect_number_); - ESP_LOGCONFIG(TAG, " Current Detect Length: %u", convert_adc_cd_len_to_value(this->current_detect_length_)); - ESP_LOGCONFIG(TAG, " Current Detect Threshold: %ld", this->current_detect_threshold_); + std::string str =" Global Chop"; + ESP_LOGCONFIG(TAG, "%s: %s", str.c_str(), this->global_chop_enable_ ? "enable" : "disable"); + ESP_LOGCONFIG(TAG, "%s Delay: %u", str.c_str(), 1 << (1 + this->global_chop_delay_)); + str = " Current Detect"; + ESP_LOGCONFIG(TAG, "%s: %s", str.c_str(), this->current_detect_enable_ ? "enable" : "disable"); + ESP_LOGCONFIG(TAG, "%s All: %s", str.c_str(), this->current_detect_all_enable_ ? "YES" : "NO"); + ESP_LOGCONFIG(TAG, "%s Number: %u", str.c_str(), 1 << this->current_detect_number_); + ESP_LOGCONFIG(TAG, "%s Length: %u", str.c_str(), convert_adc_cd_len_to_value(this->current_detect_length_)); + ESP_LOGCONFIG(TAG, "%s Threshold: %ld", str.c_str(), this->current_detect_threshold_); + str =" DC Block Filter"; + if(this->dcblock_filter_ == 0) { + ESP_LOGCONFIG(TAG, "%s: 0 (disabled)", str.c_str()); + } + else if(this->dcblock_filter_ > 15) { + ESP_LOGCONFIG(TAG, "%s: %u (invalid)", str.c_str(), this->dcblock_filter_); + } + else { + ESP_LOGCONFIG(TAG, "%s('a'): %u (1/%u)", str.c_str(), this->dcblock_filter_, convert_adc_dc_block_to_value(this->dcblock_filter_)); + } ESP_LOGCONFIG(TAG, " SPI Mode: %d", this->mode_); ESP_LOGCONFIG(TAG, " Bit Order: %s", this->bit_order_ == spi::BIT_ORDER_MSB_FIRST ? "msb_first" : this->bit_order_ == spi::BIT_ORDER_LSB_FIRST ? "lsb_first" - : "unknown"); - // TODO: add new configs + : "invalid"); } uint8_t ADS131M08Hub::update_adc_word_length() { @@ -835,7 +846,7 @@ void ADS131M08Hub::read_single() { } // do not like what the following procedure does at all (it blocks for +-40ms), but at the moment do not know how to get // DRDY interrupt / ISR / loop setup to respond fast enough to read multiple frames with no or the minimum possible -// missed frames in quick succession idealy would want to read +- 200 24/32bit in 40ms. Gor now, this seems to be the +// missed frames in quick succession idealy would want to read +- 200 24/32bit in 40ms. For now, this seems to be the // only way to do it. float_str ADS131M08Hub::read_multi() { int32_t raw; @@ -861,7 +872,11 @@ float_str ADS131M08Hub::read_multi() { } CHIP_SELECTx uint32_t settling_end_time = loop_start_time; - while ((elapsed_time < sample_time) && (iteration++ < 1000)) { + uint32_t samples_per_second = 8000; // for osr of 512 + uint32_t sample_target = (sample_time * samples_per_second) / 1000000; // sample_time is in microseconds + //ESP_LOGI(TAG,"sample_target: %lu active_channel_count_: %d", sample_target, this->active_channel_count_); + int nchannels_sampled = 0; + while ((elapsed_time < sample_time) && (nchannels_sampled < this->active_channel_count_) && (iteration++ < 1000)) { result[2] = iteration; setup_frame(rx_frame, numFrameWords); this->transfer_array(rx_frame); @@ -890,7 +905,8 @@ float_str ADS131M08Hub::read_multi() { if(drdy_status == 0){ result[3]++; } - for (i = 0; i < ADC_CHANNELS && drdy_status != 0; i++) { + nchannels_sampled = 0; + for (i = 0; (i < ADC_CHANNELS) && (drdy_status != 0); i++) { data_ready = drdy_status & 1; drdy_status = drdy_status >> 1; if (data_ready) { @@ -901,9 +917,15 @@ float_str ADS131M08Hub::read_multi() { raw = get_sign_ext_frame_word(rx_frame, i + 1); value = this->conversion_factor_ * raw; if (this->rms_enabled_[i]) { - (this->num_samples_[i])++; - (this->sample_sum_[i]) += value; - (this->sample_squared_sum_[i]) += value * value; + if(this->num_samples_[i] < sample_target) { + (this->num_samples_[i])++; + (this->sample_sum_[i]) += value; + (this->sample_squared_sum_[i]) += value * value; + } + else { + nchannels_sampled++; + //ESP_LOGI(TAG,"target: %lu ac_cnt: %d it: %d sampled: %d time: %lu", sample_target, this->active_channel_count_, iteration, nchannels_sampled, elapsed_time); + } } else { if(ac_sensor_exist) this->sensors_ac[i]->publish_state(value); @@ -1190,8 +1212,7 @@ bool ADS131M08Hub::adc_register_write(uint16_t start_address, const uint16_str & uint16_t addr_regcnt_mask = (start_address << 7) | ((nregs - 1) & MASK_CMD_RW_REG_COUNT); uint16_t wreg_addr_opcode = CMD_WREG | addr_regcnt_mask; // Combine WREG command, start_address and register count uint16_t rreg_addr_opcode = CMD_RREG | addr_regcnt_mask; // Combine RREG command, start_address and register count - bool has_mode_reg = - (start_address == REG_MODE) || ((start_address < REG_MODE) && ((start_address + nregs) > REG_MODE)); + bool has_mode_reg = (start_address == REG_MODE) || ((start_address < REG_MODE) && ((start_address + nregs) > REG_MODE)); setup_frame(rx_frame, wreg_nwords); { CHIP_SELECT @@ -1217,6 +1238,11 @@ bool ADS131M08Hub::adc_register_write(uint16_t start_address, const uint16_str & //ESP_LOGW(TAG, "Write: %s", reg_data_to_string(start_address + i, data[i]).c_str()); //ESP_LOGW(TAG, "Ack : %s", reg_data_to_string(start_address + i, rxdata[i]).c_str()); } + if((start_address + i) == REG_CLOCK) { + // count the number of channels that were enabled + uint16_t clock_reg_data = data[i] & MASK_CLOCK_ALLCH; + this->active_channel_count_ = std::popcount(clock_reg_data); + } } delay_microseconds_safe(T_REGACQ); return verified; diff --git a/ads131m08/ads131m08.h b/ads131m08/ads131m08.h index 6a94d15..7aaddc2 100644 --- a/ads131m08/ads131m08.h +++ b/ads131m08/ads131m08.h @@ -150,6 +150,7 @@ class ADS131M08Hub : public Component, uint32_t convert_adc_clock_cycles_to_micros(uint32_t cycles); uint16_t convert_adc_osr_to_value(uint16_t adc_osr) { auto osr = 1 << (7 + adc_osr); return (osr == 16384) ? 16256 : osr; } uint16_t convert_adc_cd_len_to_value(uint16_t adc_cd_len) { return adc_cd_len >= sizeof(current_detect_lengths) ? 0 : current_detect_lengths[adc_cd_len]; } + uint16_t convert_adc_dc_block_to_value(uint16_t dcblock) { return dcblock > 15 ? 0 : (1 << (dcblock + 1)); } // returns denominator of 'a' coefficient protected: std::string id_; @@ -165,6 +166,7 @@ class ADS131M08Hub : public Component, float sampled_values_[ADC_CHANNELS]; bool rms_enabled_[MAX_CHANNELS]; bool rms_calc_req_{false}; + int active_channel_count_{0}; uint16_t osr_{3}; uint16_t global_chop_delay_{16}; bool global_chop_enable_{false}; @@ -203,8 +205,7 @@ class ADS131M08Hub : public Component, int32_t SET_IRAM get_sign_ext_frame_word(const spiframe &frame, int w_index); //bool update_averages(uint8_t channel, float rms_ac, float rms_dc); std::atomic adc_init_{0}; - std::atomic cs_ctr_{ - 0}; // Counter to track nested CS enable/disable calls for proper handling of multiple transfers in a row + std::atomic cs_ctr_{0}; // Counter to track nested CS enable/disable calls std::atomic isr_ctr_{0}; void enable(const char *txt); void disable(const char *txt); diff --git a/ads131m08/sensor/ads131m08_sensor.cpp b/ads131m08/sensor/ads131m08_sensor.cpp index c4427a7..fe9d9ae 100644 --- a/ads131m08/sensor/ads131m08_sensor.cpp +++ b/ads131m08/sensor/ads131m08_sensor.cpp @@ -70,16 +70,16 @@ int16_t Channel::normalised_phase_calibration() const void Channel::dump_config() { ESP_LOGCONFIG(TAG, "Channel %" PRIu8 ":", this->channel_); - ESP_LOGCONFIG(TAG, " Gain: %" PRIu8, 1 << this->gain_); - ESP_LOGCONFIG(TAG, " Gain calibration: %f", this->gain_cal_); - ESP_LOGCONFIG(TAG, " Offset calibration: %" PRIu32, this->offset_cal_); - ESP_LOGCONFIG(TAG, " Phase calibration: %" PRId32, this->phase_cal_); - ESP_LOGCONFIG(TAG, " DC block: %s", this->dc_block_ ? "YES" : "NO"); ESP_LOGCONFIG(TAG, " Input select: %s", (this->input_ == ICM_AIN0P_AIN0N) ? "normal" \ : (this->input_ == ICM_INPUT_SHORTED) ? "shorted" \ : (this->input_ == ICM_POSITIVE_DC_TEST_SIGNAL) ? "positive_dc" \ : (this->input_ == ICM_NEGATIVE_DC_TEST_SIGNAL) ? "negative_dc" \ : "invalid"); + ESP_LOGCONFIG(TAG, " Gain: %" PRIu8, 1 << this->gain_); + ESP_LOGCONFIG(TAG, " Gain calibration: %f", this->gain_cal_); + ESP_LOGCONFIG(TAG, " Offset calibration: %" PRIu32, this->offset_cal_); + ESP_LOGCONFIG(TAG, " Phase calibration: %" PRId32, this->phase_cal_); + ESP_LOGCONFIG(TAG, " DC block: %s", this->dc_block_ ? "YES" : "NO"); } void RMS_Sensor::loop()