Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[SOLVED] ACS712-5A AC Current Reading - How to?

Status
Not open for further replies.

baileychic

Advanced Member level 3
Joined
Aug 2, 2017
Messages
728
Helped
56
Reputation
112
Reaction score
57
Trophy points
28
Activity points
7,033
ACS712-5A AC Current Reading - How to?

I am referring Application 4 on Page 12 of the datasheet and also referring Pafe 13.

Datasheet

https://www.allegromicro.com/~/media/files/datasheets/acs712-datasheet.ashx

I am using exact 1N4448W diode mentioned in the Application 4.

I don't want to use RF or R1.

I amusing a 100nF for C1.

I want to know how to get full scale reading of 0 to 5Amps.

I am using PIC12F1840's ADC pin to read the output of the sensor interface circuit.

I am using mikroC PRO PIC to write the code.
 

I am not sure what you are asking, I assume you know the nominal zero-current output voltage is VCC/2 (2.5V). The ACS712 is a 5V part as is the PIC, I don't see anything strange with the PIC's DAC so it should just work!
The circuit is designed to only respond to positive peaks and it will ignore negative current, maybe that's your problem ?
 

No, that is not my issue.

I don't want to measure negative current.

There is a Vf of the diode. How to compensate it to get 0 to 5Amps reading for 0 to 5Amps actual AC current?
 

As clearly stated there, it is a transformerless circuit designed to do the same job of a current transformer, so it has the same issue regarding the rectifying output diode.
 
I discarded application 4 as the compensation of diode Vf doesn't give correct and accurate readings.

Instead, I just measured AC current continuously.

Here is my code which works fine.

Code:
unsigned int max = 0;
unsigned int old_max = 0;
unsigned int min = 0;
unsigned int old_min = 0;
double volt = 0.0;
double current = 0.0;
char txt[23];

void main() {
    OSCCON = 0b01101011;
    OSCTUNE = 0x00;
    
    CM1CON0 = 0x00;
    TRISA = 0xFC;
    PORTA = 0x00;
    LATA = 0x00;
    
    ANSELA = 0x10;
    ADCON1 = 0b11010000;
    
    UART1_Init(9600);
    Delay_ms(200);
    
    while(1) {
           max = 0;
           old_max = 0;
           while(1) {
                max = (double)ADC_Read(3);
                if(old_max < max)old_max = max;
                else break;
           }
           
           min = 0;
           old_min = 1024;
           while(1) {
                min = (double)ADC_Read(3);
                if(old_min > min)old_min = min;
                else break;
           }
           
           volt = (double)(old_max - old_min) * 5.0 / 1023.0;
           volt = (volt  / 2.0) * 0.707;
           current = volt * 1000.0 / 185.0;
           FloatToStr(current, txt);
           Ltrim(txt);
           strcat(txt," Amps\r\n");
           UART1_Write_Text(txt);
           Delay_ms(300);
    }
}
 

The code should work, but measuring peak-to-peak current magnitude is sensitive to waveform distortions and noise and in so far the least accurate way to measure AC current.
 

@FvM

Then what is the best way to measure the AC current using ACS712-05T sensor module?
 

...moreover, with the above code it is being hold the maximum peak-to-peak, not updating with further smaller values.

what is the best way to measure the AC current using ACS712-05T sensor module?

You should compute the average value (sum divided by amount of samples); if you had mentioned the A/C rms value you would have searched the web for the mean square formula, but I am afraid you will ask this, so there it is:

rms.png
 

@andre_teprom

Yes, that was what I was thinking but it is processor time consuming even with integer arithmetic.

I need a faster method or how to speed up the processing of data in the array?


Should I take say 10 samples in 10ms = 20 samples in 20ms (10 max samples and 10 min samples) in a max[10] and min[10] arrays and then find out

max[0] - min[0] ... max[9] - min[9] which gives rawCurrentRaw[0]...rawCurrentRaw[9]

rawCurrentSquared = rawCurrentRaw[0]^2.... rawCurrentRaw[9]^2

and then

rawCurrentRawSquaredMean = rawCurrentRawSquared / 10

and then

rawCurrent = sqrt(rawCurrentRawSquaredMean)

???

But this is time consuming.

I need to read 10 channels and I am using ADG731 analog MUX between 10x ACS712-05T/20T/30T combination.

I need to trip individual load SSRs based on the channels current value. Each load channel will have a lower and upper threshold for current. If current for that channel is beyond the thresholds then SSR of that channel will be tripped in 3ms.
 

Hi,

Fast code:
What is the most time consuming with "RMS"? Usually the "square root" function.
Then omit it.

For example:
If you want to compare an RMS value with 7 (let's omit the unit "Amperes" for now)
Then "mathematically" square both sides:
--> Compare the "average of squares" (without square root) against 7x7 = 49.

Klaus
 

@Klaus

Okay, I will try them.

For now, I am trying to get 1 channel working but all I get is 0.00 as readings for all the channels continuously.

This is my code.

Find the attached mikroC PRO PIC project + Proteus 8.9 SP2 simulation file + circuit as PDF file. I am only simulating in Proteus as of now.

What needs to be fixed?


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
sbit Chip_Select           at LATC0_bit;
sbit Chip_Select_Direction at TRISC0_bit;
 
unsigned int adcVal = 0;
unsigned int rawCurrentValue[8] = {0};
unsigned int rawCurrentValueMax[8] = {0};
unsigned int rawCurrentValueMin[8] = {1024};
char i = 0;
char j = 0;
char txt[47];
unsigned char flagReg = 0;
 
sbit maxFlag at flagReg.B0;
sbit minFlag at flagReg.B1;
 
void Select_ADG731_Channel(unsigned char data_) {
     Chip_Select = 1;
     Delay_us(1);
     Chip_Select = 0;
     SPI1_Write(data_);
}
 
void interrupt() {
    if((ADIE_bit) && (ADIF_bit)) {
        ADIF_bit = 0;
        adcVal = (unsigned int)((unsigned int)(ADRESH << 8) + ADRESL);
 
        if(rawCurrentValueMax[i] <= adcVal)rawCurrentValueMax[i] = adcVal;
        else maxFlag = 1;
 
        if(rawCurrentValueMin[i] >= adcVal)rawCurrentValueMin[i] = adcVal;
        else minFlag = 1;
 
        if(!((maxFlag) && (minFlag))) {
            GO_DONE_bit = 1;
        }
    }
    
    LATD7_bit = ~LATD7_bit;
}
 
void main() {
    CM1CON0 = 0x00;
    CM2CON0 = 0x00;
    
    SLRCON = 0x00;
    
    ADCON1 = 0x00;
    ADCON2 = 0b10110101;
    
    ANSELA = 0x01;
    ANSELB = 0x00;
    ANSELC = 0x00;
    ANSELD = 0x00;
    ANSELE = 0x00;
 
    TRISA = 0x01;
    TRISB = 0x00;
    TRISC = 0xC0;
    TRISD = 0x00;
    TRISE = 0x00;
    
    PORTA = 0x00;
    PORTB = 0x00;
    PORTC = 0x00;
    PORTD = 0x00;
    PORTE = 0x00;
    
    LATA = 0x00;
    LATB = 0x00;
    LATC = 0x00;
    LATD = 0x00;
    LATE = 0x00;
    
    UART1_Init(9600);
 
    Chip_Select = 1;
    SPI1_Init_Advanced(_SPI_MASTER_OSC_DIV4, _SPI_DATA_SAMPLE_MIDDLE, _SPI_CLK_IDLE_LOW, _SPI_LOW_2_HIGH);
    Delay_ms(100);
    
    ADC_Init_Advanced(_ADC_INTERNAL_REF);
    ADIE_bit = 1;
    UART1_Write_Text("Taking readings...\r\n\r\n");
    INTCON |= 0xC0;
    Select_ADG731_Channel(0);
    GO_DONE_bit = 1;
    
    while(1) {
         IntToStr(adcVal,txt);
         UART1_Write_Text(txt);
         
         if((maxFlag) && (minFlag)) {
            for(j = 0; j < 8; j++) {
                rawCurrentValue[j] = rawCurrentValueMax[j] - rawCurrentValueMin[j];
                sprintf(txt, "Channel [%d] = %5.2f Amps\r\n", j, (double)rawCurrentValue[j]);
                UART1_Write_Text(txt);
            }
            
            UART1_Write_Text("\r\n");
            
            memset(rawCurrentValueMax, 0, sizeof(rawCurrentValueMax));
            memset(rawCurrentValueMin, 1024, sizeof(rawCurrentValueMin));
            
            maxFlag = 0;
            minFlag = 0;
            
            GO_DONE_bit = 1;
         }
    }
}



- - - Updated - - -

Sorry, wrong file posted in the previous post.

Consider the file attached here as the latest one. SPMM-Test-Rev-1.1.rar
 

Attachments

  • SPMM TEST-Rev-1.0.rar
    163.2 KB · Views: 133
  • SPMM-TEST-1.png
    SPMM-TEST-1.png
    191.5 KB · Views: 203
  • SPMM-Test-Rev-1.1.rar
    165.1 KB · Views: 134
Last edited:

taking the peak in any 25mS period ( pk - pk divided by two ) should give accurate results for the peak current - a small RC filter on the signal before the A/D should get rid of the worst of the noise+spikes

You can only assume rms = 0.707 * Vpk if the current is a good sine wave - often it is not

for accuracy you need to sample at least 10 times per half cycle and compute the RMS as you go, i.e. the SQRT of the((added squared samples) / 10mS)

for 10 samples in the half period ( 10mS ) for every half period - and then take a running average of say the last 100 half periods to get an update every second on the display or whatever

EP
 

I will update the display once per second only.

The relays if needed to be tripped has to be within 3ms after getting the rawCurrentValues.

I will try both pk-to-pk method and rms method with integer only calculations. Whichever works best with the hardware for different known loads, I will use that code.

The current issue is not able to get a non-zero value for rawCurrentMax[] and rawCurrentMin[].

Code:
sbit Chip_Select           at LATC0_bit;
sbit Chip_Select_Direction at TRISC0_bit;

unsigned int adcVal = 0;
unsigned int rawCurrentValue[8] = {0};
unsigned int rawCurrentValueMax[8] = {0};
unsigned int rawCurrentValueMin[8] = {1024};
char i = 0;
char j = 0;
char txt[47];
unsigned char flagReg = 0;

sbit maxFlag at flagReg.B0;
sbit minFlag at flagReg.B1;

void Select_ADG731_Channel(unsigned char data_) {
     Chip_Select = 1;
     Delay_us(1);
     Chip_Select = 0;
     SPI1_Write(data_);
}

void interrupt() {
    if((ADIE_bit) && (ADIF_bit)) {
        ADIF_bit = 0;
        adcVal = (unsigned int)((unsigned int)(ADRESH << 8) + ADRESL);

        if(rawCurrentValueMax[i] <= adcVal)rawCurrentValueMax[i] = adcVal;
        else maxFlag = 1;

        if(rawCurrentValueMin[i] >= adcVal)rawCurrentValueMin[i] = adcVal;
        else minFlag = 1;

        if(!((maxFlag) && (minFlag))) {
            GO_DONE_bit = 1;
        }
    }
    
    LATD7_bit = ~LATD7_bit;
}

void main() {
    CM1CON0 = 0x00;
    CM2CON0 = 0x00;
    
    SLRCON = 0x00;
    
    ADCON1 = 0x00;
    ADCON2 = 0b10110101;
    
    ANSELA = 0x01;
    ANSELB = 0x00;
    ANSELC = 0x00;
    ANSELD = 0x00;
    ANSELE = 0x00;

    TRISA = 0x01;
    TRISB = 0x00;
    TRISC = 0xC0;
    TRISD = 0x00;
    TRISE = 0x00;
    
    PORTA = 0x00;
    PORTB = 0x00;
    PORTC = 0x00;
    PORTD = 0x00;
    PORTE = 0x00;
    
    LATA = 0x00;
    LATB = 0x00;
    LATC = 0x00;
    LATD = 0x00;
    LATE = 0x00;
    
    UART1_Init(9600);

    Chip_Select = 1;
    SPI1_Init_Advanced(_SPI_MASTER_OSC_DIV4, _SPI_DATA_SAMPLE_MIDDLE, _SPI_CLK_IDLE_LOW, _SPI_LOW_2_HIGH);
    Delay_ms(100);
    
    ADC_Init_Advanced(_ADC_INTERNAL_REF);
    ADIE_bit = 1;
    UART1_Write_Text("Taking readings...\r\n\r\n");
    INTCON |= 0xC0;
    Select_ADG731_Channel(0);
    GO_DONE_bit = 1;
    
    while(1) {
         sprintf(txt, "adcVal = %d\r\n\r\n", adcVal);
         UART1_Write_Text(txt);
         
         if((maxFlag) && (minFlag)) {
            for(j = 0; j < 8; j++) {
                rawCurrentValue[j] = rawCurrentValueMax[j] - rawCurrentValueMin[j];
                sprintf(txt, "Channel [%d] = %5.2f Amps\r\n", j, (double)rawCurrentValue[j]);
                UART1_Write_Text(txt);
            }
            
            UART1_Write_Text("\r\n");
            
            memset(rawCurrentValueMax, 0, sizeof(rawCurrentValueMax));
            memset(rawCurrentValueMin, 1024, sizeof(rawCurrentValueMin));
            
            maxFlag = 0;
            minFlag = 0;
            
            GO_DONE_bit = 1;
         }
    }
}

What is wrong?
 

hello Ozzie, sorry reading code is not my long suit - I write the algorithms that are then coded by others ( less **** ups that way ) keep reading and I'm sure whatever is sticking/keeping zero in the variables / registers will shine thru ...
 

Hi,

Should I take say 10 samples in 10ms = 20 samples in 20ms
If current for that channel is beyond the thresholds then SSR of that channel will be tripped in 3ms.
A rather curious requirement.
The measurement may take 10ms ... or 20ms --> a bit more than 3ms tolerance
The SSR switching time maybe is 10ms or longer --> longer than 3ms
At 50Hz it takes 10ms from one peak to the other --> longer than 3ms
******
A single peak value:
Consider that some appliance nearby will cause some switching noise in mains... (let's say a refrigerator) ...it will cause some voltage noise and for sure some current noise. Mainly wired noise, but you also need to consider radiated noise.
Additionally thunderstorms will cause noise...and other unexpected sources of noise.
Thus you need to consider uncontrolled peaks in your measurement values.

I'm designing industrial equippment ... using algorithms to detect overcurrent...then in case of overcurrent we have to switch OFF.
In one plant there are chemical processes, switching OFF the power supply makes the production batch to be lost. A loss of 40,000Euro.
You recognice that the company does not want "erroneous switch OFF".
One industrial standard (algorithm) is via I^2t.. trip times that depend on the value of overcurrent.
I've never seen an industrial requirement for "3ms".

Now maybe you have very sensitive parts, that really require that fast switch OFF time .... then for sure you must not use an SSR, because it is much slower.
I hope you understand my worries. In my eyes the requirements do not make sense.

Klaus
 

I discarded application 4 as the compensation of diode Vf doesn't give correct and accurate readings.

Instead, I just measured AC current continuously.

Here is my code which works fine.

Code:
unsigned int max = 0;
unsigned int old_max = 0;
unsigned int min = 0;
unsigned int old_min = 0;
double volt = 0.0;
double current = 0.0;
char txt[23];

void main() {
    OSCCON = 0b01101011;
    OSCTUNE = 0x00;
    
    CM1CON0 = 0x00;
    TRISA = 0xFC;
    PORTA = 0x00;
    LATA = 0x00;
    
    ANSELA = 0x10;
    ADCON1 = 0b11010000;
    
    UART1_Init(9600);
    Delay_ms(200);
    
    while(1) {
           max = 0;
           old_max = 0;
           while(1) {
                max = (double)ADC_Read(3);
                if(old_max < max)old_max = max;
                else break;
           }
           
           min = 0;
           old_min = 1024;
           while(1) {
                min = (double)ADC_Read(3);
                if(old_min > min)old_min = min;
                else break;
           }
           
           volt = (double)(old_max - old_min) * 5.0 / 1023.0;
           volt = (volt  / 2.0) * 0.707;
           current = volt * 1000.0 / 185.0;
           FloatToStr(current, txt);
           Ltrim(txt);
           strcat(txt," Amps\r\n");
           UART1_Write_Text(txt);
           Delay_ms(300);
    }
}

i am not sure your code gcould gives good result because
Code:
unsigned int max = 0;
unsigned int old_max = 0;
unsigned int min = 0;
unsigned int old_min = 0;



max = (double)ADC_Read(3);  ?
 
min = (double)ADC_Read(3);  ?


you must declare all variables in double format .
Compiler can not tanspose a double into an int ...
 
I tried to implement a non-interrupt based code for 8x channels and it somewhat works.

There are some glitches that are some channels values fluctuate a little between two values and some random channels values go beyond the range like 6xx.xx Amps.

This is the latest code and the project and its result screenshot are attached here.


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
sbit Chip_Select           at LATC0_bit;
sbit Chip_Select_Direction at TRISC0_bit;
 
unsigned int max[8] = {0};
unsigned int old_max[8] = {0};
unsigned int min[8] = {0};
unsigned int old_min[8] = {0};
double volt = 0.0;
double current = 0.0;
unsigned int i = 0;
char txt[67];
 
void Select_ADG731_Channel(unsigned char data_) {
     Chip_Select = 0;
     SPI1_Write(data_);
     Chip_Select = 1;
}
 
void Clear_Arrays() {
    memset(max, 0, sizeof(max));
    memset(min, 0, sizeof(min));
    memset(old_max, 0, sizeof(old_max));
    memset(old_min, 0, sizeof(old_min));
}
 
void main() {
    CM1CON0 = 0x00;
    CM2CON0 = 0x00;
 
    SLRCON = 0x00;
 
    ADCON0 = 0x00;
    ADCON1 = 0x00;
    ADCON2 = 0b10110101;
 
    ANSELA = 0x01;
    ANSELB = 0x00;
    ANSELC = 0x00;
    ANSELD = 0x00;
    ANSELE = 0x00;
 
    TRISA = 0xC1;
    TRISB = 0x00;
    TRISC = 0xC0;
    TRISD = 0x00;
    TRISE = 0x00;
 
    PORTA = 0x00;
    PORTB = 0x00;
    PORTC = 0x00;
    PORTD = 0x00;
    PORTE = 0x00;
 
    LATA = 0x00;
    LATB = 0x00;
    LATC = 0x00;
    LATD = 0x00;
    LATE = 0x00;
 
    UART1_Init(9600);
 
    Chip_Select = 1;
    Chip_Select_Direction = 0;
    SPI1_Init_Advanced(_SPI_MASTER_OSC_DIV16, _SPI_DATA_SAMPLE_MIDDLE, _SPI_CLK_IDLE_LOW, _SPI_HIGH_2_LOW);
    Delay_ms(100);
    
    UART1_Write_Text("Taking readings...\r\n\r\n");
    Clear_Arrays();
 
    while(1) {
           i = 0;
           while(i < 8) {
               Select_ADG731_Channel(i);
               Delay_us(50);
               
               max[i] = 0;
               old_max[i] = 0;
               while(1) {
                    max[i] = ADC_Read(0);
                    Delay_us(10);
                    if(old_max[i] < max[i])old_max[i] = max[i];
                    else break;
               }
               
               min[i] = 0;
               old_min[i] = 1024;
               while(1) {
                    min[i] = ADC_Read(0);
                    Delay_us(10);
                    if(old_min[i] > min[i])old_min[i] = min[i];
                    else break;
               }
               
               i++;
           }
 
           for(i = 0; i < 8; i++) {
               volt = (double)(old_max[i] - old_min[i]) * 5.0 / 1023.0;
               volt = (volt / 2.0) * 0.707;
               current = volt * 1000.0 / 185.0;
               sprintf(txt, "Channel [%u] = %5.2f Amps\r\n", i, current);
               UART1_Write_Text(txt);
           }
           
           UART1_Write_Text("\r\n");
           Clear_Arrays();
    }
}

 

Attachments

  • SPMM-Test-Rev-1.2.rar
    135.1 KB · Views: 130
  • SMPP-TEST-Rev-1.2-1.png
    SMPP-TEST-Rev-1.2-1.png
    168.3 KB · Views: 207

Hi,

I tried to implement a non-interrupt based code
I don´t understand why "non interrupt" solution.

In my eyes this is a classical application for interrupt based processing.
I personally like the interrupt solution, because I find it much easier to keep timings, it gives more precise results and it leaves maybe more than 80% of your processing power for the main loop.
.. and I don´t find it difficult to implement.

A solution with running average or biquad filtering means just a couple of MULTIPLY / ADD / SUBTRACT ... and the result will be reliable and stable. And you may get the "overcurrent" result after each conversion...(not after 8 conversions or after 10ms or after 20ms.. or whatever you decide)
You do the processing of one channel while the conversion of the next channel is running.

Klaus
 

What should be the minimum delay between switching channels of ADG731?

Currently, in the new code, I am using 1us.

I am now trying to implement the code using interrupts and want to read 8x channels min in 10ms and max in 10ms.

I am thinking about using only the ADC interrupt.

I will first implement only the pk-to-pk method. If that works fine then I will implement the RMS method using interrupts.

This way the reading + processing should not exceed 20ms + 10ms (64 MHz Oscillator) and I will be able to trip the faulty load line within 30ms of fault.
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top