Skip to content

Commit

Permalink
Merge pull request #6 from borpin/v2.03
Browse files Browse the repository at this point in the history
Update emonLibCM to V2.03
  • Loading branch information
TrystanLea authored May 30, 2020
2 parents d4dd94d + f9c0e38 commit 8d0539c
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 63 deletions.
26 changes: 15 additions & 11 deletions Examples/EmonTxV34CM_max/EmonTxV34CM_max.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
}
Expand Down
14 changes: 7 additions & 7 deletions Examples/EmonTxV34CM_min/EmonTxV34CM_min.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -69,19 +69,19 @@ 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

delay(50);

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));
Expand Down
Binary file modified emonLibCM User Doc.pdf
Binary file not shown.
61 changes: 43 additions & 18 deletions emonLibCM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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];
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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; i<no_of_channels; i++) // Current channels
{
Expand All @@ -617,14 +641,14 @@ void EmonLibCM_get_readings()
sumRealPower = (protected_sumPA[i] * x[i] + protected_sumPB[i] * y[i]);

// sumRealPower still contains the fine offsets of both V & I. Correct this by subtracting the "Offset Power": cumV_deltas * cumI_deltas
powerNow = (sumRealPower / protected_samplesDuringThisDatalogPeriod - (double)protected_cumV_deltas * protected_cumI_deltas[i]
/ protected_samplesDuringThisDatalogPeriod / protected_samplesDuringThisDatalogPeriod) * voltageCal * currentCal[i];
powerNow = (sumRealPower / protected_sampleSetsDuringThisDatalogPeriod - (double)protected_cumV_deltas * protected_cumI_deltas[i]
/ protected_sampleSetsDuringThisDatalogPeriod / protected_sampleSetsDuringThisDatalogPeriod) * voltageCal * currentCal[i];

// root of mean squares, removing fine offset
// The rms of a signal plus an offset is sqrt( signal^2 + offset^2).
// Here (signal+offset)^2 = protected_sumIsquared / no of samples
// offset = cumI_deltas / no of samples
Irms_CT[i] = sqrt(((double)protected_sumIsquared[i] / protected_samplesDuringThisDatalogPeriod) - ((double)protected_cumI_deltas[i] * protected_cumI_deltas[i] / protected_samplesDuringThisDatalogPeriod / protected_samplesDuringThisDatalogPeriod));
Irms_CT[i] = sqrt(((double)protected_sumIsquared[i] / protected_sampleSetsDuringThisDatalogPeriod) - ((double)protected_cumI_deltas[i] * protected_cumI_deltas[i] / protected_sampleSetsDuringThisDatalogPeriod / protected_sampleSetsDuringThisDatalogPeriod));

Irms_CT[i] *= currentCal[i];

Expand All @@ -635,7 +659,8 @@ void EmonLibCM_get_readings()
pf[i] = 0.0;
realPower_CT[i] = powerNow + 0.5; // rounded to nearest Watt
apparentPower_CT[i] = VA + 0.5; // rounded to nearest VA
energyNow = (powerNow * datalog_period_in_seconds) + residualEnergy_CT[i]; // fp for accuracy
energyNow = (powerNow * datalog_period_in_seconds / frequencyDeviation) // correct for mains time != clock time
+ residualEnergy_CT[i]; // fp for accuracy
wattHoursRecent = energyNow / 3600; // integer assignment to extract whole Wh
wh_CT[i]+= wattHoursRecent; // accumulated WattHours since start-up
residualEnergy_CT[i] = energyNow - (wattHoursRecent * 3600.0); // fp for accuracy
Expand Down Expand Up @@ -747,7 +772,7 @@ void EmonLibCM_allGeneralProcessing_withinISR()
#ifdef INTEGRITY
lowestNoOfSampleSetsPerMainsCycle = 999;
#endif
samplesDuringThisDatalogPeriod = 0;
sampleSetsDuringThisDatalogPeriod = 0;
}

// Start temperature conversion
Expand Down Expand Up @@ -775,8 +800,8 @@ void EmonLibCM_allGeneralProcessing_withinISR()
copyOf_sum_Vsquared = sum_Vsquared;
sum_Vsquared = 0;
cumV_deltas = 0;
copyOf_samplesDuringThisDatalogPeriod = samplesDuringThisDatalogPeriod;
samplesDuringThisDatalogPeriod = 0;
copyOf_sampleSetsDuringThisDatalogPeriod = sampleSetsDuringThisDatalogPeriod;
sampleSetsDuringThisDatalogPeriod = 0;
#ifdef INTEGRITY
copyOf_lowestNoOfSampleSetsPerMainsCycle = lowestNoOfSampleSetsPerMainsCycle; // (for diags only)
lowestNoOfSampleSetsPerMainsCycle = 999;
Expand Down Expand Up @@ -842,8 +867,8 @@ void EmonLibCM_allGeneralProcessing_withinISR()
sum_Vsquared = 0;
copyOf_cumV_deltas = cumV_deltas;
cumV_deltas = 0;
copyOf_samplesDuringThisDatalogPeriod = samplesDuringThisDatalogPeriod;
samplesDuringThisDatalogPeriod = 0;
copyOf_sampleSetsDuringThisDatalogPeriod = sampleSetsDuringThisDatalogPeriod;
sampleSetsDuringThisDatalogPeriod = 0;
#ifdef INTEGRITY
copyOf_lowestNoOfSampleSetsPerMainsCycle = lowestNoOfSampleSetsPerMainsCycle; // (for diags only)
// lowestNoOfSampleSetsPerMainsCycle = 999;
Expand Down Expand Up @@ -892,7 +917,7 @@ void EmonLibCM_interrupt()
if (next>no_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
Expand Down Expand Up @@ -940,7 +965,7 @@ void EmonLibCM_interrupt()
#ifdef INTEGRITY
sampleSetsDuringThisMainsCycle++;
#endif
samplesDuringThisDatalogPeriod++;
sampleSetsDuringThisDatalogPeriod++;
samplesDuringThisCycle++;
//
// for the Vrms calculation
Expand Down
9 changes: 9 additions & 0 deletions emonLibCM.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.




Expand Down Expand Up @@ -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);
Expand All @@ -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);

Expand All @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading

0 comments on commit 8d0539c

Please sign in to comment.