Measure any AC current with ACS712 and Arduino + LCD / OLED
PLEASE BE CAREFUL IF YOU’RE USING THE POWER LINE!
Hello, and welcome to another tutorial, this one is about measuring Alternating Current (AC), using ACS712 I’m using the 30Amps version which is ACS712 30A, and our lovely Arduino Uno board, I tried to add an OLED screen but unfortunately ended up breaking it while shooting the tutorial so I switched to the LCD, but below you’ll find the wiring and codes for both versions.
And by “Any AC…” I mean that we gonna see a code/library that works for all signal types not only sinewaves one, and our Ammeter will be able to calculate the TRUE ROOT MEAN SQUARE (TRMS). And note that the sensor uses the Hall Effect (production of a voltage difference across an electrical conductor, transverse to an electric current in the conductor and to an applied magnetic field perpendicular to the current)
You can combine this project with this one: Easy measure of AC Voltage using Arduino and ZMPT101B
Tests will be done on an incandescent light bulb, controlled by a light dimmer in series with a reference multimeter and the ACS712 current sensor module.
Here’s the plan:
- First we need to interface our module with the Arduino board and check the signal shape when the dimmer is ON – Full Cycle – Half Cycle.
- Then we gonna check a simple code that doesn’t require any library, but it works only with Sinewaves signal (it will be tested).
- After that we gonna see the code that will measure the TRMS of the AC, and use the LCD.
Hardware and parts
Please be careful when choosing the ACS712, don’t try to play it safe like I did (by purchasing a 30Amps version), this will just give big fluctuation when you try to use it for a domestic use or small amps applications, so here for me a 5A or 10A version would have been fine.
Also note that 30A or 20A … will affect your calibrations too, but it’s explained how to calibrate in video and in codes comments.
Those are the parts I used, you can replace the UNO with any compatible board, or that LCD i²c with an OLED or any display you prefer.
Test 1
Wiring
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
/*This code works with ACS712 Current sensor, it permits to read the raw data It's better to use it with Serial Plotter More details on http://www.surtrtech.com */ #define Current_sensor A0 //The sensor analog input pin float i; void setup() { Serial.begin(9600); pinMode(Current_sensor, INPUT); } void loop() { i = analogRead(Current_sensor); Serial.println(i); delay(100); //Modifying or removing the delay will change the way the signal is shown //set it until you get the correct sinewave shap } |
Results
Upload the code and launch the serial plotter, you should be able to see a sinewave like signal, the red line shows when I turned down the dimmer and the current started having that shape that you usually find in a TRIAC.
Test 2
Now if you want to measure the current using the code above only, you’ll get values around 512 – 511, they don’t reflect the RMS value at all. So to measure the signal RMS value we gonna keep the same wiring as above but now we need another code that can calculate the RMS.
Code
Download code .ino here or check below.
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 |
/* This code works with ACS712 current sensor, it permits to calculate the RMS of a sinewave Alternating Current * it uses the Peak to Peak method to calculate the RMS * For more information check http://www.surtrtech.com */ #define SAMPLES 300 //Number of samples you want to take everytime you loop #define ACS_Pin A0 //ACS712 data pin analong input float High_peak,Low_peak; //Variables to measure or calculate float Amps_Peak_Peak, Amps_RMS; void setup() { Serial.begin(9600); pinMode(ACS_Pin,INPUT); //Define pin mode } void loop() { read_Amps(); //Launch the read_Amps function Amps_RMS = Amps_Peak_Peak*0.3536*0.06; //Now we have the peak to peak value normally the formula requires only multiplying times 0.3536 //but since the values will be very big you should multiply by 0.06, you can first not use it, //do your calculations and compare them to real values measured by an Ammeter. eg: 0.06=Real value/Measured value Serial.print(Amps_RMS); //Here I show the RMS value and the peak to peak value, you can print what you want and add the "A" symbol... Serial.print("\t"); Serial.println(Amps_Peak_Peak); delay(200); } void read_Amps() //read_Amps function calculate the difference between the high peak and low peak { //get peak to peak value int cnt; //Counter High_peak = 0; //We first assume that our high peak is equal to 0 and low peak is 1024, yes inverted Low_peak = 1024; for(cnt=0 ; cnt<SAMPLES ; cnt++) //everytime a sample (module value) is taken it will go through test { float ACS_Value = analogRead(ACS_Pin); //We read a single value from the module if(ACS_Value > High_peak) //If that value is higher than the high peak (at first is 0) { High_peak = ACS_Value; //The high peak will change from 0 to that value found } if(ACS_Value < Low_peak) //If that value is lower than the low peak (at first is 1024) { Low_peak = ACS_Value; //The low peak will change from 1024 to that value found } } //We keep looping until we take all samples and at the end we will have the high/low peaks values Amps_Peak_Peak = High_peak - Low_peak; //Calculate the difference } |
Results
As you can see the values on the left (RMS) are the same as the value measured by the multimeter, on the right those are Peak to Peak values, and note that the dimmer is at full cycle as I’m using 75W bulb and 230VAC/50Hz.
!!!!!!!!!!!!!!!! BUT !!!!!!!!!!!!!!!!!!!!!
Here I turned all the way down my light dimmer, which means I’m approximately around 1/4 of a cycle, the multimeter is giving me 0.21A but my code gives me 0.36A, the code still measures from Peak to Peak, but as you can see above my signal is not linear when I turn the dimmer all the way down.
And that’s the difference between a TRMS measuring and a measuring that works only with sinewaves signal.
Test 3
Now you see the problem of the code above, but fortunately there’s a library to the rescue, I really appreciate that library for all the work it does because as you know if you want to calculate a RMS value you need to use Integral Calculus, and that’s way difficult to achieve on an electronic development board, you can check the library .cpp file to see what methods are used like the “Average” “Sigma”… Things related to statistics/probabilities….
Library
Here you can download the Filters library: Download the library here.
Wiring
For the wiring we can keep everything the same as above.
Code
Download the code here or check below. The code contains a lot of comment to make it easy for you.
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 |
/* This code works with ACS712 current sensor, it permits the calculation of the signal TRMS * Visit http://www.surtrtech.com for more details */ #include <Filters.h> //This library does a massive work check it's .cpp file #define ACS_Pin A0 //Sensor data pin on A0 analog input float ACS_Value; //Here we keep the raw data valuess float testFrequency = 50; // test signal frequency (Hz) float windowLength = 40.0/testFrequency; // how long to average the signal, for statistist float intercept = 0; // to be adjusted based on calibration testing float slope = 0.0752; // to be adjusted based on calibration testing //Please check the ACS712 Tutorial video by SurtrTech to see how to get them because it depends on your sensor, or look below float Amps_TRMS; // estimated actual current in amps unsigned long printPeriod = 1000; // in milliseconds // Track time in milliseconds since last reading unsigned long previousMillis = 0; void setup() { Serial.begin( 9600 ); // Start the serial port pinMode(ACS_Pin,INPUT); //Define the pin mode } void loop() { RunningStatistics inputStats; // create statistics to look at the raw test signal inputStats.setWindowSecs( windowLength ); //Set the window length while( true ) { ACS_Value = analogRead(ACS_Pin); // read the analog in value: inputStats.input(ACS_Value); // log to Stats function if((unsigned long)(millis() - previousMillis) >= printPeriod) { //every second we do the calculation previousMillis = millis(); // update time Amps_TRMS = intercept + slope * inputStats.sigma(); Serial.print( "\t Amps: " ); Serial.print( Amps_TRMS ); } } } /* About the slope and intercept * First you need to know that all the TRMS calucations are done by functions from the library, it's the "inputStats.sigma()" value * At first you can display that "inputStats.sigma()" as your TRMS value, then try to measure using it when the input is 0.00A * If the measured value is 0 like I got you can keep the intercept as 0, otherwise you'll need to add or substract to make that value equal to 0 * In other words " remove the offset" * Then turn on the power to a known value, for example use a bulb or a led that ou know its power and you already know your voltage, so a little math you'll get the theoritical amps * you divide that theory value by the measured value and here you got the slope, now place them or modify them */ |
Results
The correct results are on the middle, the left ones are without calibration, and the right ones are used to demonstrate how to calibrate (check the video)
Turning down the light dimmer.
As you saw the huge difference between the two codes response to a non linear signal but the second one act as a TRMS multimeter.
Test 4
Here we keep the same things all we do is instead of using the serial monitor we can use LCD or OLED
Wirings
Wiring 1: Using LCD i2c screen
Wiring 2: Using OLED i2c screen
Libraries
- Filters library: Download the library here.
- OLED libraries:
- Adafruit OLED SSD1306 library: Download here from Github or Donwload here.
- Adafruit GFX Library Download here from Github or Download here.
- LCD i²c library you can Download it here.
Codes
Code 1: For LCD i2c version
Download the code for the LCD i²c version or check below.
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 |
/* This code works with ACS712 and LCD i²c * It measure the TRMS of an Alternating Current and displays the value on the screen * Visit http://www.SurtrTech.com for more details */ #include <Filters.h> //This library does a huge work check its .cpp file #include <LiquidCrystal_I2C.h> //LCD i²c library #define ACS_Pin A0 //ACS712 data pin #define I2C_ADDR 0x27 //I2C adress, you should use the code to scan the adress first (0x27) here #define BACKLIGHT_PIN 3 // Declaring LCD Pins #define En_pin 2 #define Rw_pin 1 #define Rs_pin 0 #define D4_pin 4 #define D5_pin 5 #define D6_pin 6 #define D7_pin 7 LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin); //Declaring the lcd float testFrequency = 50; // test signal frequency (Hz) float windowLength = 40.0/testFrequency; // how long to average the signal, for statistist float intercept = 0; // to be adjusted based on calibration testing float slope = 0.0752; // to be adjusted based on calibration testing //Please check the ACS712 Tutorial video by SurtrTech to see how to get them because it depends on your sensor, or look below float Amps_TRMS; float ACS_Value; unsigned long printPeriod = 1000; unsigned long previousMillis = 0; void setup() { digitalWrite(2,HIGH); lcd.begin (16,2); lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE); lcd.setBacklight(HIGH); //Lighting backlight lcd.home (); } void loop() { RunningStatistics inputStats; // create statistics to look at the raw test signal inputStats.setWindowSecs( windowLength ); while( true ) { ACS_Value = analogRead(ACS_Pin); // read the analog in value: inputStats.input(ACS_Value); // log to Stats function if((unsigned long)(millis() - previousMillis) >= printPeriod) { //every second we do the calculation previousMillis = millis(); // update time Amps_TRMS = intercept + slope * inputStats.sigma(); //Calibrate the values lcd.clear(); //clear the lcd and print in a certain position lcd.setCursor(2,0); lcd.print(Amps_TRMS); lcd.print(" A"); } } } /* About the slope and intercept * First you need to know that all the TRMS calucations are done by functions from the library, it's the "inputStats.sigma()" value * At first you can display that "inputStats.sigma()" as your TRMS value, then try to measure using it when the input is 0.00A * If the measured value is 0 like I got you can keep the intercept as 0, otherwise you'll need to add or substract to make that value equal to 0 * In other words " remove the offset" * Then turn on the power to a known value, for example use a bulb or a led that ou know its power and you already know your voltage, so a little math you'll get the theoritical amps * you divide that theory value by the measured value and here you got the slope, now place them or modify them */ |
Code 3: For the OLED version
Download the code for the OLED version or check below.
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 |
/* This code works with ACS712 and OLED i²c * It measure the TRMS of an Alternating Current and displays the value on the screen * Visit http://www.SurtrTech.com for more details */ #include <Filters.h> //This library does a huge work check its .cpp file #include <Adafruit_GFX.h> //OLED libraries #include <Adafruit_SSD1306.h> #define ACS_Pin A0 #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 32 // OLED display height, in pixels #define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) float testFrequency = 50; // test signal frequency (Hz) float windowLength = 40.0/testFrequency; // how long to average the signal, for statistist float intercept = 0; // to be adjusted based on calibration testing float slope = 0.0752; // to be adjusted based on calibration testing //Please check the ACS712 Tutorial video by SurtrTech to see how to get them because it depends on your sensor, or look below float Amps_TRMS; float ACS_Value; unsigned long printPeriod = 1000; unsigned long previousMillis = 0; Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); //Declaring the display name (display) void setup() { display.begin(SSD1306_SWITCHCAPVCC, 0x3C); //Start the OLED display display.clearDisplay(); display.display(); } void loop() { RunningStatistics inputStats; // create statistics to look at the raw test signal inputStats.setWindowSecs( windowLength ); while( true ) { ACS_Value = analogRead(ACS_Pin); // read the analog in value: inputStats.input(ACS_Value); // log to Stats function if((unsigned long)(millis() - previousMillis) >= printPeriod) { //Do the calculations every 1s previousMillis = millis(); // update time Amps_TRMS = intercept + slope * inputStats.sigma(); display.clearDisplay(); display.setTextSize(3); display.setTextColor(WHITE); display.setCursor(15,10); display.print(Amps_TRMS); display.println(" A"); display.display(); } } } /* About the slope and intercept * First you need to know that all the TRMS calucations are done by functions from the library, it's the "inputStats.sigma()" value * At first you can display that "inputStats.sigma()" as your TRMS value, then try to measure using it when the input is 0.00A * If the measured value is 0 like I got you can keep the intercept as 0, otherwise you'll need to add or substract to make that value equal to 0 * In other words " remove the offset" * Then turn on the power to a known value, for example use a bulb or a led that ou know its power and you already know your voltage, so a little math you'll get the theoritical amps * you divide that theory value by the measured value and here you got the slope, now place them or modify them */ |
Result
This is the only test I could do because I broke my OLED screen while trying to shoot the video tutorial 🙁
Overall this works the same way as we saw before it just displays it on a LCD i²c screen.
That’s all folks, I hope you like this tutorial don’t forget to like the video tutorial and subscribe to SurtrTech channel to show support, thank you very much.
Categories
Yassine View All
Automation and Electrical Engineer, Electronics amateur trying to share my little projects.
Nice bro..
So,what the Conclusion for test 4,
It’s precitions measurement?
Yes it is, it uses the same method as the one before, TRMS.
can we use this non linear value of current to calculate power factor for non linear loads?
I’m really not sure (Didn’t test yet), you can test and check with a reference device.
Can we have the Filters.cpp file? It isn’t there in the Filters Library !!
There is not .cpp file, all you need is already there.
Like you said in this video , i need to combine this code with your voltmeter code and make a watt meter (no need to consider power factor ) , I tried … , Successfully uploaded it to my arduino but the values i get is just too weird . I don’t know what I did wrong , i did change slope to Vslope for voltage sensor and Cslope for current sensor (intersept too). In short i am unable to combine these two programs into a working watt meter program , please help me as soon as possible
Hello, you should put two separate slope/intercept for each element to measure, also put two separate “RunningStatistics” elements with their own parameters and functions… you can do the same thing copy/paste and change the name, first measure the Voltage for example and store it, then call the second function to measure Current… you can obtain the (phi) angle by measuring the duration between the current and the voltage.
I’ve never did this before, you can try, just be careful.
i changed input stats to vinputStats and cinputStats for voltage and current respectively. now the program is working BUT, the while loop for voltage measurement comes inside the while loop for current measurement. so the program is revolving around the second while loop and not exiting to the main while loop.
in short, please tell me how to write the while loops for current and voltage measurement inside void loop.
this is the problem :
void loop() {
RunningStatistics CinputStats; // create statistics to look at the raw test signal
CinputStats.setWindowSecs( windowLength ); //Set the window length
while( true ) {
ACS_Value = analogRead(ACS_Pin); // read the analog in value:
CinputStats.input(ACS_Value); // log to Stats function
if((unsigned long)(millis() – previousMillis) >= printPeriod) { //every second we do the calculation
previousMillis = millis(); // update time
Amps_TRMS = Cintercept + Cslope * CinputStats.sigma();
Serial.println( ” Amps: ” );
Serial.println( Amps_TRMS );
Serial.println( ” ” );
lcd.setCursor(2,0);
lcd.print(Amps_TRMS);
lcd.setCursor(9,0);
lcd.print(“A”);
}
// }
/* This code works with ZMPT101B AC voltage sensor module and 128×32 OLED display
*/
RunningStatistics VinputStats; //Easy life lines, actual calculation of the RMS requires a load of coding
VinputStats.setWindowSecs( windowLength );
while( true ) {
Sensor = analogRead(A0); // read the analog in value:
VinputStats.input(Sensor); // log to Stats function
if((unsigned long)(millis() – previousMillis) >= printPeriod) {
previousMillis = millis(); // update time every second
Serial.print( “\n” );
current_Volts = Vintercept + Vslope * VinputStats.sigma(); //Calibartions for offset and amplitude
current_Volts= current_Volts*(40.3231); //Further calibrations for the amplitude
Serial.print( “\tVoltage: ” );
Serial.print( current_Volts ); //Calculation and Value display is done the rest is if you’re using an OLED display
lcd.setCursor(5,1);
lcd.print(current_Volts);
lcd.setCursor(11,1);
lcd.print(“V”);
}
}
}
}
It’s not exiting the first loop because of the while loop… please learn about loops in the Arduino references first.
As I told you, you better put each measuring in a function that the code will execute separately each time… maybe I used an example of the funtion with the ESP8266 and ZMPT101B, you can check the other article because this one is meant for measuring one current signal only.
Hello, thank you very much for this project. I need help with the filter library. Download the Filters library, but it shows an error when compiling. The type_traits library is missing, but I only find ArxTypeTraits and I can’t get it to work either.
In Helper.h I have changed #include to #include
And it keeps showing errors when compiling:
error: ‘RunningStatistics’ was not declared in this scope
error: ‘inputStats’ was not declared in this scope
Where do I find type_traits that works with this project?
Thank you
Diego.
Is there a way around the while(true) loop?
I’m using the code on a nodemcu and sending the data to firebase, if I include the firebase.setfloat command in the while loop wrong values are recorded.
Hello, please check my other tutorial about using the ZMPT101B with the NodeMcu, they are pretty much the same, you can check there how to put the measuring sequence into a function and get rid of the while loop… it’s easy.
https://surtrtech.com/2020/04/08/measure-any-ac-voltage-250vac-with-zmpt101b-and-esp8266-12e-with-android-app-adafruit-io-mqtt/
Thanks, it worked.
Good evening, I’m using your code I set slope and intercept to 0 and my output was 0, how do I get my intercept
I know it’s output/ expected but my output is 0
Hello, if you set the slope to 0 you’ll always get 0 (+intercept) as output, at first the intercept should be 0 and the slope at 1 to get the raw value then you can adjust as shown in the tutorial.
Kindly explain code to measure 3 phase current and give fault signal for phase unbalanced more than settable limit
Hi. I will use 4pcs of AC712 5A sensors. Some variable is named according to analog input. But how about inputStats.sigma() ? It is use for 4 differen sensor input?
Thanks for reply.
Hi, you could either use the same variables and you’ll have to switch between pins and storing every value alone, or you could declare other “RunningStatistics” entities…
How do we use these codes with 5A version or 20A version
Same thing, you’ll have different calibration values.
Hi! It sems that you use DC current switch in multimeter instead of AC current switch, Is that possible? I will do the same thing because there is no AC curent option in my multimeter. Thank you 🙂
Hi! It seems that you use DC current switch in multimeter instead of DC current switch. Is that possible? My multimeter (Cellkit CK83OL) has only DC current switch. I am afraid if I use it, it will break my multimeter. Thank you 🙂
Hi bro. Great Tutorial. The code works perfectly fine for me. My problem is this; I want to use 3 Current sensors at the same time, what do I need to change in the RunningStatistics? Or how else can I go about this. Hoping to hear from you soon.
Hi if I may ask what will the code be if you’re using 2.42 inch 7pin oled
Hi, where can I find LiquidCrystal_I2C.h used for this project, none of the standard libraries seem to work with this code. Thanks and good job with this project.
Hello
!! huge job!!
I would prefer to avoid studying the library so I have this question: the measurement interval of 1s is too long for my application, it would take three or four measurements per second.
Is changing only printPeriod completely stupid?
hello, my without calibartion value is 1.55, how do i calibrate it to zero? I cannot understand how to change the intercept value
bravo and many thanks. Works perfectly with ACS712s. And…
Good to know :
after many attempts to operate a ZMCT103C in AC, this is the only code that worked fine !