Better AC sensing circuit for ADC conversion

Thread Starter

Hasan2019

Joined Sep 5, 2019
199
you need to select ratio that works for you.
if you are getting 19.5VDC when AC side is ok, ask yourself what is it going to be when AC size is at maximum that you are expecting to see.

suppose you expect 270V and right now is 230V, then rectified DC will be

19.5VDC * (270VAC/230VAC) = 22.9VDC

then your voltage divider need to be chosen so that you get less than 5V from it. keeping R1 as 1Meg and using R2 as 220k will give you decent ratio.

Vadc_max=Vdc_max*R2/(R1+R2)
Vadc_max=22.9VDC * 220k/(220k+1000k)=4.13V

Exactly, this is something that should usefull to understand what is the voltage bank status after BR1.
 
Last edited:

Thread Starter

Hasan2019

Joined Sep 5, 2019
199
same goes for minimum voltage... so using also 50V change,
Vac_min = 220-50V=170VAC
Vdc_min=Vdc*Vac_min/Vac=19.5VDC * (170VAC/230VAC) = 14.4VDC

so for mains voltage in range 170*270VAC, voltage after bridge rectifier is expected to be approximately 14.4...22.9VDC.
this is reasonable to derive 12VDC.

and the voltage for ADC will be
Vadc_min=Vdc_max*R2/(R1+R2)
Vadc_min=14.4VDC * 220k/(220k+1000k)=2.59V
so for mains voltage in range 170VAC-270VAC, your ADC would see 2.59VDC .. 4.13VDC. Which looks reasonable.

btw, you chose lower value filter caps, for some reason placed as D5 a Schottky diode instead of Zener. that is not going to help protect ADC. if you want to use that diode for protection it should be wired like this:
View attachment 362314

however, Schottky diodes have large leakage current and that would affect voltage reading that you are trying to measure.
Changing diode direction is not helping here. But the Max and Min DC voltage after BR1 approximation make sense. The ADC for 2.59V or 4.13 V won't related with number 1023 or 1024, this is the fact.
 

panic mode

Joined Oct 10, 2011
5,013
direction is not changed, diode is still reverse biased but tied to positive rail. in case of overvoltage it would be forward biased and clamp the signal to 5V rail. the ADC (if 10-bit) would still have range 0-1023 for 0-5V. and entire sensing range would still be within it, and - with healthy margin. the actual values 2.59 and 4.13V may be slightly off depending on your component tolerances. but, you do as you please...
 
Last edited:

eetech00

Joined Jun 8, 2013
4,709
@eT well comeback, draw a circuit then.
Here it is.

1768774858442.png

But this issue you explain here already discuss 3 or 4 times.
Maybe..but too many posts for me find it.

Do you have any plan to design Arduino Uno lib in Ltspice ?
I have existing Ardunio and PIC GPIO development stuff but I set it aside a few months ago.

Anyway, I won't be posting in this thread for a while..
 

Thread Starter

Hasan2019

Joined Sep 5, 2019
199
direction is not changed, diode is still reverse biased but tied to positive rail. in case of overvoltage it would be forward biased and clamp the signal to 5V rail. the ADC (if 10-bit) would still have range 0-1023 for 0-5V. and entire sensing range would still be within it, and - with healthy margin. the actual values 2.59 and 4.13V may be slightly off depending on your component tolerances. but, you do as you please...
I have another set of code where I can put my targeted AC values, have some loop for relays and corresponding ACvalues. I can see in this circuit Acvalues goes more than 580, that makes the problem. But the relay 3 ( set for 210VAC) changes its postion!!! Very strange! The code I posted at #169, has comparisons for input and output voltages at A0 and A1 pin. I know that 220AC to 10V or 7V step down voltages will cause the same, because from R1 and R2 ratio we can set it nearly 5VDC. @drjohsmith was talking about the input voltage with relay response time/ ADC sampling / each tap voltage steps and so on. Do you think any of these issue is root cause here or I am wrongly sampling AC values in this code.
 

MisterBill2

Joined Jan 23, 2018
27,639
@MrAl
wel. said , if your catching up, be aware the op has multiple posts over multiple forums over multiple years on this project ,
Qdos to them for keeping at it , but its hard to understand
.
My comment is deleted by myself because I had not read all of those comments in between. Sorry to but in folks.
 

MrAl

Joined Jun 17, 2014
13,716
I am posting a basic one here, take a look

C:
// Moving average parameters
const int N = 10;          // Number of samples for averaging
float Vavg = 0.0;          // Running average
int sensorPin = A0;        // Analog input pin

void setup() {
  Serial.begin(9600);
}

void loop() {
  // Read raw ADC value (0–1023)
  int raw = analogRead(sensorPin);

  // Convert to voltage (0–5V)
  float Vsample = raw * (5.0 / 1023.0);

  // Apply your formula
  Vavg = (Vavg * (N - 1) + Vsample) / N;

  // Print results
  Serial.print("Sample: ");
  Serial.print(Vsample, 3);
  Serial.print(" V   |   Avg: ");
  Serial.print(Vavg, 3);
  Serial.println(" V");

  delay(100);
}

Hello again,

Yes that looks like it would work, using floating point.

If you want to use all integers there are a couple of choices.
The first was given by MrChips:
sum=sume+Vsample-Vavg
Vavg=sum/N
The time constant in samples is N samples, and the actual time constant is N*Ts where Ts is the sample time.
This is probably the easiest to implement in code unless you don't mind using floating point.
What I've done in the past is use floating point to start getting things up and running, then switched to integer if I wanted it faster.

The second is the actual moving average where we drop the oldest sample and redo with the new sample. This requires a buffer so you have to have room in Ram. The averaging window is N samples.
Let's start with a buffer with some samples and N=3 and the oldest sample is the '3':
Buff=[1,2,3]
The average is (1+2+3)/3=2
Now the next sample comes in, let's say it is a 6. We drop the oldest value 3 and insert the new value 6:
Buff=[6,1,2]
The new average is (6+1+2)/3=3
When the next sample comes in the next sample to be dropped is the '2'.
This implementation requires shifting all the values to the next position in the buffer, but you can use a pointer instead so you only change one sample and update the pointer once.
The other implementation is to still keep an average value, but you'll know what value to subtract based on the sample pointed to by the pointer.
These methods are harder to implement though, and the N is limited unless you have a lot of Ram to spare.

There are 2nd order methods that work a little better but the ones above work good enough usually. By choosing N really large, like 4096 or even higher, we get a really nice smooth DC voltage, when we don't expect it to change suddenly. I did one version with N=65536.
 

Thread Starter

Hasan2019

Joined Sep 5, 2019
199
Hello again,

Yes that looks like it would work, using floating point.

If you want to use all integers there are a couple of choices.
The first was given by MrChips:
sum=sume+Vsample-Vavg
Vavg=sum/N
The time constant in samples is N samples, and the actual time constant is N*Ts where Ts is the sample time.
This is probably the easiest to implement in code unless you don't mind using floating point.
What I've done in the past is use floating point to start getting things up and running, then switched to integer if I wanted it faster.

The second is the actual moving average where we drop the oldest sample and redo with the new sample. This requires a buffer so you have to have room in Ram. The averaging window is N samples.
Let's start with a buffer with some samples and N=3 and the oldest sample is the '3':
Buff=[1,2,3]
The average is (1+2+3)/3=2
Now the next sample comes in, let's say it is a 6. We drop the oldest value 3 and insert the new value 6:
Buff=[6,1,2]
The new average is (6+1+2)/3=3
When the next sample comes in the next sample to be dropped is the '2'.
This implementation requires shifting all the values to the next position in the buffer, but you can use a pointer instead so you only change one sample and update the pointer once.
The other implementation is to still keep an average value, but you'll know what value to subtract based on the sample pointed to by the pointer.
These methods are harder to implement though, and the N is limited unless you have a lot of Ram to spare.

There are 2nd order methods that work a little better but the ones above work good enough usually. By choosing N really large, like 4096 or even higher, we get a really nice smooth DC voltage, when we don't expect it to change suddenly. I did one version with N=65536.
Thank you, I will read your comment and try to follow it. Perhaps it may be a good solution for post #212 but I have plan to add this circuit from post #224.
 

drjohsmith

Joined Dec 13, 2021
1,614
From what I can surmise, I think the TS is trying to build a voltage regulator for an AC generator.
I would simply use a step-down transformer, a single rectifier diode and a smoothing capacitor. There is no need to measure RMS.
not needing true rms , I agree with you @MrChips , but @Hasan2019 has repetebly come back to wanting / needing this,

@Hasan2019 , are we in agreement you dont need true rms now ?
 

drjohsmith

Joined Dec 13, 2021
1,614
I believed it from beginning but people in other forum did NOT agree to avoid true RMS, anyway professor Smith try to figure out the problem in post #212, #217
@Hasan2019
Can you clarify do you want or not true rns ?
If you could be clear on your intention , then the question will not come up again .

#212 , you posted a circuit and code
Can I confirm this is the way forward your taking ?
How are you getting on at testing the system ?
Do you mean #217 ? That is about this "magic" high value resistor power loss .
 

MrChips

Joined Oct 2, 2009
34,866
The simple explanation is we did not know the purpose for monitoring AC voltage.
Now that we know it is for AC voltage stabilizer, you do not need to measure true RMS. A running average of DC voltage is good enough.
 

Thread Starter

Hasan2019

Joined Sep 5, 2019
199
@Hasan2019
Can you clarify do you want or not true rns ?
If you could be clear on your intention , then the question will not come up again .

#212 , you posted a circuit and code
Can I confirm this is the way forward your taking ?
How are you getting on at testing the system ?
Do you mean #217 ? That is about this "magic" high value resistor power loss .
No, we don't need true RMS. I did not test in real system I told you. Only simulation result is presented.
 

Thread Starter

Hasan2019

Joined Sep 5, 2019
199
The simple explanation is we did not know the purpose for monitoring AC voltage.
Now that we know it is for AC voltage stabilizer, you do not need to measure true RMS. A running average of DC voltage is good enough.
You are right @MrChips. But the optocoupler/DIAC application were set for good peak to peak AC.
 

Thread Starter

Hasan2019

Joined Sep 5, 2019
199
You can convert from peak-to-peak to RMS with a simple multiplication by a constant factor.
Something like this code then?

C:
#include <LiquidCrystal.h>    // include Arduino LCD library

// LCD module connections (RS, E, D4, D5, D6, D7)
LiquidCrystal lcd(2, 3, 4, 5, 6, 7);

void setup(void) {
  lcd.begin(16, 2);           // set up the LCD's number of columns and rows
  lcd.setCursor(0, 0);
  lcd.print("RMS Voltage:");
  analogReference(INTERNAL);  // set ADC positive reference voltage to 1.1V (internal)
}

// get maximum reading value
uint16_t get_max() {
  uint16_t max_v = 0;
  for(uint8_t i = 0; i < 100; i++) {
    uint16_t r = analogRead(A3);  // read from analog channel 3 (A3)
    if(max_v < r) max_v = r;
    delayMicroseconds(200);
  }
  return max_v;
}

// main loop
void loop() {

  char buf[10];
  // get amplitude (maximum - or peak value)
  uint32_t v = get_max();
  // get actual voltage (ADC voltage reference = 1.1V)
  v = v * 1100/1023;
  // calculate the RMS value ( = peak/√2 )
  v /= sqrt(2);

  sprintf(buf, "%03u Volts", v);
  lcd.setCursor(0, 1);
  lcd.print(buf);

  delay(100);

}
 

panic mode

Joined Oct 10, 2011
5,013
I have another set of code where I can put my targeted AC values, have some loop for relays and corresponding ACvalues. I can see in this circuit Acvalues goes more than 580, that makes the problem. ....
you have switched controller several times (Arduino Uno, PIC, Arduino Nano, Proteus...). are you sure that ADC resolution is 1024 counts? because i know that even on Arduino Uno, there is a difference between Rev3 and Rev4. read datasheets - that is what they are for.

the crude but simple method is to get a multimeter, measure AC voltage (say you get 226VAC), then look at ADC raw value (say 580) and calculate scaling factor:

226/580=0.39

then your AC voltage is

float vac= 0.39 * analogRead(A3);
 
Last edited:
Top