Photon + ACS712

Hello Guys!

This is my first ever post in the Particle community, I’m happy to be here :smile:
I’ve seen 2 more threads related to the ACS712 current sensor, but I have some questions that are a bit different from what is discussed in those threads, so I hope that the decision to open a new thread was a correct one.

I’m trying to use an ACS712 Hall-effect current sensor with a Photon. I have the +/-20A version of the current sensor, with a sensitivity of 100 mV/A.

  1. First of all, I’m trying to get a confirmation that it’s safe to use this sensor with the Photon. The Photon has a 12-bit ADC that works with 3.3V, meaning that the mid-point which corresponds to 0A current flow should be at 1.65V and should generate a digital reading of 2048. Although my current sensor can be used for currents of up to 20A, I know for a fact that in my system there will never be a current greater than 15A. So, based on the 100 mV/A sensitivity of the sensor, a 15A current could generate a voltage difference of 1.5V relative to the ADC’s mid-point of 1.65V, meaning that the ADC’s input voltage could vary between 0.15V (for -20A) and 3.15V (for +20A). Is this line of thoughts correct? Is it correct to assume that the 1.5V voltage difference generated by a 15A current should be calculated relative to the ADC’s mid-point of 1.65V and not relative to the current sensor’s midpoint of 2.5V? The current sensor needs 5V supply, which the Photon can provide (4.8V actually through VIN), but I’m afraid that this will mean that the current sensor’s output will be relative to 2.5V, not to 1.65V. In this case I see no other solution but to use a voltage divider between the current sensor’s output and the Photon’s ADC in order to not burn the Photon’s ADC… But that will result in resolution loss…
    I find it a bit controversial that the Photon has been designed with a 3.3V ADC since all Arduino-compatible sensors work with 5V and the Photon is advertised as highly Arduino-compatible…
    What is the practical way to use such 5V sensors with the Photon without loosing resolution?
    I have seen the 3.3V application of the ACS712 suggested by Peekay123. I’m just trying to understand wether I can avoid that complication with the voltage divider and the diode (I also have no idea why that diode is there…).

  2. Second of all, I’d like to verify that my idea of conecting the ACS712 sensor to the Photon is correct:
    ACS712 5V pin to Photon VIN pin (Photon powered through the micro-USB port)
    ACS712 GND pin to Photon GND pin
    ACS712 VOUT pin to Photon ? pin (not sure which Photon pin to use, perhaps A0?)

Thank you in advance for any useful info you may be able to give me!

@BazsoDombiAndras, the voltage you need to look at the the ACS712's output which is referenced to 2.5v. So with a +15A current, that means the voltage output will be 2.5v+1.5v = 4v which exceeds the maximum input voltage of 3.3v for the Photon. This is why you need a voltage divider between the ACS712 output and the Photon ADC input. BTW, that diode is not necessary but it provides a known voltage drop.

Actually, many of the Arduino boards like the Due run at 3.3v and have the same issues when dealing with analog inputs. For digital I/O, most of the Photon's pins are 5V tolerant but not the analog I/O. The ACS712 was not designed for Arduino, it simply uses a 5v supply. The output can be modified for any application using voltage dividers or op-amp divider/multiplier circuits.

In your case, going from a 5v range to a 3.3v range will mean some dynamic range loss. I this is an issue, you can always look to an external I2C or SPI analog-to-digital converter (eg MCP3002). :smile:

Thank you very much, peekay123! I had a feeling that you’re going to give me a useful answer :slight_smile: I’ll go for the voltage divider. Thank you! :slight_smile:

1 Like

please send me program

@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?

actually but I want to display my ACS 712 current value to dashboard so please help me with program

@armor your adcAjust represents a voltage offset in the sensor and could be caused by current leak or other factors (stray magnetic field).

Your code is not zero-crossing synchronized so sampling can start and finish at any point in the AC wave. With no TFT code, the delay(1800) seems to have provided enough synchronization to provide the correct full-wave sampling. However, with the tft code affecting your loop time, that delay needs to be adjusted I suspect. Have you tried modifying the value to look at the affect. You could easily write a Particle.function() to receive a delay value so you could tune without reflashing.

The BEST way to sample is to have a zero-cross detector but that requires extra hardware. :smile:

1 Like
# include <math.h>
int led1 = D7;

int led2 = D0;

float current=0;
const int currentPin = A0;
const unsigned long sampleTime = 100000UL;                           // sample over 100ms, it is an exact number of cycles for both 50Hz and 60Hz mains
const unsigned long numSamples = 100UL;                               // choose the number of samples to divide sampleTime exactly, but low enough for the ADC to keep up
const unsigned long sampleInterval = sampleTime/numSamples;  // the sampling interval, must be longer than then ADC conversion time
const int adc_zero = 2048;                                                     // relative digital zero of the arudino input from ACS712 (could make this a variable and auto-adjust it)

void setup()
{
pinMode(led1, OUTPUT);
   
pinMode(led2, OUTPUT);

  


   Spark.function("led",ledToggle);


 Serial.begin(9600);
}

void loop()
{

CurrentSense();
Serial.println(current);
delay(1000);
  Spark.publish("current", String(current) + "A"); 

}



int ledToggle(String command) 
{
   
    
if (command=="on")
 {
        
digitalWrite(led1,HIGH);
  
       digitalWrite(led2,LOW);
  
      Spark.publish("led","ON",60,PRIVATE);

        return 1;
  
  }

    else if (command=="off")
 {

        digitalWrite(led1,LOW);
   
      digitalWrite(led2,HIGH);

     Spark.publish("led","OFF",60,PRIVATE);
  
      return 1;

    }
    
else
 {
   
     return -1;
  
  }

}

void CurrentSense()
{
 unsigned long currentAcc = 0;
 unsigned int count = 0;
 unsigned long prevMicros = micros() - sampleInterval ;
 while (count < numSamples)
 {
   if (micros() - prevMicros >= sampleInterval)
   {
     int adc_raw = analogRead(currentPin) - adc_zero;
     currentAcc += (unsigned long)(adc_raw * adc_raw);
     ++count;
     prevMicros += sampleInterval;
   }
 }
 
 float rms = sqrt((float)currentAcc/(float)numSamples) * (50 / 4095.0);
rms=rms-0.10;
if (rms<0.20)
{
rms=0;
}

current=rms;
}

this is my program but its not working correctly

@peekay123

I will look into the adcAdjust causes - I believed it was due to the voltage range split - I am using ±5% tolerance resistors and hence with no current I get 1.62v at the A0 pin not 1.65v - this suggests 0.03/0.0008 or a 38 offset not the value I empirically arrived at to have centred readings.

Honestly, how important is zero-crossing synchronisation? I am taking 100 samples and sampling at 10 times the AC frequency. The more samples the better? The higher the frequency of sampling the better?
What I was trying to understand is why without the LCD on SPI the ADC works fine and with it doesn’t.

Why do you think the loop timing is impacting the ADC operation? It is taking the same number of samples.

It says in the documentation that if a ADC pin is set with pinMode() then it will give erratic output - does the getPinMode() = 0 prove that this is not the case? Particle do not provide much documentation/examples of this or setADCSampleTime() - does this work? It does not seem to have any impact.

Thank you for sharing the project. Could you please provide the schematic.

@mtun009

I do not have a schematic to hand for this project. The connections are documented in the code with const uint8_t. The other connections are:
ILI9341 uses hardware SPI A3-clock, A4-MISO, A5-MOSI, the tft LED backlight uses A7. The display is connected to Gnd and uses 5v Vin.

The ACS712 pwr is connected to 5v/Vin gnd to Gnd and S/output to A0.

Since my original post I have tried experimenting with various different connections to see if any change the results. I suspect that the PWM output used for the tft LED control is impacting the ADC results - the reasoning being that it operates at 500hz and there is some cross talk going on as well as harmonics between 1000hz sampling. The zero value I have tried amending the resistor bridge values to provide exactly 3.3 v at full 5v range - not yet tested. Trust that helps.

3 posts were merged into an existing topic: Not able to factory reset photon only blinking blue continusly

@peekay123

Is a possible cause of this offset 0.17V the fact there is a diode on the ACS712 module and it is giving a voltage drop on the output?

I am now trying with an I2CADC board to read the AC current - this appears to work but I still have not added the SPI driven screen which was when the odd readings started. Do you have any further ideas about what might impact the analogRead()? I would really prefer not to have this I2CADC as it requires an interrupt driven reading approach once the ADC is in a continuous conversion mode!

Thanks

@armor, do you have a schematic to share? What current range are you measuring? The input impedance of the Photon ADC will be affected by the sampling rate. Using an op-amp follower to buffer the output of the ACS712 into the ADC pin could help.

I am not replying with a schematic (I will get around to it) - Essentially, I am using a 5A range ACS712 on a purchased board with 3 pins - 1-5V (supplied from photon VIN), 2 - GND (connected to photon GND) and 3 - signal out. This is a pin who’s voltage can vary between 0 and 5V and with no AC current will set at 2.32V (I know it should be 2.5V but I understand there is a small voltage drop across the diode on the board. If I attach the signal out via a voltage splitting resistor bridge (to ensure the 5V is limited to 3.3V) to A0 and I run the output of a RMS calculation to Serial.print then it all works fine.

As soon as I try running an SPI display where the LED is a PWM output on A7, then the readings on A0 (and I have tried A1) go haywire. My only conclusion is that either the SPI or using the PWM output is affecting the ADC. Your thoughts?