I am building up an electricity monitor using the ACS712 hall effect sensor. I have tested and built the circuit having the Arduino (Nano) connected via USB to my laptop, and have tested/debugged etc., getting expected results.
I've migrated the circuit to be powered from an external 5v power supply via the VIN pin (details—this is not a "wallmart"-style supply). It is supplying a solid 5.01v across the circuit according to multimeter readings—I've tested at multiple points in the circuit. The 5v supply is rated at 700mA, so plenty of head room. The ACS712 is deriving it's power directly from the supply, not the Arduino 5v pin.
I'm running a "calibration" routine that takes readings from the ACS712 (which is measuring AC current) to find the "zero" point (where the AC signal crosses over), which should be the value of half VCC with this sensor—see ACS712 sensor reading for AC current for more detail/background on this. Usually this hovers around 512 (as expected—in the test unit I'm getting 547 when connected to an AC supply).
When I run/test this circuit with the USB connected, I get expected results. When I remove the USB, the calibration routine returns a much higher value (about 650 in the test units) and resultant readings are therefore incorrect. The laptop/test computer is not connected to a power supply during these tests.
When the USB is connected, there is no difference in reading across the VIN, 5V or sensor power pins, so the circuit seems to continue working from the external 5V source. However, I have noted that the 5v output pin on the Arduino drops to about 3.95v when run off the power supply alone, vs 4.7v when powered by USB/both.
When I repeat this test by connecting an external USB power supply to the Arduino USB port (i.e. no serial connection, and removing the laptop from the equation) I get the same results (a higher calibration value and strange readings).
I've read that the analog pin is referenced to the 5V voltage. This suggests that I should be connecting the ACS712 VCC pin to the Arduino 5V—is this the case? (Given the whole circuit is powered from the same power supply, this seemed unnecessary to me, but I'm still pretty new to this whole thing.) The ACS is very sensitive to input voltage fluctuations, so I assumed that I'd be better powering it directly and not relying on the Arduino.
Given that I'm supplying a 5v input, should I be switching from the VIN to the 5v pin? (something I've read you can do, and that the VIN should be for 7v+ supply input only).
Is there anything else that could be causing this discrepancy?
For reference, the calibration and read routines are reproduced below.
#define SENSOR_PIN A0
#define ACS_MVPA (100)
double _numSamples = 0;
double _ampsRMSSum = 0;
int16_t acs712Calibrate() {
uint8_t numReadings = 100;
int16_t vals[numReadings];
// collect a sample of readings
for (uint8_t i=0; i<numReadings; i++) {
vals[i] = constrain(analogRead(SENSOR_PIN), 0, 1023);
}
// Freq doesn't need to be 1024 wide, as the max val
// from the ACS712 will be limited subset.
// 20A = 100mV/A = 20*100mV/1000 = ~2v max voltage range
// 2v / 1024 = 0.0048828125v per increment = ~410 potential increment values
uint8_t freq[512]{0};
uint8_t offset = 250;
// count the frequency of specific readings
for (uint8_t i=0; i<numReadings; i++) {
freq[vals[i]-offset]++;
}
// work out the most frequent value
// note that if there is a tie, the lowest value will be used
uint8_t maxFreq = 0;
int16_t modeVal = -1;
for (int i=0; i<512; i++) {
if (freq[i] > maxFreq) {
maxFreq = freq[i];
modeVal = i;
}
}
modeVal = modeVal + offset;
return modeVal;
}
void avgRead() {
//debugSerial.println("_sample()");
// see https://arduino.stackexchange.com/questions/19301/acs712-sensor-reading-for-ac-current for description
int rVal = 0;
unsigned long rSquaredSum = 0;
// 50hz = 20 milliseconds per wavelength
// if we measure for 100ms we will get 5 cycles
// 60hz = 16.66667 milliseconds per wavelength, or just over ~6 cycles
// from what I can tell, either should be sufficient for our purposes
int sampleDuration = 100; // 100ms
// each logical "sample" is actually made up of a 100ms sample
// for our internal arithmatic, we need to track the number of internal samples
// not to be confused with _numSamples which tracks the number of times _sample()
// is called between _log()
int sampleCount = 0;
// take samples for 100ms
uint32_t startTime = millis();
while((millis()-startTime) < sampleDuration)
{
rVal = analogRead(SENSOR_PIN) - _config.acs712Zero;
rSquaredSum += rVal * rVal;
sampleCount++;
}
double voltRMS = 5.0 * sqrt(rSquaredSum/sampleCount)/1024.0;
// x 1000 to convert volts to millivolts
// divide by the number of millivolts per amp to determine amps measured
// in the case of the 20A component is 100 mv/A
double ampsRMS = voltRMS * (1000/ACS_MVPA);
_numSamples++; // update the overall sample count
// add this reading to the summed total(which is averaged before logging)
_ampsRMSSum += ampsRMS;
}