My energy monitoring project is complete but I'd like to improve it. Even though I was helped by generous forum members with the parts, printed values still aren't accurate and stable. To correct this I was suggested to average them.
Here is the printing sequence from the .cpp file of the Emon library:
The entire code is pretty large. Here's the 1st half:
/*
Emon.cpp - Library for openenergymonitor
Created by Trystan Lea, April 27 2010
GNU GPL
modified to use up to 12 bits ADC resolution (ex. Arduino Due)
by boredman@boredomprojects.net 26.12.2013
Low Pass filter for offset removal replaces HP filter 1/1/2015 - RW
*/
//#include "WProgram.h" un-comment for use on older versions of Arduino IDE
#include "EmonLib.h"
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
//--------------------------------------------------------------------------------------
// Sets the pins to be used for voltage and current sensors
//--------------------------------------------------------------------------------------
void EnergyMonitor::voltage(unsigned int _inPinV, double _VCAL, double _PHASECAL)
{
inPinV = _inPinV;
VCAL = _VCAL;
PHASECAL = _PHASECAL;
offsetV = ADC_COUNTS>>1;
}
void EnergyMonitor::current(unsigned int _inPinI, double _ICAL)
{
inPinI = _inPinI;
ICAL = _ICAL;
offsetI = ADC_COUNTS>>1;
}
//--------------------------------------------------------------------------------------
// Sets the pins to be used for voltage and current sensors based on emontx pin map
//--------------------------------------------------------------------------------------
void EnergyMonitor::voltageTX(double _VCAL, double _PHASECAL)
{
inPinV = 2;
VCAL = _VCAL;
PHASECAL = _PHASECAL;
offsetV = ADC_COUNTS>>1;
}
void EnergyMonitor::currentTX(unsigned int _channel, double _ICAL)
{
if (_channel == 1) inPinI = 3;
if (_channel == 2) inPinI = 0;
if (_channel == 3) inPinI = 1;
ICAL = _ICAL;
offsetI = ADC_COUNTS>>1;
}
//--------------------------------------------------------------------------------------
// emon_calc procedure
// Calculates realPower,apparentPower,powerFactor,Vrms,Irms,kWh increment
// From a sample window of the mains AC voltage and current.
// The Sample window length is defined by the number of half wavelengths or crossings we choose to measure.
//--------------------------------------------------------------------------------------
void EnergyMonitor::calcVI(unsigned int crossings, unsigned int timeout)
{
#if defined emonTxV3
int SupplyVoltage=3300;
#else
int SupplyVoltage = readVcc();
#endif
unsigned int crossCount = 0; //Used to measure number of times threshold is crossed.
unsigned int numberOfSamples = 0; //This is now incremented
//-------------------------------------------------------------------------------------------------------------------------
// 1) Waits for the waveform to be close to 'zero' (mid-scale adc) part in sin curve.
//-------------------------------------------------------------------------------------------------------------------------
boolean st=false; //an indicator to exit the while loop
unsigned long start = millis(); //millis()-start makes sure it doesnt get stuck in the loop if there is an error.
while(st==false) //the while loop...
{
startV = analogRead(inPinV); //using the voltage waveform
if ((startV < (ADC_COUNTS*0.55)) && (startV > (ADC_COUNTS*0.45))) st=true; //check its within range
if ((millis()-start)>timeout) st = true;
}
//-------------------------------------------------------------------------------------------------------------------------
// 2) Main measurement loop
//-------------------------------------------------------------------------------------------------------------------------
start = millis();
while ((crossCount < crossings) && ((millis()-start)<timeout))
{
numberOfSamples++; //Count number of times looped.
lastFilteredV = filteredV; //Used for delay/phase compensation
//-----------------------------------------------------------------------------
// A) Read in raw voltage and current samples
//-----------------------------------------------------------------------------
sampleV = analogRead(inPinV); //Read in raw voltage signal
sampleI = analogRead(inPinI); //Read in raw current signal
//-----------------------------------------------------------------------------
// B) Apply digital low pass filters to extract the 2.5 V or 1.65 V dc offset,
// then subtract this - signal is now centred on 0 counts.
//-----------------------------------------------------------------------------
offsetV = offsetV + ((sampleV-offsetV)/1024);
filteredV = sampleV - offsetV;
offsetI = offsetI + ((sampleI-offsetI)/1024);
filteredI = sampleI - offsetI;
//-----------------------------------------------------------------------------
// C) Root-mean-square method voltage
//-----------------------------------------------------------------------------
sqV= filteredV * filteredV; //1) square voltage values
sumV += sqV; //2) sum
//-----------------------------------------------------------------------------
// D) Root-mean-square method current
//-----------------------------------------------------------------------------
sqI = filteredI * filteredI; //1) square current values
sumI += sqI; //2) sum
//-----------------------------------------------------------------------------
cipri92:
I was suggested to sum up 10 values each second and average that sum. Problem is I don't know how to do that. Change the delay to 100ms and then what?
And take your readings and add them up as you go. When you've got ten readings added up, divide by 10 and you've got your average value. What part of that is complicated?
long previousMillis;
long currentMillis;
long interval=100;
float total=0;
int counter=0;
float adjusted_sample;
float sample=70;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
}
void loop() {
// put your main code here, to run repeatedly:
currentMillis = millis();
if(currentMillis - previousMillis > interval) {
total = total + sample;
counter++;
if (counter==10) {
adjusted_sample = (total/10);
total=0;
counter=0;}
previousMillis = currentMillis;}
Serial.print(sample);
Serial.print (" ");
Serial.print (total);
Serial.print (" ");
Serial.println (adjusted_sample);
}
just a old piece of code of I was playing with while messing with a temp sensor......down side of this is that it will not work if you have delays in the program as it requires the loop to be read every 10th of a second. (thinking about that it shouldn't matter other than the reading will not be updated every second)
Delta_G:
And take your readings and add them up as you go. When you've got ten readings added up, divide by 10 and you've got your average value. What part of that is complicated?
I don't know how the program detects 10 readings, how it knows when to sum up and print the average.
gpop1:
long previousMillis;
long currentMillis;
long interval=100;
float total=0;
int counter=0;
float adjusted_sample;
float sample=70;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
}
void loop() {
// put your main code here, to run repeatedly:
currentMillis = millis();
if(currentMillis - previousMillis > interval) {
total = total + sample;
counter++;
if (counter==10) {
adjusted_sample = (total/10);
total=0;
counter=0;}
previousMillis = currentMillis;}
just a old piece of code of I was playing with while messing with a temp sensor......down side of this is that it will not work if you have delays in the program as it requires the loop to be read every 10th of a second. (thinking about that it shouldn't matter other than the reading will not be updated every second)
Thanks, gpop1. As soon as I get a chance I'll update it.
cipri92:
I don't know how the program detects 10 readings, how it knows when to sum up and print the average.
You know how to use an int variable. Create two. One to hold the total and one to hold the number of samples. Each time you take a reading, add it to the one variable and add 1 to the other. When the one that is counting samples gets to 10, then you have taken ten readings. Set it back to zero and do your division with the other to get the average.