Skip to content

Commit 5d43706

Browse files
committed
Add emon lib for a later non invasive current sensor.
1 parent 4261233 commit 5d43706

File tree

5 files changed

+417
-0
lines changed

5 files changed

+417
-0
lines changed

libraries/EmonLib/EmonLib.cpp

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
/*
2+
Emon.cpp - Library for openenergymonitor
3+
Created by Trystan Lea, April 27 2010
4+
GNU GPL
5+
modified to use up to 12 bits ADC resolution (ex. Arduino Due)
6+
by [email protected] 26.12.2013
7+
*/
8+
9+
//#include "WProgram.h" un-comment for use on older versions of Arduino IDE
10+
#include "EmonLib.h"
11+
12+
#if defined(ARDUINO) && ARDUINO >= 100
13+
14+
#include "Arduino.h"
15+
16+
#else
17+
18+
#include "WProgram.h"
19+
20+
#endif
21+
22+
//--------------------------------------------------------------------------------------
23+
// Sets the pins to be used for voltage and current sensors
24+
//--------------------------------------------------------------------------------------
25+
void EnergyMonitor::voltage(int _inPinV, double _VCAL, double _PHASECAL)
26+
{
27+
inPinV = _inPinV;
28+
VCAL = _VCAL;
29+
PHASECAL = _PHASECAL;
30+
}
31+
32+
void EnergyMonitor::current(int _inPinI, double _ICAL)
33+
{
34+
inPinI = _inPinI;
35+
ICAL = _ICAL;
36+
}
37+
38+
//--------------------------------------------------------------------------------------
39+
// Sets the pins to be used for voltage and current sensors based on emontx pin map
40+
//--------------------------------------------------------------------------------------
41+
void EnergyMonitor::voltageTX(double _VCAL, double _PHASECAL)
42+
{
43+
inPinV = 2;
44+
VCAL = _VCAL;
45+
PHASECAL = _PHASECAL;
46+
}
47+
48+
void EnergyMonitor::currentTX(int _channel, double _ICAL)
49+
{
50+
if (_channel == 1) inPinI = 3;
51+
if (_channel == 2) inPinI = 0;
52+
if (_channel == 3) inPinI = 1;
53+
ICAL = _ICAL;
54+
}
55+
56+
//--------------------------------------------------------------------------------------
57+
// emon_calc procedure
58+
// Calculates realPower,apparentPower,powerFactor,Vrms,Irms,kwh increment
59+
// From a sample window of the mains AC voltage and current.
60+
// The Sample window length is defined by the number of half wavelengths or crossings we choose to measure.
61+
//--------------------------------------------------------------------------------------
62+
void EnergyMonitor::calcVI(int crossings, int timeout)
63+
{
64+
#if defined emonTxV3
65+
int SUPPLYVOLTAGE=3300;
66+
#else
67+
int SUPPLYVOLTAGE = readVcc();
68+
#endif
69+
70+
int crossCount = 0; //Used to measure number of times threshold is crossed.
71+
int numberOfSamples = 0; //This is now incremented
72+
73+
//-------------------------------------------------------------------------------------------------------------------------
74+
// 1) Waits for the waveform to be close to 'zero' (500 adc) part in sin curve.
75+
//-------------------------------------------------------------------------------------------------------------------------
76+
boolean st=false; //an indicator to exit the while loop
77+
78+
unsigned long start = millis(); //millis()-start makes sure it doesnt get stuck in the loop if there is an error.
79+
80+
while(st==false) //the while loop...
81+
{
82+
startV = analogRead(inPinV); //using the voltage waveform
83+
if ((startV < (ADC_COUNTS/2+50)) && (startV > (ADC_COUNTS/2-50))) st=true; //check its within range
84+
if ((millis()-start)>timeout) st = true;
85+
}
86+
87+
//-------------------------------------------------------------------------------------------------------------------------
88+
// 2) Main measurment loop
89+
//-------------------------------------------------------------------------------------------------------------------------
90+
start = millis();
91+
92+
while ((crossCount < crossings) && ((millis()-start)<timeout))
93+
{
94+
numberOfSamples++; //Count number of times looped.
95+
96+
lastSampleV=sampleV; //Used for digital high pass filter
97+
lastSampleI=sampleI; //Used for digital high pass filter
98+
99+
lastFilteredV = filteredV; //Used for offset removal
100+
lastFilteredI = filteredI; //Used for offset removal
101+
102+
//-----------------------------------------------------------------------------
103+
// A) Read in raw voltage and current samples
104+
//-----------------------------------------------------------------------------
105+
sampleV = analogRead(inPinV); //Read in raw voltage signal
106+
sampleI = analogRead(inPinI); //Read in raw current signal
107+
108+
//-----------------------------------------------------------------------------
109+
// B) Apply digital high pass filters to remove 2.5V DC offset (centered on 0V).
110+
//-----------------------------------------------------------------------------
111+
filteredV = 0.996*(lastFilteredV+(sampleV-lastSampleV));
112+
filteredI = 0.996*(lastFilteredI+(sampleI-lastSampleI));
113+
114+
//-----------------------------------------------------------------------------
115+
// C) Root-mean-square method voltage
116+
//-----------------------------------------------------------------------------
117+
sqV= filteredV * filteredV; //1) square voltage values
118+
sumV += sqV; //2) sum
119+
120+
//-----------------------------------------------------------------------------
121+
// D) Root-mean-square method current
122+
//-----------------------------------------------------------------------------
123+
sqI = filteredI * filteredI; //1) square current values
124+
sumI += sqI; //2) sum
125+
126+
//-----------------------------------------------------------------------------
127+
// E) Phase calibration
128+
//-----------------------------------------------------------------------------
129+
phaseShiftedV = lastFilteredV + PHASECAL * (filteredV - lastFilteredV);
130+
131+
//-----------------------------------------------------------------------------
132+
// F) Instantaneous power calc
133+
//-----------------------------------------------------------------------------
134+
instP = phaseShiftedV * filteredI; //Instantaneous Power
135+
sumP +=instP; //Sum
136+
137+
//-----------------------------------------------------------------------------
138+
// G) Find the number of times the voltage has crossed the initial voltage
139+
// - every 2 crosses we will have sampled 1 wavelength
140+
// - so this method allows us to sample an integer number of half wavelengths which increases accuracy
141+
//-----------------------------------------------------------------------------
142+
lastVCross = checkVCross;
143+
if (sampleV > startV) checkVCross = true;
144+
else checkVCross = false;
145+
if (numberOfSamples==1) lastVCross = checkVCross;
146+
147+
if (lastVCross != checkVCross) crossCount++;
148+
}
149+
150+
//-------------------------------------------------------------------------------------------------------------------------
151+
// 3) Post loop calculations
152+
//-------------------------------------------------------------------------------------------------------------------------
153+
//Calculation of the root of the mean of the voltage and current squared (rms)
154+
//Calibration coeficients applied.
155+
156+
double V_RATIO = VCAL *((SUPPLYVOLTAGE/1000.0) / (ADC_COUNTS));
157+
Vrms = V_RATIO * sqrt(sumV / numberOfSamples);
158+
159+
double I_RATIO = ICAL *((SUPPLYVOLTAGE/1000.0) / (ADC_COUNTS));
160+
Irms = I_RATIO * sqrt(sumI / numberOfSamples);
161+
162+
//Calculation power values
163+
realPower = V_RATIO * I_RATIO * sumP / numberOfSamples;
164+
apparentPower = Vrms * Irms;
165+
powerFactor=realPower / apparentPower;
166+
167+
//Reset accumulators
168+
sumV = 0;
169+
sumI = 0;
170+
sumP = 0;
171+
//--------------------------------------------------------------------------------------
172+
}
173+
174+
//--------------------------------------------------------------------------------------
175+
double EnergyMonitor::calcIrms(int NUMBER_OF_SAMPLES)
176+
{
177+
178+
#if defined emonTxV3
179+
int SUPPLYVOLTAGE=3300;
180+
#else
181+
int SUPPLYVOLTAGE = readVcc();
182+
#endif
183+
184+
185+
for (int n = 0; n < NUMBER_OF_SAMPLES; n++)
186+
{
187+
lastSampleI = sampleI;
188+
sampleI = analogRead(inPinI);
189+
lastFilteredI = filteredI;
190+
filteredI = 0.996*(lastFilteredI+sampleI-lastSampleI);
191+
192+
// Root-mean-square method current
193+
// 1) square current values
194+
sqI = filteredI * filteredI;
195+
// 2) sum
196+
sumI += sqI;
197+
}
198+
199+
double I_RATIO = ICAL *((SUPPLYVOLTAGE/1000.0) / (ADC_COUNTS));
200+
Irms = I_RATIO * sqrt(sumI / NUMBER_OF_SAMPLES);
201+
202+
//Reset accumulators
203+
sumI = 0;
204+
//--------------------------------------------------------------------------------------
205+
206+
return Irms;
207+
}
208+
209+
void EnergyMonitor::serialprint()
210+
{
211+
Serial.print(realPower);
212+
Serial.print(' ');
213+
Serial.print(apparentPower);
214+
Serial.print(' ');
215+
Serial.print(Vrms);
216+
Serial.print(' ');
217+
Serial.print(Irms);
218+
Serial.print(' ');
219+
Serial.print(powerFactor);
220+
Serial.println(' ');
221+
delay(100);
222+
}
223+
224+
//thanks to http://hacking.majenko.co.uk/making-accurate-adc-readings-on-arduino
225+
//and Jérôme who alerted us to http://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/
226+
227+
long EnergyMonitor::readVcc() {
228+
long result;
229+
230+
//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/
231+
232+
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328__) || defined (__AVR_ATmega328P__)
233+
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
234+
#elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_AT90USB1286__)
235+
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
236+
ADCSRB &= ~_BV(MUX5); // Without this the function always returns -1 on the ATmega2560 http://openenergymonitor.org/emon/node/2253#comment-11432
237+
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
238+
ADMUX = _BV(MUX5) | _BV(MUX0);
239+
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
240+
ADMUX = _BV(MUX3) | _BV(MUX2);
241+
242+
#endif
243+
244+
245+
#if defined(__AVR__)
246+
delay(2); // Wait for Vref to settle
247+
ADCSRA |= _BV(ADSC); // Convert
248+
while (bit_is_set(ADCSRA,ADSC));
249+
result = ADCL;
250+
result |= ADCH<<8;
251+
result = 1126400L / result; //1100mV*1024 ADC steps http://openenergymonitor.org/emon/node/1186
252+
return result;
253+
#elif defined(__arm__)
254+
return (3300); //Arduino Due
255+
#else
256+
return (3300); //Guess that other un-supported architectures will be running a 3.3V!
257+
#endif
258+
}
259+

libraries/EmonLib/EmonLib.h

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
Emon.h - Library for openenergymonitor
3+
Created by Trystan Lea, April 27 2010
4+
GNU GPL
5+
modified to use up to 12 bits ADC resolution (ex. Arduino Due)
6+
by [email protected] 26.12.2013
7+
*/
8+
9+
#ifndef EmonLib_h
10+
#define EmonLib_h
11+
12+
#if defined(ARDUINO) && ARDUINO >= 100
13+
14+
#include "Arduino.h"
15+
16+
#else
17+
18+
#include "WProgram.h"
19+
20+
#endif
21+
22+
// to enable 12-bit ADC resolution on Arduino Due,
23+
// include the following line in main sketch inside setup() function:
24+
// analogReadResolution(ADC_BITS);
25+
// otherwise will default to 10 bits, as in regular Arduino-based boards.
26+
#if defined(__arm__)
27+
#define ADC_BITS 12
28+
#else
29+
#define ADC_BITS 10
30+
#endif
31+
32+
#define ADC_COUNTS (1<<ADC_BITS)
33+
34+
35+
class EnergyMonitor
36+
{
37+
public:
38+
39+
void voltage(int _inPinV, double _VCAL, double _PHASECAL);
40+
void current(int _inPinI, double _ICAL);
41+
42+
void voltageTX(double _VCAL, double _PHASECAL);
43+
void currentTX(int _channel, double _ICAL);
44+
45+
void calcVI(int crossings, int timeout);
46+
double calcIrms(int NUMBER_OF_SAMPLES);
47+
void serialprint();
48+
49+
long readVcc();
50+
//Useful value variables
51+
double realPower,
52+
apparentPower,
53+
powerFactor,
54+
Vrms,
55+
Irms;
56+
57+
private:
58+
59+
//Set Voltage and current input pins
60+
int inPinV;
61+
int inPinI;
62+
//Calibration coeficients
63+
//These need to be set in order to obtain accurate results
64+
double VCAL;
65+
double ICAL;
66+
double PHASECAL;
67+
68+
//--------------------------------------------------------------------------------------
69+
// Variable declaration for emon_calc procedure
70+
//--------------------------------------------------------------------------------------
71+
int lastSampleV,sampleV; //sample_ holds the raw analog read value, lastSample_ holds the last sample
72+
int lastSampleI,sampleI;
73+
74+
double lastFilteredV,filteredV; //Filtered_ is the raw analog value minus the DC offset
75+
double lastFilteredI, filteredI;
76+
77+
double phaseShiftedV; //Holds the calibrated phase shifted voltage.
78+
79+
double sqV,sumV,sqI,sumI,instP,sumP; //sq = squared, sum = Sum, inst = instantaneous
80+
81+
int startV; //Instantaneous voltage at start of sample window.
82+
83+
boolean lastVCross, checkVCross; //Used to measure number of times threshold is crossed.
84+
int crossCount; // ''
85+
86+
87+
};
88+
89+
#endif

libraries/EmonLib/Readme.txt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
_ _ _
2+
| | (_) |
3+
___ _ __ ___ ___ _ __ | | _| |__
4+
/ _ \ '_ ` _ \ / _ \| '_ \| | | | '_ \
5+
| __/ | | | | | (_) | | | | |____| | |_) |
6+
\___|_| |_| |_|\___/|_| |_|______|_|_.__/
7+
8+
Arduino Energy Monitoring Library - compatible with Arduino 1.0
9+
*****************************************************************
10+
11+
Designed for use with emonTx: http://openenergymonitor.org/emon/Modules
12+
13+
Download to Arduino IDE 'libraries' folder. Restart of IDE required.
14+
15+
Git Clone and Git Pull can be easily used to keep the library up-to-date and manage changes.
16+
JeeLabs has done a good post on the topic: http://jeelabs.org/2011/12/29/out-with-the-old-in-with-the-new/
17+
18+
19+
20+
Update: 5th January 2014: Support Added for Arduino Due (ARM Cortex-M3, 12-bit ADC) by icboredman.
21+
22+
To enable this feature on Arduino Due, add the following statement to setup() function in main sketch:
23+
analogReadResolution(ADC_BITS); This will set ADC_BITS to 12 (Arduino Due), EmonLib will otherwise default to 10 analogReadResolution(ADC_BITS);.
24+
See blog post on using Arduino Due as energy monitor: http://boredomprojects.net/index.php/projects/home-energy-monitor
25+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// EmonLibrary examples openenergymonitor.org, Licence GNU GPL V3
2+
3+
#include "EmonLib.h" // Include Emon Library
4+
EnergyMonitor emon1; // Create an instance
5+
6+
void setup()
7+
{
8+
Serial.begin(9600);
9+
10+
emon1.current(1, 111.1); // Current: input pin, calibration.
11+
}
12+
13+
void loop()
14+
{
15+
double Irms = emon1.calcIrms(1480); // Calculate Irms only
16+
17+
Serial.print(Irms*230.0); // Apparent power
18+
Serial.print(" ");
19+
Serial.println(Irms); // Irms
20+
}

0 commit comments

Comments
 (0)