Better AC sensing circuit for ADC conversion

Thread Starter

Hasan2019

Joined Sep 5, 2019
199
Why do you need "True RMS"? You aren't computing power. You just need to be able to accurately determine the value of the voltage of the AC input signal.
I have used 2 set of Arduino code, both suffer from not being true RMS, but are OK for non precision rough approximation work.
When line is essentially sinusoidal. Both only look at + half of cycle, so anything going on in
other half cycle not measured.
True RMS implies measure complex waveform, like distorted sine, or other, the energy in each cycle.
Early methods took waveform, applied to a R to generate heat, and calculated from R thermal proper-
tiers the power, from which V could be extracted.
 

drjohsmith

Joined Dec 13, 2021
1,613
I have used 2 set of Arduino code, both suffer from not being true RMS, but are OK for non precision rough approximation work.
When line is essentially sinusoidal. Both only look at + half of cycle, so anything going on in
other half cycle not measured.
True RMS implies measure complex waveform, like distorted sine, or other, the energy in each cycle.
Early methods took waveform, applied to a R to generate heat, and calculated from R thermal proper-
tiers the power, from which V could be extracted.
Seems your on top of the true rms
It's all about what accuracy your after against code complexity and time to produce an answer.
It's those eternal engineering questions
 

Thread Starter

Hasan2019

Joined Sep 5, 2019
199
Seems your on top of the true rms
It's all about what accuracy your after against code complexity and time to produce an answer.
It's those eternal engineering questions
All that code does is find the peak value. If input is sine than its accurate.
Any other waveform its inaccurate.

For example if it takes 100 readings, 200 uS per reading (approx). In a 60 Hz waveform there are
roughly 16 mS / .2 mS = 80 readings, so its using more than 1 cycle of the line V
and its not synced to the line if the intent was to do one cycle evaluation. But anyway
its not summing readings (as if to compute energy in one cycle) its just finding peak.

Solution does not get to true RMS and cannot get to true RMS unless line is pure sine.
 

drjohsmith

Joined Dec 13, 2021
1,613
All that code does is find the peak value. If input is sine than its accurate.
Any other waveform its inaccurate.

For example if it takes 100 readings, 200 uS per reading (approx). In a 60 Hz waveform there are
roughly 16 mS / .2 mS = 80 readings, so its using more than 1 cycle of the line V
and its not synced to the line if the intent was to do one cycle evaluation. But anyway
its not summing readings (as if to compute energy in one cycle) its just finding peak.

Solution does not get to true RMS and cannot get to true RMS unless line is pure sine.
Might have posts crossed @Hasan2019
But there are algorithms to do true rms off of asynchronous over sampled data , again depends on what accuracy/ resolution you want

Once upon time , part of project to measure true rms power on a RF signal. We heated a resistor and measured temperature!
20 years later ,new instrument used voltage measurement across load , under sampled and algorithm.its just maths ..
 

Ian0

Joined Aug 7, 2020
13,155
All that code does is find the peak value. If input is sine than its accurate.
Any other waveform its inaccurate.

For example if it takes 100 readings, 200 uS per reading (approx). In a 60 Hz waveform there are
roughly 16 mS / .2 mS = 80 readings, so its using more than 1 cycle of the line V
and its not synced to the line if the intent was to do one cycle evaluation. But anyway
its not summing readings (as if to compute energy in one cycle) its just finding peak.

Solution does not get to true RMS and cannot get to true RMS unless line is pure sine.
Just thinking about implementing an IIR on a 8-bit processor. ..
If you can use a filter with a time constant of 256 samples, then it can be done quite easily.
Use 3 bytes to make a 24-bit accumulator.
To do the IIR filter, subtract the 16-bit number formed from the highest two bytes from the 24-bit accumulator, then add in the new sample. Use 8 bits from the ADC and square it to make 16 bits. It takes 3 subtract with carry and 3 add with carry instructions.
If you are just comparing the output with a fixed value, there is no need to calculate the square root, just square the value to which it is being compared.
(Of course, if you have a 32-bit processor with a barrel-shifter, the it’s a complete doddle. Done in 3 instructions.)
 

Thread Starter

Hasan2019

Joined Sep 5, 2019
199
Just thinking about implementing an IIR on a 8-bit processor. ..
If you can use a filter with a time constant of 256 samples, then it can be done quite easily.
Use 3 bytes to make a 24-bit accumulator.
To do the IIR filter, subtract the 16-bit number formed from the highest two bytes from the 24-bit accumulator, then add in the new sample. Use 8 bits from the ADC and square it to make 16 bits. It takes 3 subtract with carry and 3 add with carry instructions.
If you are just comparing the output with a fixed value, there is no need to calculate the square root, just square the value to which it is being compared.
(Of course, if you have a 32-bit processor with a barrel-shifter, the it’s a complete doddle. Done in 3 instructions.)
Is it for other processor ? According to your response at post 13, can you suggest which of the code I should follow here, Arduino-signal-filtering-library
 

drjohsmith

Joined Dec 13, 2021
1,613
Iir good way
Just re number of samples
It needs to be decided upon based upon desired acceptable accuracy, acceptable delay in reading , sample rate and signal frequency

We need to know what you really want in terms of these.

Applying different algorithms to the same signal can lead to different results

If your only after simple numbers , the classic iir is all you. Need
 

Thread Starter

Hasan2019

Joined Sep 5, 2019
199
Iir good way
Just re number of samples
It needs to be decided upon based upon desired acceptable accuracy, acceptable delay in reading , sample rate and signal frequency

We need to know what you really want in terms of these.

Applying different algorithms to the same signal can lead to different results

If your only after simple numbers , the classic iir is all you. Need

Lets use one of the easy circuit from this thread and how about follow this code ? Kindly discuss if you think its wrong.

C:
/*
  Measure Vrms and Vavg (rectified) on an analog input.

  Hardware:
    - Feed a SAFE conditioned signal (0-5 V) into A0.
    - AC signals must be biased around ~VREF/2 with a resistor divider and/or coupling capacitor.
    - Never connect mains or high voltage directly to Arduino.

  What is computed:
    - Vrms: RMS of AC component (samples minus estimated midpoint)
    - Vavg(rectified): average of |samples - midpoint|
    - Avg_raw: average of raw voltage (shows DC/bias)
    - Midpoint_est: estimated DC midpoint in volts

  Adjustments:
    - VREF: set to your board's ADC reference voltage.
    - N_SAMPLES: number of samples per block.
    - SAMPLE_DELAY_US: microseconds delay between samples (rudimentary control).
*/

const uint8_t ANALOG_PIN = A0;
const float   VREF       = 5.0f;     // ADC reference voltage (e.g., 5.0 for Arduino Uno)
const uint16_t ADC_MAX   = 1023;     // 10-bit ADC

const size_t  N_SAMPLES       = 1000;  // Number of samples per measurement block
const unsigned int SAMPLE_DELAY_US = 200; // microseconds between samples (~5 kHz sampling rate)

// Optional: average multiple blocks for stability
const uint8_t BLOCKS = 1;  // set >1 to average results across blocks

// Storage (allocated statically as global to avoid stack issues)
uint16_t samples[N_SAMPLES];

void setup() {
  Serial.begin(115200);
  // Use default reference (5 V on Uno). For other boards, adjust or use analogReference(INTERNAL/EXTERNAL).
  // analogReference(DEFAULT);
  // Stabilize a moment
  delay(500);

  Serial.println(F("=== Vrms and Vavg(rectified) Measurement ==="));
  Serial.print(F("VREF = ")); Serial.print(VREF); Serial.println(F(" V"));
  Serial.print(F("Sampling ~ "));
  float fs_khz = 1000.0f / float(SAMPLE_DELAY_US); // in kHz
  Serial.print(fs_khz, 2); Serial.println(F(" kHz (approx)"));
  Serial.print(F("Samples per block = ")); Serial.println(N_SAMPLES);
  Serial.println();
}

void loop() {
  // Accumulators to average over multiple blocks if BLOCKS > 1
  double sum_Vrms = 0.0;
  double sum_Vavg_rect = 0.0;
  double sum_Avg_raw = 0.0;
  double sum_midpoint_est = 0.0;

  for (uint8_t b = 0; b < BLOCKS; ++b) {
    // 1) Acquire samples
    for (size_t i = 0; i < N_SAMPLES; ++i) {
      samples[i] = analogRead(ANALOG_PIN);
      if (SAMPLE_DELAY_US > 0) {
        delayMicroseconds(SAMPLE_DELAY_US);
      }
    }

    // 2) Convert to volts and estimate the DC midpoint (mean of raw samples)
    double sum_raw = 0.0;
    for (size_t i = 0; i < N_SAMPLES; ++i) {
      sum_raw += (double)samples[i];
    }
    double mean_adc = sum_raw / (double)N_SAMPLES;
    double midpoint_volts = (mean_adc / ADC_MAX) * VREF;

    // 3) Compute RMS around midpoint and rectified average
    double sum_sq = 0.0;
    double sum_abs = 0.0;

    for (size_t i = 0; i < N_SAMPLES; ++i) {
      // Convert each sample to volts
      double v = (double)samples[i] * VREF / ADC_MAX;

      // AC component: remove estimated midpoint
      double vac = v - midpoint_volts;

      sum_sq  += vac * vac;     // for RMS
      sum_abs += fabs(vac);     // for rectified average
    }

    double Vrms = sqrt(sum_sq / (double)N_SAMPLES);
    double Vavg_rectified = sum_abs / (double)N_SAMPLES;
    double Avg_raw = (mean_adc / ADC_MAX) * VREF;

    // Aggregate
    sum_Vrms        += Vrms;
    sum_Vavg_rect   += Vavg_rectified;
    sum_Avg_raw     += Avg_raw;
    sum_midpoint_est += midpoint_volts;
  }

  // Average across blocks
  double Vrms_avg        = sum_Vrms / BLOCKS;
  double Vavg_rect_avg   = sum_Vavg_rect / BLOCKS;
  double Avg_raw_avg     = sum_Avg_raw / BLOCKS;
  double Midpoint_avg    = sum_midpoint_est / BLOCKS;

  // 4) Print results
  Serial.println(F("----- Measurement -----"));
  Serial.print(F("Estimated midpoint (DC bias): ")); Serial.print(Midpoint_avg, 4); Serial.println(F(" V"));
  Serial.print(F("Avg(raw): ")); Serial.print(Avg_raw_avg, 4); Serial.println(F(" V"));

  Serial.print(F("Vavg (rectified): ")); Serial.print(Vavg_rect_avg, 4); Serial.println(F(" V"));
  Serial.print(F("Vrms: ")); Serial.print(Vrms_avg, 4); Serial.println(F(" V"));

  // 5) Compare Vrms and Vavg(rectified)
  // For a pure sine: Vavg_rectified ≈ (2/π) * Vpeak, Vrms = Vpeak / √2 => Vrms ≈ 1.1107 * Vavg_rectified
  double ratio = (Vavg_rect_avg > 0.0) ? (Vrms_avg / Vavg_rect_avg) : NAN;
  Serial.print(F("Ratio Vrms / Vavg(rect): "));
  if (isnan(ratio)) Serial.println(F("N/A"));
  else              Serial.println(ratio, 4);

  // Wait before next block
  delay(800);
}
 

drjohsmith

Joined Dec 13, 2021
1,613
Lets use one of the easy circuit from this thread and how about follow this code ? Kindly discuss if you think its wrong.

C:
/*
  Measure Vrms and Vavg (rectified) on an analog input.

  Hardware:
    - Feed a SAFE conditioned signal (0-5 V) into A0.
    - AC signals must be biased around ~VREF/2 with a resistor divider and/or coupling capacitor.
    - Never connect mains or high voltage directly to Arduino.

  What is computed:
    - Vrms: RMS of AC component (samples minus estimated midpoint)
    - Vavg(rectified): average of |samples - midpoint|
    - Avg_raw: average of raw voltage (shows DC/bias)
    - Midpoint_est: estimated DC midpoint in volts

  Adjustments:
    - VREF: set to your board's ADC reference voltage.
    - N_SAMPLES: number of samples per block.
    - SAMPLE_DELAY_US: microseconds delay between samples (rudimentary control).
*/

const uint8_t ANALOG_PIN = A0;
const float   VREF       = 5.0f;     // ADC reference voltage (e.g., 5.0 for Arduino Uno)
const uint16_t ADC_MAX   = 1023;     // 10-bit ADC

const size_t  N_SAMPLES       = 1000;  // Number of samples per measurement block
const unsigned int SAMPLE_DELAY_US = 200; // microseconds between samples (~5 kHz sampling rate)

// Optional: average multiple blocks for stability
const uint8_t BLOCKS = 1;  // set >1 to average results across blocks

// Storage (allocated statically as global to avoid stack issues)
uint16_t samples[N_SAMPLES];

void setup() {
  Serial.begin(115200);
  // Use default reference (5 V on Uno). For other boards, adjust or use analogReference(INTERNAL/EXTERNAL).
  // analogReference(DEFAULT);
  // Stabilize a moment
  delay(500);

  Serial.println(F("=== Vrms and Vavg(rectified) Measurement ==="));
  Serial.print(F("VREF = ")); Serial.print(VREF); Serial.println(F(" V"));
  Serial.print(F("Sampling ~ "));
  float fs_khz = 1000.0f / float(SAMPLE_DELAY_US); // in kHz
  Serial.print(fs_khz, 2); Serial.println(F(" kHz (approx)"));
  Serial.print(F("Samples per block = ")); Serial.println(N_SAMPLES);
  Serial.println();
}

void loop() {
  // Accumulators to average over multiple blocks if BLOCKS > 1
  double sum_Vrms = 0.0;
  double sum_Vavg_rect = 0.0;
  double sum_Avg_raw = 0.0;
  double sum_midpoint_est = 0.0;

  for (uint8_t b = 0; b < BLOCKS; ++b) {
    // 1) Acquire samples
    for (size_t i = 0; i < N_SAMPLES; ++i) {
      samples[i] = analogRead(ANALOG_PIN);
      if (SAMPLE_DELAY_US > 0) {
        delayMicroseconds(SAMPLE_DELAY_US);
      }
    }

    // 2) Convert to volts and estimate the DC midpoint (mean of raw samples)
    double sum_raw = 0.0;
    for (size_t i = 0; i < N_SAMPLES; ++i) {
      sum_raw += (double)samples[i];
    }
    double mean_adc = sum_raw / (double)N_SAMPLES;
    double midpoint_volts = (mean_adc / ADC_MAX) * VREF;

    // 3) Compute RMS around midpoint and rectified average
    double sum_sq = 0.0;
    double sum_abs = 0.0;

    for (size_t i = 0; i < N_SAMPLES; ++i) {
      // Convert each sample to volts
      double v = (double)samples[i] * VREF / ADC_MAX;

      // AC component: remove estimated midpoint
      double vac = v - midpoint_volts;

      sum_sq  += vac * vac;     // for RMS
      sum_abs += fabs(vac);     // for rectified average
    }

    double Vrms = sqrt(sum_sq / (double)N_SAMPLES);
    double Vavg_rectified = sum_abs / (double)N_SAMPLES;
    double Avg_raw = (mean_adc / ADC_MAX) * VREF;

    // Aggregate
    sum_Vrms        += Vrms;
    sum_Vavg_rect   += Vavg_rectified;
    sum_Avg_raw     += Avg_raw;
    sum_midpoint_est += midpoint_volts;
  }

  // Average across blocks
  double Vrms_avg        = sum_Vrms / BLOCKS;
  double Vavg_rect_avg   = sum_Vavg_rect / BLOCKS;
  double Avg_raw_avg     = sum_Avg_raw / BLOCKS;
  double Midpoint_avg    = sum_midpoint_est / BLOCKS;

  // 4) Print results
  Serial.println(F("----- Measurement -----"));
  Serial.print(F("Estimated midpoint (DC bias): ")); Serial.print(Midpoint_avg, 4); Serial.println(F(" V"));
  Serial.print(F("Avg(raw): ")); Serial.print(Avg_raw_avg, 4); Serial.println(F(" V"));

  Serial.print(F("Vavg (rectified): ")); Serial.print(Vavg_rect_avg, 4); Serial.println(F(" V"));
  Serial.print(F("Vrms: ")); Serial.print(Vrms_avg, 4); Serial.println(F(" V"));

  // 5) Compare Vrms and Vavg(rectified)
  // For a pure sine: Vavg_rectified ≈ (2/π) * Vpeak, Vrms = Vpeak / √2 => Vrms ≈ 1.1107 * Vavg_rectified
  double ratio = (Vavg_rect_avg > 0.0) ? (Vrms_avg / Vavg_rect_avg) : NAN;
  Serial.print(F("Ratio Vrms / Vavg(rect): "));
  if (isnan(ratio)) Serial.println(F("N/A"));
  else              Serial.println(ratio, 4);

  // Wait before next block
  delay(800);
}
Code looks fine , if it gives you the answers you want , and your happy with the level of complexity of the circuit .
You have not told us what you want though !
 

Thread Starter

Hasan2019

Joined Sep 5, 2019
199
Code looks fine , if it gives you the answers you want , and your happy with the level of complexity of the circuit .
You have not told us what you want though !
Just to check with this table, see all the discussion from the beginning.
1. 10 bit ADC
2. True RMS calculation
3. Reduce number of component (Attenuation)
4. Avoided OpAMP,LM358, sensors, duel power supply.
1767530586341.png
 

drjohsmith

Joined Dec 13, 2021
1,613
Just to check with this table, see all the discussion from the beginning.
1. 10 bit ADC
2. True RMS calculation
3. Reduce number of component (Attenuation)
4. Avoided LM358, sensors, duel power supply.
View attachment 361637
Thank you .
Sorry for not being explicit

You have a 10 bit adc in the pic ? but the specs of that arc are liable to be +- a few lsb.
So assuming you have a real 9 bits
Then the filtering , by means of adc over sampling , is adding say 3 bits of signal resolution
Easy rough numbers

You now have 250v , sampled to 12 bits, that's 250 / 4096 ,which off top of head is 1/16 th of a volt . About 0.06 volts

To be true , your external circuit , needs to contribute less than around 1/4 of that as noise / errors . About 10 mV

Now none of the circuit you have shown is no where near that accuracy , so do you really need 10 bits.
For a text DVM. , then a few bits is needed , but then your external circuit needs to be a lot better, using high quality components , and ability to calibrate , in the hardware or the SW.

Your current algorithm. Relies upon an AC coupled signal ,which as and if itself is a limitation on the accuracy..
Extream example, if your ax wavefirm into adc is 4v pkpk, but centered on 3v check what the algorithm gives with the input signal same but biased at 2v .

Id suggest you talk to whom set the project , see what they want .
If it's a professor , they will be happy that you have considered all these options .

Also note .
All the examples you show , are single cycles
What happens with the processing sampling over many cycles, and not an integer number.
Say the bottom one , the DC pulse
Then if you sampled over say 4 1/2 cyckes, one extream you could have 5 pulses, the other only 4 . As you don't know how many cycles you have , the answer will be different

The answer is to take many cycles ,
Then you loose the ability to see perks on only a few cycles, and add delay to the answer piping out .

It all depends what you want to do
 
Last edited:

Thread Starter

Hasan2019

Joined Sep 5, 2019
199
@drjohsmith, your are exceptionally well done here. This comment make sense.
Id suggest you talk to whom set the project , see what they want .
If it's a professor , they will be happy that you have considered all these options .
16 years ago I was enrolling a EEE PhD student, that crazy professor wanted lot from me, his expectation was sky level where my level was zero. He made money, he dreamed for money. I struggled for money. Sorry to say I am a bad *fellow*.
 

drjohsmith

Joined Dec 13, 2021
1,613
@drjohsmith, your are exceptionally well done here. This comment make sense.

16 years ago I was enrolling a EEE PhD student, that crazy professor wanted lot from me, his expectation was sky level where my level was zero. He made money, he dreamed for money. I struggled for money. Sorry to say I am a bad *fellow*.
you are doing just fine ,

strugling for money is a real strain , I get that
but I bet the professor is missimg out now compared to you

so what is this for ,,a client, yourself , a friend or what ?
 

Thread Starter

Hasan2019

Joined Sep 5, 2019
199
you are doing just fine ,

strugling for money is a real strain , I get that
but I bet the professor is missimg out now compared to you

so what is this for ,,a client, yourself , a friend or what ?
I am working in a TOP global company, my clients are Meta, Google, AMD. but this is my personal project. Home made. Its a voltage stabilizer project.
 

eetech00

Joined Jun 8, 2013
4,709
I have used 2 set of Arduino code, both suffer from not being true RMS, but are OK for non precision rough approximation work.
When line is essentially sinusoidal. Both only look at + half of cycle, so anything going on in
other half cycle not measured.
True RMS implies measure complex waveform, like distorted sine, or other, the energy in each cycle.
Early methods took waveform, applied to a R to generate heat, and calculated from R thermal proper-
tiers the power, from which V could be extracted.
But without rectification, the ADC will be reading the full cycle, not half cycle.
There might be a language issue here, but nothing you have shown measures RMS power (requiring current (IRMS) measurement). Your voltage stabilizer requires an AC line voltage level reading.
 

Jerry-Hat-Trick

Joined Aug 31, 2022
828
I’ve just revisited this thread so please forgive if I have missed something, but may I ask exactly what this voltage measurement is to be used for? Will the system feed back somehow to adjust something or will the result just be displayed? Several posts have asked what level of accuracy you are looking for, which should be an early question to be answered when preparing a requirement specification. If a peak to peak measurement would suffice then a fairly basic processor with minimal code would look out for zero crossings and A/D half way between. If switching with relays based on measurements then no need to isolate the processor, just use opto isolators to switch the relays
 

eetech00

Joined Jun 8, 2013
4,709
I’ve just revisited this thread so please forgive if I have missed something, but may I ask exactly what this voltage measurement is to be used for? Will the system feed back somehow to adjust something or will the result just be displayed? Several posts have asked what level of accuracy you are looking for, which should be an early question to be answered when preparing a requirement specification. If a peak to peak measurement would suffice then a fairly basic processor with minimal code would look out for zero crossings and A/D half way between. If switching with relays based on measurements then no need to isolate the processor, just use opto isolators to switch the relays
The TS is designing a "voltage stabilizer". The AC line input might vary above or below the target output Line voltage.
It is supposed to monitor AC line voltage and adjust the line output (thru a selection of taps on an auto-transformer) to keep the output at a stable level. The TS has discussed this project in other forums. Its also supposed to detect over/under voltages
 

Ian0

Joined Aug 7, 2020
13,155
Lets use one of the easy circuit from this thread and how about follow this code ? Kindly discuss if you think its wrong.

C:
/*
  Measure Vrms and Vavg (rectified) on an analog input.

  Hardware:
    - Feed a SAFE conditioned signal (0-5 V) into A0.
    - AC signals must be biased around ~VREF/2 with a resistor divider and/or coupling capacitor.
    - Never connect mains or high voltage directly to Arduino.

  What is computed:
    - Vrms: RMS of AC component (samples minus estimated midpoint)
    - Vavg(rectified): average of |samples - midpoint|
    - Avg_raw: average of raw voltage (shows DC/bias)
    - Midpoint_est: estimated DC midpoint in volts

  Adjustments:
    - VREF: set to your board's ADC reference voltage.
    - N_SAMPLES: number of samples per block.
    - SAMPLE_DELAY_US: microseconds delay between samples (rudimentary control).
*/

const uint8_t ANALOG_PIN = A0;
const float   VREF       = 5.0f;     // ADC reference voltage (e.g., 5.0 for Arduino Uno)
const uint16_t ADC_MAX   = 1023;     // 10-bit ADC

const size_t  N_SAMPLES       = 1000;  // Number of samples per measurement block
const unsigned int SAMPLE_DELAY_US = 200; // microseconds between samples (~5 kHz sampling rate)

// Optional: average multiple blocks for stability
const uint8_t BLOCKS = 1;  // set >1 to average results across blocks

// Storage (allocated statically as global to avoid stack issues)
uint16_t samples[N_SAMPLES];

void setup() {
  Serial.begin(115200);
  // Use default reference (5 V on Uno). For other boards, adjust or use analogReference(INTERNAL/EXTERNAL).
  // analogReference(DEFAULT);
  // Stabilize a moment
  delay(500);

  Serial.println(F("=== Vrms and Vavg(rectified) Measurement ==="));
  Serial.print(F("VREF = ")); Serial.print(VREF); Serial.println(F(" V"));
  Serial.print(F("Sampling ~ "));
  float fs_khz = 1000.0f / float(SAMPLE_DELAY_US); // in kHz
  Serial.print(fs_khz, 2); Serial.println(F(" kHz (approx)"));
  Serial.print(F("Samples per block = ")); Serial.println(N_SAMPLES);
  Serial.println();
}

void loop() {
  // Accumulators to average over multiple blocks if BLOCKS > 1
  double sum_Vrms = 0.0;
  double sum_Vavg_rect = 0.0;
  double sum_Avg_raw = 0.0;
  double sum_midpoint_est = 0.0;

  for (uint8_t b = 0; b < BLOCKS; ++b) {
    // 1) Acquire samples
    for (size_t i = 0; i < N_SAMPLES; ++i) {
      samples[i] = analogRead(ANALOG_PIN);
      if (SAMPLE_DELAY_US > 0) {
        delayMicroseconds(SAMPLE_DELAY_US);
      }
    }

    // 2) Convert to volts and estimate the DC midpoint (mean of raw samples)
    double sum_raw = 0.0;
    for (size_t i = 0; i < N_SAMPLES; ++i) {
      sum_raw += (double)samples[i];
    }
    double mean_adc = sum_raw / (double)N_SAMPLES;
    double midpoint_volts = (mean_adc / ADC_MAX) * VREF;

    // 3) Compute RMS around midpoint and rectified average
    double sum_sq = 0.0;
    double sum_abs = 0.0;

    for (size_t i = 0; i < N_SAMPLES; ++i) {
      // Convert each sample to volts
      double v = (double)samples[i] * VREF / ADC_MAX;

      // AC component: remove estimated midpoint
      double vac = v - midpoint_volts;

      sum_sq  += vac * vac;     // for RMS
      sum_abs += fabs(vac);     // for rectified average
    }

    double Vrms = sqrt(sum_sq / (double)N_SAMPLES);
    double Vavg_rectified = sum_abs / (double)N_SAMPLES;
    double Avg_raw = (mean_adc / ADC_MAX) * VREF;

    // Aggregate
    sum_Vrms        += Vrms;
    sum_Vavg_rect   += Vavg_rectified;
    sum_Avg_raw     += Avg_raw;
    sum_midpoint_est += midpoint_volts;
  }

  // Average across blocks
  double Vrms_avg        = sum_Vrms / BLOCKS;
  double Vavg_rect_avg   = sum_Vavg_rect / BLOCKS;
  double Avg_raw_avg     = sum_Avg_raw / BLOCKS;
  double Midpoint_avg    = sum_midpoint_est / BLOCKS;

  // 4) Print results
  Serial.println(F("----- Measurement -----"));
  Serial.print(F("Estimated midpoint (DC bias): ")); Serial.print(Midpoint_avg, 4); Serial.println(F(" V"));
  Serial.print(F("Avg(raw): ")); Serial.print(Avg_raw_avg, 4); Serial.println(F(" V"));

  Serial.print(F("Vavg (rectified): ")); Serial.print(Vavg_rect_avg, 4); Serial.println(F(" V"));
  Serial.print(F("Vrms: ")); Serial.print(Vrms_avg, 4); Serial.println(F(" V"));

  // 5) Compare Vrms and Vavg(rectified)
  // For a pure sine: Vavg_rectified ≈ (2/π) * Vpeak, Vrms = Vpeak / √2 => Vrms ≈ 1.1107 * Vavg_rectified
  double ratio = (Vavg_rect_avg > 0.0) ? (Vrms_avg / Vavg_rect_avg) : NAN;
  Serial.print(F("Ratio Vrms / Vavg(rect): "));
  if (isnan(ratio)) Serial.println(F("N/A"));
  else              Serial.println(ratio, 4);

  // Wait before next block
  delay(800);
}
writing double Vrms = sqrt(sum_sq / (double)N_SAMPLES); in c is trivially easy, but it masks just how difficult and time consuming it is to calculate it on an 8-bit processor without a floating point arithmetic unit.
You won't get an answer until a week next tuesday.
As I said in post #45, there is actually no need to calculate the square root if all you are doing is a comparison,
and
implementing it as an IIR can be trivial if you want it to be, by choosing a time constant that makes it easy for the processor, and it has and advantage of needing only 3 bytes of storage, and producing an updated answer at every sample.
 
Top