@Peekay A quick question about using the ACS712 to measure AC current and with other devices connected to the analog pins. I have asked Particle Support and they have helpfully referred me to ask the Community. You seem to have your name on most of the AC712 threads!!
I have followed various threads, here and on arduino linked youtube demos of how to measure AC current. I tried out the following code with output to Serial port and straight off got a very decent result. The issue I have now come across is that I want to integrate the current sensing with other connected devices - but principally a TFT screen. As soon as I do that however, the ADC starts to give out pretty random answers. I am hoping you may be able to shed some light on why this is so.
Here is the basic setup, supply from mains on the left into the enclosure with a AC712 5A range module. Socket out is to a lamp holder with a 40W bulb. With 240V/50Hz that should use 0.167Amps.Output from the ACS712 is split with 1kohms and 2.2kohms to GND to give a voltage range of 0-3.3V on pin A0.
When I run the output from the RMS calculation to a Serial output with no other libraries, I get a reliable answer whatever load I put on the socket. The code is here:
#include "math.h"
/**
* ACS712 5A current sensor for AC current
* analogRead () reads the value from the specified analog pin.
* The device has 8 channels (A0 to A7) with a 12-bit resolution.
* This means that it will map input voltages between 0 and 3.3 volts
* into integer values between 0 and 4095. This yields a resolution
* between readings of: 3.3 volts / 4096 units or, 0.0008 volts (0.8 mV)
* per unit. The ACS712 output can be between 0 and 5V hence a resistor
* bridge is used to reduce the voltage from 0-5V to 0-3.3V.
* GND - 2.2kohms - A Pin - 1kohms- OUT(ACS712)
**/
#define ADCRANGE 4096
#define DEADBAND 0.02
const long adcZero = 2047;
const long adcAdjust = +65;
const float adcToCurrent = 70.0; // empirical
const unsigned long sampleTime = 100000UL; //100mS for 50Hz
const unsigned long numSamples = 100UL; //ADC must be able to run at this rate - every 1mS
const unsigned long sampleInterval = sampleTime/numSamples;
long readings[100];
float amps = 0;
float lastAmps = 0;
float maxAmps, minAmps, noiseAmps;
const uint8_t currPin = A0;
void setup()
{
Serial.begin(9600);
Serial.println("Setup AC712 Sensor");
delay(1000);
}
void loop()
{
Serial.printlnf("AC Current (Amps) %2.3f", sampleCurrent());
delay(1900);
}
float sampleCurrent()
{
unsigned long currentAcc = 0UL;
unsigned long count = 0UL;
unsigned long prevMicros = 0UL;
prevMicros = micros() - sampleInterval;
while (count < numSamples)
{
if ((micros() - prevMicros) >= sampleInterval)
{
long adcRaw = analogRead(currPin) + adcAdjust - adcZero; //order depends upon current flow
readings[count] = adcRaw;
currentAcc += (unsigned long) (adcRaw * adcRaw); //square the raw reading and add to sum of sample
count++;
prevMicros += sampleInterval; //prevMicros = micros(); ?
}
}
Serial.println("Logged adcRaw");
for (int i=0; i<100; i++) Serial.println(readings[i]);
Serial.printlnf("currentAcc %lu",currentAcc);
Serial.printlnf("numSamples %lu", numSamples);
float rms = (sqrt((float) currentAcc / (float) numSamples)) * (adcToCurrent/ADCRANGE);
if (rms < DEADBAND) rms = 0.0;
return rms;
}
A couple of observations on this code - I had to introduce an “adcAdjust” factor as the 0 amps readings were no sat in the middle of the range. This code reads 100 samples, one every 1 mS (so 20 samples per AC cycle). Squares them and sums them over the 100 samples then takes the mean and square root of that. This works great. However, once I add the TFT library and put the output to the TFT screen it all goes to pot. 0.167A becomes 0.257 with an odd sine wave and sometimes switches to the second screen with 0.142A.
This is the code
#include “Adafruit_mfGFX/Adafruit_mfGFX.h”
#include “Adafruit_ILI9341.h”
#include “math.h”
#define ADCRANGE 4096
#define DEADBAND 0.02 //the value under which current is set as zero
const uint8_t tftcs = A2;
const uint8_t dcrs = A1;
const uint8_t rst = D5;
const uint8_t tftLite = A7;
const uint8_t sdcs = A6;
const uint8_t currPin = A0;
const uint32_t ZBLUE = 0x0558;
const long adcZero = 2047; //assumed zero current reading is at mid range
const long adcAdjust = 99;
const float adcToCurrent = 48.0; // empirical
const unsigned long sampleTime = 100000UL; //100mS for both 50Hz
const unsigned long numSamples = 100UL; //ADC must be able to run at this rate - every 1mS
const unsigned long sampleInterval = sampleTime/numSamples;
long readings[100]; //array to store the adjusted raw analog reads
float amps;
Adafruit_ILI9341 tft = Adafruit_ILI9341(tftcs, dcrs, rst);
void setup()
{
tft.begin(); //need this at the start of the setup
Serial.begin(9600);
Serial.println("Adafruit 2.2\" SPI TFT with AC712 @ 11:48");
tft.setRotation(3);
pinMode(tftLite, OUTPUT);
analogWrite(tftLite, 200);
tft.setTextColor(ILI9341_BLACK);
tft.setTextSize(2);
Serial.printlnf("PinMode for currPin %i", getPinMode(currPin));
setADCSampleTime(ADC_SampleTime_112Cycles);
}
void loop()
{
int x, y, h, range;
int miny = 0;
int maxy = -2047;
tft.fillScreen(ILI9341_WHITE);
tft.setCursor(0, 0);
tft.printf("AC Current (Amps) %2.3f", sampleCurrent());
for (int i = 0; i < 100; i++)
{
miny = min(miny,readings[i]);
maxy = max(maxy,readings[i]);
}
range = maxy-miny/2;
for (int i = 0; i < 100; i++)
{
x = 60+i+i;
y = 120; //midline
h = 80*readings[i]/range;// line height
if (h >= 0) y = 120 - h; //start top
if (h < 0) h = -h; //length
tft.drawFastVLine(x, y, h, ZBLUE);
tft.drawFastVLine(x+1, y, h, ZBLUE);
}
tft.setCursor(1,220);
tft.print(" 0--------------100ms");
delay(1800);
}
float sampleCurrent()
{...}
I have used getPinMode() to check if the A0 pin is being set in some library - the return is 0. So OK. I have tried changing the ADC sample time - no difference.
One idea I had is that A7 is used as a PWM output to drive the TFT LED - could this be impacting A0 as an ADC?