Friday, 3 July 2015

How to use an ACS712 breakout board to measure current

One of my good friends and colleagues is a little busy at the moment...actually he is always busy!  He asked me to design and develop some instrumentation using the arduino Due development board. The requirements of the device are:
  • Measure electrical current from 0 to 5 Amps with at least 1 mA resolution with at least 0.1% accuracy.
  • Measure voltage from 0 Volts to 500 Volts with 1 mV resolution with at least 0.1% accuracy
  • Measure temperature from 0 °C to 200 °C with 0.5 °C resolution with at least 0.1% accuracy.
  • Log the data to a microSD card.
  • Display the measurements in real time on an LCD display.
  • Communicate using wifi to an external computer.
This is all fairly easy to achieve particularly as there are now plenty of modules and shields available to perform most of these functions.  For the wireless communications I'm going to use an ESP2866 wifi module.  I could also use a module for the microSD card but I might realise my own circuit as part of the printed circuit board that I intend to design.  The hardest part of the circuit to implement (in my opinion) is the analogue instrumentation.  The resolution requested is quite fine and the accuracy may be difficult to achieve.  

Let's discuss the current measurement section first.  We need a way of sensing electrical current flow in an external circuit that we attach to our data logger.  The sensing circuit shouldn't interfere with the thing we are trying to measure and should be as low power but accurate as possible.  The classic way to measure electrical current is to force the electrical current to pass through a known value resistor and then measure the voltage across that resistor and then using Ohm's law calculate the current. This is a very accurate and proven method.  The issues with it is that the circuit we are measuring is 'loaded' by the measurement or 'shunt' resistor which affects the external circuit slightly.  The shunt resistor needs to be a precision part which is unaffected by temperature.  That makes the 'shunt' resistor expensive.  Here is a simple shunt resistor current measurement circuit:
The circuit uses a 'difference amplifier' to measure the voltage developed across the shunt resistor. This voltage is then amplified and passed to an analogue to digital converter with is then in turn connected to a micro-controller and processed as required.  The circuit above provides a gain of 2. The issues with the above circuit is that the resistors must be their precise stated value and must not differ with temperature.  Any slight fluctuation in the supply voltage to the op amp or noise at the ground terminal will cause the measurement to be incorrect. 

Most difference amplifiers these days are made with precision resistors.  They can be bought as a single part with the circuit implemented within the silicon. The resistors are typically trimmed during manufacturing so that R2/R1 = R4/R3. The differential gain of the device can be calculated easily:

Av (Gain) = R2/R1 

The reference voltage (Vref) is added to the output voltage (Vo).

I have discussed difference amplifiers in a previous post and if more information is required please see that post - 

Another method of measuring current which is less invasive relies on the 'Hall Effect'.  The Hall Effect is basically a science trick using some gold leaf and some contacts; when electrical current is passed through gold leaf a voltage appears that is directly proportional to the amount of current flowing.  It is a product of the magnetic field induced around the gold leaf.  It was named for it's discoverer Edwin Hall in the year 1879 - an American Professor of Physics and very clever bloke!

Wikipedia Entry on the Hall Effect

Luckily I don't have to fiddle around with bits of glass and gold leaf in order to make Hall effect sensors.  They are now available helpfully pre-made inside integrated circuits.  The most prevalent manufacturer in my experience is Allegro.  For this circuit I'm going to use on of their most popular devices the ACS712.  The device is a 0-5A hall effect device in an SOIC package that works happily on 3.3V and 5V supplies and outputs a small voltage in proportion to the current present on it's input terminals.  This voltage can then be amplified or passed to an analogue to digital converter or micro-controller and processed and displayed.  The Allegro product page for the sensor is below:

ACS712 Product Page

The device itself can be bought from all good online electronics suppliers on it's own or in a handy little development module.  I bought mine from hobby components but the module or breakout board can be bought from any of the following vendors:

Amazon Online

Cool Components

Hobby Components - currently on sale at £2.99

All that is needed is to connect it up to your micro controller of choice and prepare to make current measurements!  Here is the schematic for the breakout board:

All that is required is to make the following connections: 

VCC to +5V or +3.3V
GND to 0V
Out to an analogue pin on the micro-controller.
JP1 and JP2 go in series with the load to be measured.

I'm just using this board to prove a concept and check things work.  The circuit I intend to implement is below:

The circuit works by measuring the current using the Hall effect sensor.  The voltage output of the hall effect sensor is passed to an inverting amplifer which provides a gain of 3.1 - the voltage from the current sensor will be multiplied by 3.  This amplified voltage is then passed to a 24 bit analogue to digital converter which then will be connected to the SPI communications pins of the micro-controller.  The result of the measurement can then be displayed or communicated as required.  

I chose to use a 24 bit ADC because the circuit requires very good measurement accuracy. It needs to be able to measure current with mA precision or better if possible. I don't say this often...lets do some maths!

According to the datasheet for the ACS712 typical provides 185 mV / A which means a voltage of 0.185 V is equivalent to 1 ampere of current flowing in the load.  With the amplification stage we will get 575 mV / A which gives a bit more resolution.  

The analogue to digital converter selected is of the 24 bit variety.  That means it converts the voltage presented at it's input to 24 figure binary number.  This binary number is then sent to the micro-controller using a device specific communications protocol called serial peripheral interface or SPI.     
A full scale current measurement is equivalent to 1111 1111 1111 1111 1111 1111(b) which in decimal is 16777215.  That means we can measure between 0 Amps and 5 Amps in 298 * 10^-9 steps which is brilliant accuracy.  Lets say 300 * 10^-9 or 300 nano-Amp per division accuracy.

The calculation was as follows:

5 A / 16777215 = 2.9802 e^-7      

If we convert this number to e-9 we get 298.02 e^-7 (move the decimal place to left by two figures) and then round the figure up to a whole number we have 300 e^-9 or 300 nano-Amps per division.

In reality I sincerely doubt this level of accuracy is possible without excellent components, printed circuit board material and component layout.  All of which we will aim to achieve but realistically we will probably achieve 22 bit accuracy.

So lets now repeat the calculation with the more realistic 22 bit accuracy and see what happens:

22 bits = 1111 1111 1111 1111 1111 11(b)

which in decimal is equivalent to 4194303

5 A /  4194303 = 1.19 e^-6 rounded up that is 1.2 e^-6 or 1.2 micro-Amps per division accuracy which is still awesome and almost a thousand times better than the 1 milli-Amp accuracy requested. It's important to try to improve on specifications where possible particularly when it doesn't impact cost and design.  

Enough talking, let see the the module working...The circuit I implemented for testing is below:

The circuit above uses a difference amplifier to subtract the 2.5V offset which is inherent in the ACS712 module - I didn't want the extra 2.5V present so I removed it.  The precision reference is present to ensure a stable measurement is made.  The output of the difference amplifier is then passed to an op-amp configured to provide 4.3 times gain which increases the resolution of the measurement. The output of the op-amp is then passed to the ADC input of an arduino R3 which has an integral 10 bit ADC.  Good enough to test the module.  I also added a 16x2 LCD display but I haven't shown this on the diagram as I ran out of space on the page...

Here is the code I'm using to test the circuit:

#include <LiquidCrystal.h>

char ch;
int Contrast = 100;
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

const int analogIn = A0;
int mVperAmp = 528; // use 100 for 20A Module and 66 for 30A Module
int RawValue = 0;
float ACSoffset = 450;
double Voltage = 0;
double Amps = 0;

// Define the number of samples to keep track of.  The higher the number,
// the more the readings will be smoothed, but the slower the output will
// respond to the input.  Using a constant rather than a normal variable lets
// use this value to determine the size of the readings array.

const int numReadings = 100;

int readings[numReadings];      // the readings from the analog input
int index = 0;                  // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average

void setup()
  Serial.println("Alex Current Meter Test");
  analogWrite(6, Contrast);
  // set up the LCD's number of columns and rows:
  analogWrite(9, 28836);
  // set the backlight of the LCD on
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("I Meter test!!");
  // initialize all the readings to 0: 
  for (int thisReading = 0; thisReading < numReadings; thisReading++)
  readings[thisReading] = 0;   

void loop()
  // subtract the last reading:
  total= total - readings[index];         
  // read from the sensor:  
  readings[index] = analogRead(analogIn); 
  // add the reading to the total:
  total= total + readings[index];       
  // advance to the next position in the array:  
  index = index + 1;                    

  // if we're at the end of the array...
  if (index >= numReadings)              
  // ...wrap around to the beginning: 
  index = 0;                           

  // calculate the average:
  average = total / numReadings;         
  RawValue = average;
  Voltage = (RawValue / 1023.0) * 5000; // convertaveraged raw value to mV 
  Amps = ((Voltage - ACSoffset)/ mVperAmp);

  Serial.print("Raw Value = " ); // shows pre-scaled value
  Serial.print("\t mV = "); // shows the voltage measured
  Serial.print(Voltage, 3); // Display 3 digits after decimal point
  Serial.print("\t Amps = "); // shows the voltage measured
  Serial.println(Amps, 3); // Display 3 digits after decimal point

  // set the cursor to column 0, line 1
  // (note: line 1 is the second row, since counting begins with 0):
  lcd.setCursor(0, 1);
  // print the calculated value of current:
  lcd.print("Current: ");
  lcd.print(Amps, 3);
  lcd.print("                ");

The code is fairly self explanatory, it initialises some variables, the LCD display and the serial communications.  The program then reads in 100 samples from the ADC, averages those values and then converts them from bits to milli-volts and then to Amps accounting for the offset and gain set in the circuit.  The amount of offset may vary between implementations.  Check the value by measuring how much voltage is present at the A0 pin when no current is present (should be around 400 mV)

Here is a video showing the circuit in operation

UPDATE - 01-12-2015

I found that the accuracy of the hall effect sensor was not great so I did some more research and watched some excellent youtube videos by Julian Illett (An excellent engineer)

The last video implemented a capacitor (C2 in the schematic) change from 1 nF to 470 nF.  I changed this on my board and found it had a significant improvement.  I also added some averaging to the code which had a little improvement.

That is all for now - take care, Langster!