From 5d43706492f7c153236b9c066b38b3d4046d04f4 Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Sun, 17 Aug 2014 22:27:08 +0200 Subject: [PATCH] Add emon lib for a later non invasive current sensor. --- libraries/EmonLib/EmonLib.cpp | 259 ++++++++++++++++++ libraries/EmonLib/EmonLib.h | 89 ++++++ libraries/EmonLib/Readme.txt | 25 ++ .../examples/current_only/current_only.ino | 20 ++ .../voltage_and_current.ino | 24 ++ 5 files changed, 417 insertions(+) create mode 100644 libraries/EmonLib/EmonLib.cpp create mode 100644 libraries/EmonLib/EmonLib.h create mode 100644 libraries/EmonLib/Readme.txt create mode 100644 libraries/EmonLib/examples/current_only/current_only.ino create mode 100644 libraries/EmonLib/examples/voltage_and_current/voltage_and_current.ino diff --git a/libraries/EmonLib/EmonLib.cpp b/libraries/EmonLib/EmonLib.cpp new file mode 100644 index 0000000..1a1fd29 --- /dev/null +++ b/libraries/EmonLib/EmonLib.cpp @@ -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) 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 +} + diff --git a/libraries/EmonLib/EmonLib.h b/libraries/EmonLib/EmonLib.h new file mode 100644 index 0000000..41cafcb --- /dev/null +++ b/libraries/EmonLib/EmonLib.h @@ -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<