Add emon lib for a later non invasive current sensor.
This commit is contained in:
@@ -0,0 +1,259 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
//#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(int _inPinV, double _VCAL, double _PHASECAL)
|
||||||
|
{
|
||||||
|
inPinV = _inPinV;
|
||||||
|
VCAL = _VCAL;
|
||||||
|
PHASECAL = _PHASECAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnergyMonitor::current(int _inPinI, double _ICAL)
|
||||||
|
{
|
||||||
|
inPinI = _inPinI;
|
||||||
|
ICAL = _ICAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnergyMonitor::currentTX(int _channel, double _ICAL)
|
||||||
|
{
|
||||||
|
if (_channel == 1) inPinI = 3;
|
||||||
|
if (_channel == 2) inPinI = 0;
|
||||||
|
if (_channel == 3) inPinI = 1;
|
||||||
|
ICAL = _ICAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------
|
||||||
|
// 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(int crossings, int timeout)
|
||||||
|
{
|
||||||
|
#if defined emonTxV3
|
||||||
|
int SUPPLYVOLTAGE=3300;
|
||||||
|
#else
|
||||||
|
int SUPPLYVOLTAGE = readVcc();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int crossCount = 0; //Used to measure number of times threshold is crossed.
|
||||||
|
int numberOfSamples = 0; //This is now incremented
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------------
|
||||||
|
// 1) Waits for the waveform to be close to 'zero' (500 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/2+50)) && (startV > (ADC_COUNTS/2-50))) st=true; //check its within range
|
||||||
|
if ((millis()-start)>timeout) st = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------------
|
||||||
|
// 2) Main measurment loop
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------------
|
||||||
|
start = millis();
|
||||||
|
|
||||||
|
while ((crossCount < crossings) && ((millis()-start)<timeout))
|
||||||
|
{
|
||||||
|
numberOfSamples++; //Count number of times looped.
|
||||||
|
|
||||||
|
lastSampleV=sampleV; //Used for digital high pass filter
|
||||||
|
lastSampleI=sampleI; //Used for digital high pass filter
|
||||||
|
|
||||||
|
lastFilteredV = filteredV; //Used for offset removal
|
||||||
|
lastFilteredI = filteredI; //Used for offset removal
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// 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 high pass filters to remove 2.5V DC offset (centered on 0V).
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
filteredV = 0.996*(lastFilteredV+(sampleV-lastSampleV));
|
||||||
|
filteredI = 0.996*(lastFilteredI+(sampleI-lastSampleI));
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// 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
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// 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 coeficients 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(int NUMBER_OF_SAMPLES)
|
||||||
|
{
|
||||||
|
|
||||||
|
#if defined emonTxV3
|
||||||
|
int SUPPLYVOLTAGE=3300;
|
||||||
|
#else
|
||||||
|
int SUPPLYVOLTAGE = readVcc();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
for (int n = 0; n < NUMBER_OF_SAMPLES; n++)
|
||||||
|
{
|
||||||
|
lastSampleI = sampleI;
|
||||||
|
sampleI = analogRead(inPinI);
|
||||||
|
lastFilteredI = filteredI;
|
||||||
|
filteredI = 0.996*(lastFilteredI+sampleI-lastSampleI);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
Serial.println(' ');
|
||||||
|
delay(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
//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 = 1126400L / 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
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
Emon.h - 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EmonLib_h
|
||||||
|
#define EmonLib_h
|
||||||
|
|
||||||
|
#if defined(ARDUINO) && ARDUINO >= 100
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include "WProgram.h"
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// to enable 12-bit ADC resolution on Arduino Due,
|
||||||
|
// include the following line in main sketch inside setup() function:
|
||||||
|
// analogReadResolution(ADC_BITS);
|
||||||
|
// otherwise will default to 10 bits, as in regular Arduino-based boards.
|
||||||
|
#if defined(__arm__)
|
||||||
|
#define ADC_BITS 12
|
||||||
|
#else
|
||||||
|
#define ADC_BITS 10
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ADC_COUNTS (1<<ADC_BITS)
|
||||||
|
|
||||||
|
|
||||||
|
class EnergyMonitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
void voltage(int _inPinV, double _VCAL, double _PHASECAL);
|
||||||
|
void current(int _inPinI, double _ICAL);
|
||||||
|
|
||||||
|
void voltageTX(double _VCAL, double _PHASECAL);
|
||||||
|
void currentTX(int _channel, double _ICAL);
|
||||||
|
|
||||||
|
void calcVI(int crossings, int timeout);
|
||||||
|
double calcIrms(int NUMBER_OF_SAMPLES);
|
||||||
|
void serialprint();
|
||||||
|
|
||||||
|
long readVcc();
|
||||||
|
//Useful value variables
|
||||||
|
double realPower,
|
||||||
|
apparentPower,
|
||||||
|
powerFactor,
|
||||||
|
Vrms,
|
||||||
|
Irms;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
//Set Voltage and current input pins
|
||||||
|
int inPinV;
|
||||||
|
int inPinI;
|
||||||
|
//Calibration coeficients
|
||||||
|
//These need to be set in order to obtain accurate results
|
||||||
|
double VCAL;
|
||||||
|
double ICAL;
|
||||||
|
double PHASECAL;
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------
|
||||||
|
// Variable declaration for emon_calc procedure
|
||||||
|
//--------------------------------------------------------------------------------------
|
||||||
|
int lastSampleV,sampleV; //sample_ holds the raw analog read value, lastSample_ holds the last sample
|
||||||
|
int lastSampleI,sampleI;
|
||||||
|
|
||||||
|
double lastFilteredV,filteredV; //Filtered_ is the raw analog value minus the DC offset
|
||||||
|
double lastFilteredI, filteredI;
|
||||||
|
|
||||||
|
double phaseShiftedV; //Holds the calibrated phase shifted voltage.
|
||||||
|
|
||||||
|
double sqV,sumV,sqI,sumI,instP,sumP; //sq = squared, sum = Sum, inst = instantaneous
|
||||||
|
|
||||||
|
int startV; //Instantaneous voltage at start of sample window.
|
||||||
|
|
||||||
|
boolean lastVCross, checkVCross; //Used to measure number of times threshold is crossed.
|
||||||
|
int crossCount; // ''
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
_ _ _
|
||||||
|
| | (_) |
|
||||||
|
___ _ __ ___ ___ _ __ | | _| |__
|
||||||
|
/ _ \ '_ ` _ \ / _ \| '_ \| | | | '_ \
|
||||||
|
| __/ | | | | | (_) | | | | |____| | |_) |
|
||||||
|
\___|_| |_| |_|\___/|_| |_|______|_|_.__/
|
||||||
|
|
||||||
|
Arduino Energy Monitoring Library - compatible with Arduino 1.0
|
||||||
|
*****************************************************************
|
||||||
|
|
||||||
|
Designed for use with emonTx: http://openenergymonitor.org/emon/Modules
|
||||||
|
|
||||||
|
Download to Arduino IDE 'libraries' folder. Restart of IDE required.
|
||||||
|
|
||||||
|
Git Clone and Git Pull can be easily used to keep the library up-to-date and manage changes.
|
||||||
|
JeeLabs has done a good post on the topic: http://jeelabs.org/2011/12/29/out-with-the-old-in-with-the-new/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Update: 5th January 2014: Support Added for Arduino Due (ARM Cortex-M3, 12-bit ADC) by icboredman.
|
||||||
|
|
||||||
|
To enable this feature on Arduino Due, add the following statement to setup() function in main sketch:
|
||||||
|
analogReadResolution(ADC_BITS); This will set ADC_BITS to 12 (Arduino Due), EmonLib will otherwise default to 10 analogReadResolution(ADC_BITS);.
|
||||||
|
See blog post on using Arduino Due as energy monitor: http://boredomprojects.net/index.php/projects/home-energy-monitor
|
||||||
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// EmonLibrary examples openenergymonitor.org, Licence GNU GPL V3
|
||||||
|
|
||||||
|
#include "EmonLib.h" // Include Emon Library
|
||||||
|
EnergyMonitor emon1; // Create an instance
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
Serial.begin(9600);
|
||||||
|
|
||||||
|
emon1.current(1, 111.1); // Current: input pin, calibration.
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
double Irms = emon1.calcIrms(1480); // Calculate Irms only
|
||||||
|
|
||||||
|
Serial.print(Irms*230.0); // Apparent power
|
||||||
|
Serial.print(" ");
|
||||||
|
Serial.println(Irms); // Irms
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
// EmonLibrary examples openenergymonitor.org, Licence GNU GPL V3
|
||||||
|
|
||||||
|
#include "EmonLib.h" // Include Emon Library
|
||||||
|
EnergyMonitor emon1; // Create an instance
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
Serial.begin(9600);
|
||||||
|
|
||||||
|
emon1.voltage(2, 234.26, 1.7); // Voltage: input pin, calibration, phase_shift
|
||||||
|
emon1.current(1, 111.1); // Current: input pin, calibration.
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
emon1.calcVI(20,2000); // Calculate all. No.of half wavelengths (crossings), time-out
|
||||||
|
emon1.serialprint(); // Print out all variables (realpower, apparent power, Vrms, Irms, power factor)
|
||||||
|
|
||||||
|
float realPower = emon1.realPower; //extract Real Power into variable
|
||||||
|
float apparentPower = emon1.apparentPower; //extract Apparent Power into variable
|
||||||
|
float powerFActor = emon1.powerFactor; //extract Power Factor into Variable
|
||||||
|
float supplyVoltage = emon1.Vrms; //extract Vrms into Variable
|
||||||
|
float Irms = emon1.Irms; //extract Irms into Variable
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user