Go Down

Topic: Averaging energy monitor values (Read 1 time) previous topic - next topic

cipri92

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:

Code: [Select]
void EnergyMonitor::serialprint()
{
    Serial.print(realPower);
    Serial.print(' ');
    Serial.print(apparentPower);
    Serial.print(' ');
    Serial.print(Vrms);
    Serial.print(' ');
    Serial.print(Irms);
    Serial.print(' ');
    Serial.print(powerFactor)*2.5;
    Serial.println(' ');
    delay(1000);
}


As you can see, the Serial Monitor will print out values every 1 second. What I want is to average 10 readings (1 read/100ms) every 1 second.

What is there to do?

luisilva

You must supply the entire code. You can change the way you show the values, but you must work on the way the values are acquired and stored.

cipri92

The entire code is pretty large. Here's the 1st half:

Code: [Select]
/*
  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

And the 2nd half:

Code: [Select]
    // E) Phase calibration
    //-----------------------------------------------------------------------------
    phaseShiftedV = lastFilteredV + PHASECAL * (filteredV - lastFilteredV);
   
    //-----------------------------------------------------------------------------
    // F) Instantaneous power calc
    //-----------------------------------------------------------------------------   
    instP = phaseShiftedV * filteredI;          //Instantaneous Power
    sumP +=instP;                               //Sum 
   
    //-----------------------------------------------------------------------------
    // G) Find the number of times the voltage has crossed the initial voltage
    //    - every 2 crosses we will have sampled 1 wavelength
    //    - so this method allows us to sample an integer number of half wavelengths which increases accuracy
    //-----------------------------------------------------------------------------       
    lastVCross = checkVCross;                     
    if (sampleV > startV) checkVCross = true;
                     else checkVCross = false;
    if (numberOfSamples==1) lastVCross = checkVCross;                 
                     
    if (lastVCross != checkVCross) crossCount++;
  }
 
  //-------------------------------------------------------------------------------------------------------------------------
  // 3) Post loop calculations
  //-------------------------------------------------------------------------------------------------------------------------
  //Calculation of the root of the mean of the voltage and current squared (rms)
  //Calibration coefficients applied.
 
  double V_RATIO = VCAL *((SupplyVoltage/1000.0) / (ADC_COUNTS));
  Vrms = V_RATIO * sqrt(sumV / numberOfSamples);
 
  double I_RATIO = ICAL *((SupplyVoltage/1000.0) / (ADC_COUNTS));
  Irms = I_RATIO * sqrt(sumI / numberOfSamples);

  //Calculation power values
  realPower = V_RATIO * I_RATIO * sumP / numberOfSamples;
  apparentPower = Vrms * Irms;
  powerFactor=realPower / apparentPower;

  //Reset accumulators
  sumV = 0;
  sumI = 0;
  sumP = 0;
//--------------------------------------------------------------------------------------       
}

//--------------------------------------------------------------------------------------
double EnergyMonitor::calcIrms(unsigned int Number_of_Samples)
{
 
   #if defined emonTxV3
 int SupplyVoltage=3300;
   #else
 int SupplyVoltage = readVcc();
   #endif

 
  for (unsigned int n = 0; n < Number_of_Samples; n++)
  {
    sampleI = analogRead(inPinI);

    // Digital low pass filter extracts the 2.5 V or 1.65 V dc offset,
 //  then subtract this - signal is now centered on 0 counts.
    offsetI = (offsetI + (sampleI-offsetI)/1024);
 filteredI = sampleI - offsetI;

    // Root-mean-square method current
    // 1) square current values
    sqI = filteredI * filteredI;
    // 2) sum
    sumI += sqI;
  }

  double I_RATIO = ICAL *((SupplyVoltage/1000.0) / (ADC_COUNTS));
  Irms = I_RATIO * sqrt(sumI / Number_of_Samples);

  //Reset accumulators
  sumI = 0;
//--------------------------------------------------------------------------------------       
 
  return Irms;
}

void EnergyMonitor::serialprint()
{
    Serial.print(realPower);
    Serial.print(' ');
    Serial.print(apparentPower);
    Serial.print(' ');
    Serial.print(Vrms);
    Serial.print(' ');
    Serial.print(Irms);
    Serial.print(' ');
    Serial.print(powerFactor)*2.5;
    Serial.println(' ');
    delay(1000);
}

//thanks to http://hacking.majenko.co.uk/making-accurate-adc-readings-on-arduino
//and Jérôme who alerted us to http://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/

long EnergyMonitor::readVcc() {
  long result;
 
  //not used on emonTx V3 - as Vcc is always 3.3V - eliminates bandgap error and need for calibration http://harizanov.com/2013/09/thoughts-on-avr-adc-accuracy/

  #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328__) || defined (__AVR_ATmega328P__)
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); 
  #elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_AT90USB1286__)
  ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  ADCSRB &= ~_BV(MUX5);   // Without this the function always returns -1 on the ATmega2560 http://openenergymonitor.org/emon/node/2253#comment-11432
  #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
  ADMUX = _BV(MUX5) | _BV(MUX0);
  #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
  ADMUX = _BV(MUX3) | _BV(MUX2);
 
  #endif


  #if defined(__AVR__)
  delay(2);                                        // Wait for Vref to settle
  ADCSRA |= _BV(ADSC);                             // Convert
  while (bit_is_set(ADCSRA,ADSC));
  result = ADCL;
  result |= ADCH<<8;
  result = READVCC_CALIBRATION_CONST / result;  //1100mV*1024 ADC steps http://openenergymonitor.org/emon/node/1186
  return result;
 #elif defined(__arm__)
  return (3300);                                  //Arduino Due
 #else
  return (3300);                                  //Guess that other un-supported architectures will be running a 3.3V!
 #endif
}

gpop1

look at smoothing to see if that will help

robtillaart

have a look at my runningAverage class on Github
- https://github.com/RobTillaart/Arduino/tree/master/libraries/RunningAverage -
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

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?

Delta_G

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?

|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

gpop1

#8
Jun 06, 2015, 09:53 pm Last Edit: Jun 06, 2015, 09:57 pm by gpop1
Code: [Select]
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)

cipri92

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.


Code: [Select]
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)
Thanks, gpop1. As soon as I get a chance I'll update it.

Delta_G

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. 
|| | ||| | || | ||  ~Woodstock

Please do not PM with technical questions or comments.  Keep Arduino stuff out on the boards where it belongs.

Go Up