Hi, first time poster. I’m working on a pool controller project, based on a particle.io photon (Arduino-like). I am currently adding “power monitoring” for my pool filter pump and Stenner pumps, which are pumps that add acid and chlorine to the pool. All I really need is the wattage being consumed at any given time by those devices. I am a retired electrical engineer with chip design background so AC fundamentals are definitely NOT a forte. I think I have a solution that works, but I am unclear if I am missing something obvious.
I am using a couple SCT-013’s to accomplish the hardware portion of the project…setup exactly as described in this tutorial.
https://learn.openenergymonitor.org/electricity-monitoring/ct-sensors/interface-with-arduino
I started looking at the openenergymonitor library which has been ported to the particle.io Photon, but there seems to be a lot more there than I need (??)…I am also concerned about the “blocking nature” of the code (large number of consecutive A/D samples required), as all my other code is written in a non-blocking manner.
So I wrote some code which seems to do the job, but I really don’t understand all the intricacies of realPower vs apparentPower and powerFactor. So I might be missing something or possibly run into a problem when I use this code/hardware for my 220v pool filter pump (Stenner pumps are 120v).
The code does ~20 strategically timed samples of the SCT-013 60Hz output over a period of one second. These 20 samples contain 16 equally spaced points of the 16.7ms period of the 60Hz signal. The peak-to-peak values (high and low) are extracted from the code and a constant multiplier converts it to a current (amps) value. Then it is multiplied by the voltage (120v) to get watts.
I have only tested this on combinations of 120v “appliances” but it seems to be accurate (matches my Kill-A-Watts meter well within one percent) for all my test cases (0-600 watts). It does mismatch at the very low end (<20 watts).
Am I missing anything with this simplified approach? I am enclosing the code below…I am not very familiar with the Arduino so there might be some things in there that don’t translate directly (the A/D read on analog pin A0 in particular).
Attaching an .ino file.
SAMPLE OUTPUT
Serial connection closed. Attempting to reconnect…
Serial monitor opened successfully:
Reading Min: 0, Reading Max : 0, Reading Diff : 0, Current (amps) : 0.000000, Watts: 0
Reading Min: 1915, Reading Max : 2146, Reading Diff : 231, Current (amps) : 0.370172, Watts: 44
Reading Min: 1911, Reading Max : 2145, Reading Diff : 234, Current (amps) : 0.374979, Watts: 44
Reading Min: 1913, Reading Max : 2145, Reading Diff : 232, Current (amps) : 0.371774, Watts: 44
Reading Min: 1915, Reading Max : 2142, Reading Diff : 227, Current (amps) : 0.363762, Watts: 43
Reading Min: 1917, Reading Max : 2146, Reading Diff : 229, Current (amps) : 0.366967, Watts: 44
Reading Min: 1913, Reading Max : 2144, Reading Diff : 231, Current (amps) : 0.370172, Watts: 44
Reading Min: 1915, Reading Max : 2141, Reading Diff : 226, Current (amps) : 0.362160, Watts: 43
/* This sketch demonstrates a method to dynamically calculate current and
* power (60Hz) using a current transformer, such as a SCT-013, with low
* code and execution time overhead. It is non-blocking such that photon/arduino
* processing/adc-conversions are spread over a period of time.
*
* @60Hz, three cycles take 50ms, we will sample the SCT-013 at a frequency of 49ms
* (about every third cycle) in order to slightly change the sampled position on
* subsequent cycles (by 1ms each time). This means we would need 16-17 samples to
* characterize "one complete" cycle (which takes 16.67 ms). 17 samples @49ms is 843ms
* which means we can sample/calculate a new current/power about every second with
* low overhead.
*
* The code "main accomplishment" is to simply find the peaks (high and low values of the
* sine wave) for the amperage signal coming from the SCT-013. These are then used to
* calculate instantaneous current and power.
*
*/
#define SCT013_SAMPLE_TIME 49 // 49ms
#define PUBLISH_TIME 1000 // 1000ms = every second
/* The following constant is experimentally determined using an ammeter:
* = (Reading on amp meter) / differential
* "differential" is the variable printed out below
*/
#define CALIBRATION_CONSTANT 0.0016024759284732F
// for now...they are all global
unsigned long currentMillis, prior_sample_time, prior_publish_time;
float amps; // amp reading
uint16_t differential, wattage, recent_sample, prior_sample, current_min, current_max;
bool isFalling;
uint8_t sample_count;
void setup() {
}
void loop() {
currentMillis = millis();
// Take & compare a current (amperage) sample every SCT013_SAMPLE_TIME (49ms).
if ((currentMillis - prior_sample_time) >= SCT013_SAMPLE_TIME) {
recent_sample = analogRead(A0);
if (isFalling) { // consecutive amp samples are "heading down towards" a sine-wave minimum
if (recent_sample >= prior_sample) {
isFalling = false; // amps sine-wave minimum was reached on prior_sample, now heading back towards a maximum
current_min = prior_sample;
}
}
else { // consecutive amp samples are "heading up towards" a sine-wave maximum
if (recent_sample <= prior_sample) {
isFalling = true; // amps sine-wave maximum was reached on prior_sample, now heading back towards a minimum
current_max = prior_sample;
}
}
prior_sample = recent_sample;
prior_sample_time = currentMillis; // Next Sample Time Setup...use currentMillis rather than millis() to insure proper spacing
differential = current_max - current_min; // peak to peak value, raw and uncalibrated
amps = differential * CALIBRATION_CONSTANT; // forget about the .707 multiplier RMS stuff...just incorporate it into the constant
wattage = 120 * amps;
}
// Print the latest available amperage/power values every PUBLISH_TIME (1 second)
if ((currentMillis - prior_publish_time) >= PUBLISH_TIME) {
Serial.printlnf("Reading Min: %d, Reading Max : %d, Reading Diff : %d, Current (amps) : %f, Watts: %d", current_min, current_max, differential, amps, wattage);
prior_publish_time = currentMillis;
}
}