diff --git a/Examples/EmonTxV34CM_max/EmonTxV34CM_max.ino b/Examples/EmonTxV34CM_max/EmonTxV34CM_max.ino index f093470..9d80cfa 100644 --- a/Examples/EmonTxV34CM_max/EmonTxV34CM_max.ino +++ b/Examples/EmonTxV34CM_max/EmonTxV34CM_max.ino @@ -39,7 +39,7 @@ See: https://github.com/openenergymonitor/emonhub/blob/emon-pi/configuration.md */ -typedef struct {int power1, power2, power3, power4, Vrms, T1, T2, T3; } PayloadTX; // neat way of packaging data for RF comms +typedef struct {int power1, power2, power3, power4, Vrms, T1, T2, T3, T4, T5, T6; } PayloadTX; // package the data for RF comms PayloadTX emontx; // create an instance @@ -80,7 +80,7 @@ void setup() EmonLibCM_min_startup_cycles(10); // number of cycles to let ADC run before starting first actual measurement - EmonLibCM_setPulseEnable(false); // Enable pulse counting + EmonLibCM_setPulseEnable(true); // Enable pulse counting EmonLibCM_setPulsePin(3, 1); EmonLibCM_setPulseMinPeriod(0); @@ -122,30 +122,34 @@ void loop() Serial.println(EmonLibCM_acPresent()?"AC present ":"AC missing "); delay(5); - emontx.power1 = EmonLibCM_getRealPower(0); // Copy the desired variables ready for transmission - emontx.power2 = EmonLibCM_getRealPower(1); - emontx.power3 = EmonLibCM_getRealPower(2); - emontx.power4 = EmonLibCM_getRealPower(3); - emontx.Vrms = EmonLibCM_getVrms() * 100; + emontx.power1 = EmonLibCM_getRealPower(0); // Copy the desired variables ready for transmission + emontx.power2 = EmonLibCM_getRealPower(1); + emontx.power3 = EmonLibCM_getRealPower(2); + emontx.power4 = EmonLibCM_getRealPower(3); + emontx.Vrms = EmonLibCM_getVrms() * 100; emontx.T1 = allTemps[0]; emontx.T2 = allTemps[1]; emontx.T3 = allTemps[2]; + emontx.T4 = allTemps[3]; + emontx.T5 = allTemps[4]; + emontx.T6 = allTemps[5]; rf12_sendNow(0, &emontx, sizeof emontx); //send data delay(50); - Serial.print(" V=");Serial.println(EmonLibCM_getVrms()); + Serial.print(" V=");Serial.print(EmonLibCM_getVrms()); + Serial.print(" f=");Serial.println(EmonLibCM_getLineFrequency(),2); - for (byte ch=0; ch<4; ch++) - { + for (byte ch=0; ch<4; ch++) + { Serial.print("Ch ");Serial.print(ch+1); Serial.print(" I=");Serial.print(EmonLibCM_getIrms(ch),3); Serial.print(" W=");Serial.print(EmonLibCM_getRealPower(ch)); Serial.print(" VA=");Serial.print(EmonLibCM_getApparentPower(ch)); Serial.print(" Wh=");Serial.print(EmonLibCM_getWattHour(ch)); - Serial.print(" pf=");Serial.print(EmonLibCM_getPF(ch),4); + Serial.print(" pf=");Serial.print(EmonLibCM_getPF(ch),4); Serial.println(); delay(10); } diff --git a/Examples/EmonTxV34CM_min/EmonTxV34CM_min.ino b/Examples/EmonTxV34CM_min/EmonTxV34CM_min.ino index 5e18c40..872ae3f 100644 --- a/Examples/EmonTxV34CM_min/EmonTxV34CM_min.ino +++ b/Examples/EmonTxV34CM_min/EmonTxV34CM_min.ino @@ -38,7 +38,7 @@ See: https://github.com/openenergymonitor/emonhub/blob/emon-pi/configuration.md */ -typedef struct {int power1, power2, power3, power4, Vrms, T1, T2, T3, T4, T5, T6; unsigned long pulseCount; } PayloadTX; // neat way of packaging data for RF comms +typedef struct {int power1, power2, power3, power4, Vrms, T1, T2, T3, T4, T5, T6; unsigned long pulseCount; } PayloadTX; // package the data for RF comms PayloadTX emontx; // create an instance @@ -69,10 +69,10 @@ void loop() delay(5); emontx.power1 = EmonLibCM_getRealPower(0); // Copy the desired variables ready for transmission - emontx.power2 = EmonLibCM_getRealPower(1); - emontx.power3 = EmonLibCM_getRealPower(2); - emontx.power4 = EmonLibCM_getRealPower(3); - emontx.Vrms = EmonLibCM_getVrms() * 100; + emontx.power2 = EmonLibCM_getRealPower(1); + emontx.power3 = EmonLibCM_getRealPower(2); + emontx.power4 = EmonLibCM_getRealPower(3); + emontx.Vrms = EmonLibCM_getVrms() * 100; rf12_sendNow(0, &emontx, sizeof emontx); //send data @@ -80,8 +80,8 @@ void loop() Serial.print(" V=");Serial.println(EmonLibCM_getVrms()); - for (byte ch=0; ch<4; ch++) - { + for (byte ch=0; ch<4; ch++) + { Serial.print("Ch ");Serial.print(ch+1); Serial.print(" I=");Serial.print(EmonLibCM_getIrms(ch),3); Serial.print(" W=");Serial.print(EmonLibCM_getRealPower(ch)); diff --git a/emonLibCM User Doc.pdf b/emonLibCM User Doc.pdf index 70cb2e2..7805784 100644 Binary files a/emonLibCM User Doc.pdf and b/emonLibCM User Doc.pdf differ diff --git a/emonLibCM.cpp b/emonLibCM.cpp index e50c378..444dc02 100644 --- a/emonLibCM.cpp +++ b/emonLibCM.cpp @@ -22,7 +22,10 @@ // getLogicalChannel( ), ReCalibrate_VChannel( ), ReCalibrate_IChannel( ) added, setPulsePin( ) interrupt no. was obligatory, // pulse & temperatures were set/enabled only at startup, setTemperatureDataPin was ineffective, preloaded sensor addresses // not handled properly. -// +// Version 2.03 25/10/2019 Mains Frequency reporting [getLineFrequency( )]added, +// ADC reference source was AVcc and not selectable - ability to select [setADC_VRef( )] added, +// sampleSetsDuringThisDatalogPeriod (and derivatives) was samplesDuringThisDatalogPeriod etc, +// Energy calculation changed to use internal clock rather than mains time by addition of "frequencyDeviation". // #include "WProgram.h" un-comment for use on older versions of Arduino IDE @@ -69,6 +72,9 @@ byte no_of_Iinputs = 0; volatile boolean datalogEventPending; unsigned long missing_Voltage = 0; // provides a timebase mechanism for current-only use // - uses the ADC free-running rate as a clock. +double line_frequency; // Timed from sample rate & cycle count + + // Arrays for current channels (zero-based) int realPower_CT[max_no_of_channels]; int apparentPower_CT[max_no_of_channels]; @@ -85,6 +91,7 @@ static byte ADC_Sequence[max_no_of_channels+1] = {0,1,2,3,4,5}; // <-- Se int ADCBits = 10; // 10 for the emonTx and most of the Arduino range, 12 for the Arduino Due. double Vref = 3.3; // ADC Reference Voltage = 3.3 for emonTX, 5.0 for most of the Arduino range. int ADCDuration = 104; // Time in microseconds for one ADC conversion = 104 for 16 MHz clock +byte ADCRef = VREF_NORMAL << 6; // ADC Reference: VREF_EXTERNAL, VREF_NORMAL = AVcc, VREF_INTERNAL = Internal 1.1 V // Pulse Counting; bool PulseEnabled = false; @@ -192,7 +199,7 @@ int64_t sumPB_CT[max_no_of_channels]; // 'partial' power for real uint64_t sumIsquared_CT[max_no_of_channels]; long cumI_deltas_CT[max_no_of_channels]; // <--- for offset removal (I) uint64_t sum_Vsquared; // for Vrms datalogging -long samplesDuringThisDatalogPeriod; +long sampleSetsDuringThisDatalogPeriod; // Copies of ISR data for use by the main code // These are filled by the ADC helper routine at the end of the datalogging period @@ -201,7 +208,7 @@ volatile int64_t copyOf_sumPA_CT[max_no_of_channels]; volatile int64_t copyOf_sumPB_CT[max_no_of_channels]; volatile uint64_t copyOf_sumIsquared_CT[max_no_of_channels]; volatile uint64_t copyOf_sum_Vsquared; -volatile long copyOf_samplesDuringThisDatalogPeriod; +volatile long copyOf_sampleSetsDuringThisDatalogPeriod; volatile int64_t copyOf_cumI_deltas[max_no_of_channels]; volatile int64_t copyOf_cumV_deltas; @@ -319,6 +326,11 @@ void EmonLibCM_setADC(int _ADCBits, int _ADCDuration) ADCDuration = _ADCDuration; } +void EmonLibCM_setADC_VRef(byte _ADCRef) +{ + ADCRef = _ADCRef << 6; +} + void EmonLibCM_setPulseEnable(bool _enable) { PulseEnabled = _enable; @@ -379,6 +391,14 @@ double EmonLibCM_getVrms(void) return Vrms; } +double EmonLibCM_getLineFrequency(void) +{ + if (acPresent) + return line_frequency; + else + return 0; +} + long EmonLibCM_getWattHour(int channel) { return wh_CT[channel]; @@ -544,13 +564,14 @@ void EmonLibCM_get_readings() // It is theoretically possible for the values being copied from to be rewritten // by the ISR whilst this function is calculating the final values. This second // layer of buffering removes that possibility. + double frequencyDeviation; volatile int64_t protected_sumPA[max_no_of_channels]; volatile int64_t protected_sumPB[max_no_of_channels]; volatile uint64_t protected_sumIsquared[max_no_of_channels]; volatile int64_t protected_cumI_deltas[max_no_of_channels]; cli(); - volatile long protected_samplesDuringThisDatalogPeriod = copyOf_samplesDuringThisDatalogPeriod; + volatile long protected_sampleSetsDuringThisDatalogPeriod = copyOf_sampleSetsDuringThisDatalogPeriod; volatile uint64_t protected_sum_Vsquared = copyOf_sum_Vsquared; volatile int64_t protected_cumV_deltas = copyOf_cumV_deltas; @@ -600,9 +621,12 @@ void EmonLibCM_get_readings() // Vrms still contains the fine voltage offset. Correct this by subtracting the "Offset V^2" before the sq. root. // Real Power is calculated by interpolating between the 'partial power' values, applying "trigonometric" coefficients to // preserve the amplitude of the interpolated value. - Vrms = sqrt(((double)protected_sum_Vsquared / protected_samplesDuringThisDatalogPeriod) - - ((double)protected_cumV_deltas * protected_cumV_deltas / protected_samplesDuringThisDatalogPeriod / protected_samplesDuringThisDatalogPeriod)); - Vrms *= voltageCal; + Vrms = sqrt(((double)protected_sum_Vsquared / protected_sampleSetsDuringThisDatalogPeriod) + - ((double)protected_cumV_deltas * protected_cumV_deltas / protected_sampleSetsDuringThisDatalogPeriod / protected_sampleSetsDuringThisDatalogPeriod)); + Vrms *= voltageCal; + + frequencyDeviation = (double)ADCsamples_per_datalog_period / (protected_sampleSetsDuringThisDatalogPeriod * (no_of_channels + 1)); // nominal value / actual value + line_frequency = cycles_per_second * frequencyDeviation; for (int i=0; ino_of_channels) // no_of_channels = count of Current channels in use. Voltage channel (0) is always read, so total is no_of_channels + 1 next -= no_of_channels+1; - ADMUX = 0x40 + ADC_Sequence[next]; // set up the next-but-one conversion + ADMUX = ADCRef + ADC_Sequence[next]; // set up the next-but-one conversion #ifdef SAMPPIN digitalWrite(SAMPPIN,LOW); #endif @@ -940,7 +965,7 @@ void EmonLibCM_interrupt() #ifdef INTEGRITY sampleSetsDuringThisMainsCycle++; #endif - samplesDuringThisDatalogPeriod++; + sampleSetsDuringThisDatalogPeriod++; samplesDuringThisCycle++; // // for the Vrms calculation diff --git a/emonLibCM.h b/emonLibCM.h index b097a54..0952770 100644 --- a/emonLibCM.h +++ b/emonLibCM.h @@ -19,6 +19,8 @@ // Version 2.0 21/11/2018 // Version 2.01 3/12/2018 No change. // Version 2.02 13/07/2019 getLogicalChannel( ), ReCalibrate_VChannel( ), ReCalibrate_IChannel( ) added, setPulsePin( ) interrupt no. was obligatory, +// Version 2.03 25/10/2019 getLineFrequency( ), setADC_VRef( ) added. + @@ -57,6 +59,11 @@ #define TEMPRES_12 0x7F #define CONVERSION_LEAD_TIME 752 // this is the conversion time of the DS18B20 in ms at 12-bits (rounded up to multiple of 8). +#define VREF_EXTERNAL 0 // ADC Reference is Externally supplied voltage +#define VREF_NORMAL 1 // ADC Reference is Processor Supply (AVcc) +#define VREF_INTERNAL 3 // ADC Reference is Internal 1.1V reference + + typedef uint8_t DeviceAddress[8]; void EmonLibCM_cycles_per_second(int _cycles_per_second); @@ -66,6 +73,7 @@ void EmonLibCM_setADC(int _ADCBits, int ADCDuration); void EmonLibCM_ADCCal(double _RefVoltage); void EmonLibCM_SetADC_VChannel(byte ADC_Input, double _amplitudeCal); void EmonLibCM_SetADC_IChannel(byte ADC_Input, double _amplitudeCal, double _phaseCal); +void EmonLibCM_setADC_VRef(byte _ADCRef); void EmonLibCM_ReCalibrate_VChannel(double _amplitudeCal); void EmonLibCM_ReCalibrate_IChannel(byte ADC_Input, double _amplitudeCal, double _phaseCal); @@ -82,6 +90,7 @@ int EmonLibCM_getApparentPower(int channel); double EmonLibCM_getPF(int channel); double EmonLibCM_getIrms(int channel); double EmonLibCM_getVrms(void); +double EmonLibCM_getLineFrequency(void); long EmonLibCM_getWattHour(int channel); unsigned long EmonLibCM_getPulseCount(void); diff --git a/library.json b/library.json index 94d6c38..352a36a 100644 --- a/library.json +++ b/library.json @@ -2,7 +2,7 @@ "name": "EmonLibCM", "keywords": "electricity, energy, monitoring", "description": "Energy Monitoring Continuous Sampling Library", - "version": "2.2.0", + "version": "2.3.0", "repository": { "type": "git", diff --git a/readme.md b/readme.md index 8e3af1d..a650e9e 100644 --- a/readme.md +++ b/readme.md @@ -2,18 +2,19 @@ The EmonLibCM library is maintained by @Robert.Wall and mirrored here for users of github. -See forum release: [EmonLibCM - Version 2.02](https://community.openenergymonitor.org/t/emonlibcm-version-2-02/9241) +See forum release: [EmonLibCM - Version 2.03](https://community.openenergymonitor.org/t/emonlibcm-version-2-03/9241/1) The following release notes are copied from the forum thread. The installation section is modified to reflect git command line installation. --- -**EmonLibCM Release notes**
+## EmonLibCM Release notes + EmonLibCM is a Continuous Monitoring alternative to EmonLib. Whereas emonLib repeats, every 5 or 10 s, a sequence of voltage and current measurements in each of the input channels for a short period, normally 200 ms, and then reports the measurements back to the sketch for onwards transmission to (for example) emonCMS; emonLibCM continuously measures in the background the voltage and all the current input channels in turn, calculates a true average quantity for each and then informs the sketch that the measurements are available and should be read and processed. Temperature measurement with up to 6 DS18B20 sensors, and pulse counting, is included in the library. Neither must be added separately in the sketch. -The CM library will always give an accurate measurement of the average over the reporting period of the voltage, and for each of up to 5 channels the current, real and apparent power and power factor. A cumulative total of Watt-hours for each channel is also available. It is suitable for single phase or split phase operation at 50 Hz or 60 Hz. It will give more accurate values than the 'discrete sample' sketch where rapidly switched loads are in use, for example a burst mode energy diverter or an induction hob. +The CM library will always give an accurate measurement of the average over the reporting period of the voltage, and for each of up to 5 channels the current, real and apparent power and power factor. A cumulative total of Watt-hours for each channel is also available. It is suitable for single phase or split phase operation at 50 Hz or 60 Hz. It will give more accurate values than the ‘discrete sample’ sketch where rapidly switched loads are in use, for example a burst mode energy diverter or an induction hob. The inputs can be calibrated for any realistic voltage and current, the default calibration is for an emonTx with a UK a.c. adapter and 100 A : 50 mA current transformers. @@ -21,56 +22,60 @@ The library is distributed by @Robert.Wall as a compressed Zip file in the forum --- -**INSTALLING THE LIBRARY (Forum thread Zip file)**
+## Installing The Library + +### Arduino IDE + +#### From Zip file + The directory emonLibCM together with its contents should be extracted from the zip file and copied into the "libraries" directory, alongside (in the same level of the hierarchy as) the emonLib directory. If you wish to use the example sketches, these (in their respective directories) should be moved or copied into your Sketchbook. The User Documentation PDF file can be moved or copied to a convenient location of your choosing. -emonLibCM.zip (Version 2: 130.2 KB) - - -Robert Wall will post updates on the forum thread first. + +#### GitHub -**INSTALLING THE LIBRARY (GitHub)**
Navigate to your Arduino libraries directory and clone this repository: git clone https://github.com/openenergymonitor/EmonLibCM.git - + Reload Arduino to start using the library. --- -**CHANGES**
-Version 2.0 corrected some errors generated when converting from the original sketch, and incorporates improved handling of phase/timing compensation and improved removal of the d.c. bias. There are no other major changes from the version that has been tested by @TrystanLea since early 2017. -Changes for V2.01: Errors in phase error correction. -Changes for V2.02: Temperature measurement: Added “BAD_TEMPERATURE” return value when reporting period < 0.2 s, getLogicalChannel( ), ReCalibrate_VChannel( ), ReCalibrate_IChannel( ) added, setPulsePin( ) interrupt no. was obligatory, pulse & temperatures were set/enabled only at startup, setTemperatureDataPin was ineffective, preloaded sensor addresses were not handled properly. +## Changelog + +- Version 2.0 corrected some errors generated when converting from the original sketch, and incorporates improved handling of phase/timing compensation and improved removal of the d.c. bias. There are no other major changes from the version that has been tested by @TrystanLea since early 2017. +- V2.01: Errors in phase error correction. +- V2.02: Temperature measurement: Added “BAD_TEMPERATURE” return value when reporting period < 0.2 s, getLogicalChannel( ), ReCalibrate_VChannel( ), ReCalibrate_IChannel( ) added, setPulsePin( ) interrupt no. was obligatory, pulse & temperatures were set/enabled only at startup, setTemperatureDataPin was ineffective, preloaded sensor addresses were not handled properly. +- V2.03: Mains Frequency reporting with getLineFrequency( ) added, energy calculation changed to use the internal clock rather than mains time. ADC reference source was AVCC and not selectable - ability to select using setADC_VRef( ) added (Note the warning in the documentation regarding the use of this function). + +## Using The Library -**USING THE LIBRARY**
Two example sketches are provided as part of the distribution: **EmonTxV34CM_min** is the absolute minimum sketch required to exercise the library and produce meaningful values. **EmonTxV34CM_max** gives an example of every API call available. However, as distributed, it actually changes nothing as everything is again given the default value. If you need to change one of the defaults, then only the API call that sets that value is needed, and you can copy and add that call to the "minimum’ demo sketch. + The EmonLibCM library is not a direct replacement for the ‘discrete sample’ library emonLib. Significant changes will be need to be made if emonLibCM is to replace emonLib in any particular sketch. -The example sketches are intended only as a demonstration of the library. They do not (for example) take any account of the DIP switch settings of the emonTx V3.4. Great care must be taken if any significant additional load is to be put on the processor. -  +The example sketches are intended only as a demonstration of the library. They do not (for example) take any account of the DIP switch settings of the emonTx V3.4. Great care must be taken if any significant additional load is to be put on the processor. -**Acknowledgements.**
-Jörg Becker (@joergbecker32) for his background work on interrupts and the ADC.
-Robin Emley (@calypso_rae) for his energy diverter software, from which the major part of the library was derived by @TrystanLea
-@ursi (Andries) and @mafheldt (Mike Afheldt) for suggestions made at https://community.openenergymonitor.org/t/emonlib-inaccurate-power-factor/3790 and https://community.openenergymonitor.org/t/rms-calculations-in-emonlib-and-learn-documentation/3749/3 +## Acknowledgements -  +- Jörg Becker (@joergbecker32) for his background work on interrupts and the ADC. +- Robin Emley (@calypso_rae) for his energy diverter software, from which the major part of the library was derived by @TrystanLea +- @ursi (Andries) and @mafheldt (Mike Afheldt) for suggestions made at [EmonLib: Inaccurate power factor](https://community.openenergymonitor.org/t/emonlib-inaccurate-power-factor/3790) and [Rms calculations in EmonLib and Learn documentation](https://community.openenergymonitor.org/t/rms-calculations-in-emonlib-and-learn-documentation/3749/3) - - - + + +
MD5 Hashes: 
EmonLibCM.cpp9dd796794a90eab858a8dea60fdc2389
EmonLibCM.h db208228f62bc5f804569e70efbcee63
emonLibCM User Doc.pdfb7ed8b6f7004d0c9de38089c20695026
EmonLibCM.zip52fce884f3b76cca5531095563475992
EmonLibCM.cpp 51a8a40532e209b02c4861ce7dca5614
emonLibCM.h9004e6fec7efc390babd9f5c2be7b9ab
-Please see [https://community.openenergymonitor.org/t/emonlibcm-version-2-support/9242?u=robert.wall](https://community.openenergymonitor.org/t/emonlibcm-version-2-support/9242?u=robert.wall) to comment or request support. +Please see [EmonLibCM - Version 2 (Support)](https://community.openenergymonitor.org/t/emonlibcm-version-2-support/9242/) to comment or request support.