Skip to content

Instantly share code, notes, and snippets.

@chaeplin
Forked from whatnick/Energy_Monitor_Real.ino
Created March 22, 2016 16:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save chaeplin/5d24283ee34958c32b23 to your computer and use it in GitHub Desktop.
Save chaeplin/5d24283ee34958c32b23 to your computer and use it in GitHub Desktop.
ESP8266 Energy Monitor Real Power
/*
* This sketch sends ads1115 current sensor data via HTTP POST request to thingspeak server.
* It needs the following libraries to work (besides the esp8266 standard libraries supplied with the IDE):
*
* - https://github.com/adafruit/Adafruit_ADS1X15
*
* designed to run directly on esp8266-01 module, to where it can be uploaded using this marvelous piece of software:
*
* https://github.com/esp8266/Arduino
*
* 2015 Tisham Dhar
* licensed under GNU GPL
*/
#include <ESP8266WiFi.h>
#include <Wire.h>
#include <Adafruit_ADS1015.h>
// replace with your channel's thingspeak API key,
String apiKey = "XXXXXXXXXXXXXXXX";
//WIFI credentials go here
const char* ssid = "xxxxxxxxxxxxx";
const char* password = "xxxxxxxxxxxxxxxx";
Adafruit_ADS1115 ads; /* Use this for the 16-bit version */
//Maximum value of ADS
#define ADC_COUNTS 32768
#define PHASECAL 1.7
#define VCAL 0.6
#define ICAL 0.003
const char* server = "api.thingspeak.com";
WiFiClient client;
double filteredI;
double lastFilteredV,filteredV; //Filtered_ is the raw analog value minus the DC offset
int sampleV; //sample_ holds the raw analog read value
int sampleI;
double offsetV; //Low-pass filter output
double offsetI; //Low-pass filter output
double realPower,
apparentPower,
powerFactor,
Vrms,
Irms;
double phaseShiftedV; //Holds the calibrated phase shifted voltage.
int startV; //Instantaneous voltage at start of sample window.
double sqV,sumV,sqI,sumI,instP,sumP; //sq = squared, sum = Sum, inst = instantaneous
boolean lastVCross, checkVCross; //Used to measure number of times threshold is crossed.
double squareRoot(double fg)
{
double n = fg / 2.0;
double lstX = 0.0;
while (n != lstX)
{
lstX = n;
n = (n + fg / n) / 2.0;
}
return n;
}
void calcVI(unsigned int crossings, unsigned int timeout)
{
unsigned int crossCount = 0; //Used to measure number of times threshold is crossed.
unsigned int numberOfSamples = 0; //This is now incremented
//-------------------------------------------------------------------------------------------------------------------------
// 1) Waits for the waveform to be close to 'zero' (mid-scale 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 = ads.readADC_Differential_2_3(); //using the voltage waveform
if ((abs(startV) < (ADC_COUNTS*0.55)) && (abs(startV) > (ADC_COUNTS*0.45))) st=true; //check its within range
if ((millis()-start)>timeout) st = true;
}
//-------------------------------------------------------------------------------------------------------------------------
// 2) Main measurement loop
//-------------------------------------------------------------------------------------------------------------------------
start = millis();
while ((crossCount < crossings) && ((millis()-start)<timeout))
{
numberOfSamples++; //Count number of times looped.
lastFilteredV = filteredV; //Used for delay/phase compensation
//-----------------------------------------------------------------------------
// A) Read in raw voltage and current samples
//-----------------------------------------------------------------------------
sampleV = ads.readADC_Differential_2_3(); //Read in raw voltage signal
sampleI = ads.readADC_Differential_0_1(); //Read in raw current signal
//-----------------------------------------------------------------------------
// B) Apply digital low pass filters to extract the 2.5 V or 1.65 V dc offset,
// then subtract this - signal is now centred on 0 counts.
//-----------------------------------------------------------------------------
offsetV = offsetV + ((sampleV-offsetV)/1024);
filteredV = sampleV - offsetV;
offsetI = offsetI + ((sampleI-offsetI)/1024);
filteredI = sampleI - offsetI;
//-----------------------------------------------------------------------------
// C) Root-mean-square method voltage
//-----------------------------------------------------------------------------
sqV= filteredV * filteredV; //1) square voltage values
sumV += sqV; //2) sum
//-----------------------------------------------------------------------------
// D) Root-mean-square method current
//-----------------------------------------------------------------------------
sqI = filteredI * filteredI; //1) square current values
sumI += sqI; //2) sum
//-----------------------------------------------------------------------------
// E) Phase calibration
//-----------------------------------------------------------------------------
phaseShiftedV = lastFilteredV + PHASECAL * (filteredV - lastFilteredV);
//-----------------------------------------------------------------------------
// F) Instantaneous power calc
//-----------------------------------------------------------------------------
instP = phaseShiftedV * filteredI; //Instantaneous Power
sumP +=instP; //Sum
//-----------------------------------------------------------------------------
// G) Find the number of times the voltage has crossed the initial voltage
// - every 2 crosses we will have sampled 1 wavelength
// - so this method allows us to sample an integer number of half wavelengths which increases accuracy
//-----------------------------------------------------------------------------
lastVCross = checkVCross;
if (sampleV > 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 coefficients applied.
float multiplier = 0.125F; /* ADS1115 @ +/- 4.096V gain (16-bit results) */
double V_RATIO = VCAL * multiplier;
Vrms = V_RATIO * squareRoot(sumV / numberOfSamples);
double I_RATIO = ICAL * multiplier;
Irms = I_RATIO * squareRoot(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 calcIrms(unsigned int Number_of_Samples)
{
/* Be sure to update this value based on the IC and the gain settings! */
float multiplier = 0.125F; /* ADS1115 @ +/- 4.096V gain (16-bit results) */
for (unsigned int n = 0; n < Number_of_Samples; n++)
{
sampleI = ads.readADC_Differential_0_1();
// Digital low pass filter extracts the 2.5 V or 1.65 V dc offset,
// then subtract this - signal is now centered on 0 counts.
offsetI = (offsetI + (sampleI-offsetI)/1024);
filteredI = sampleI - offsetI;
//filteredI = sampleI * multiplier;
// Root-mean-square method current
// 1) square current values
sqI = filteredI * filteredI;
// 2) sum
sumI += sqI;
}
Irms = squareRoot(sumI / Number_of_Samples)*multiplier;
//Reset accumulators
sumI = 0;
//--------------------------------------------------------------------------------------
return Irms;
}
void setup() {
Serial.begin(115200);
delay(10);
// We start by connecting to a WiFi network
//Serial.println();
//Serial.println();
//Serial.print("Connecting to ");
//Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
//Serial.print(".");
}
//Serial.println("");
//Serial.println("WiFi connected");
//Serial.println("IP address: ");
//Serial.println(WiFi.localIP());
ads.setGain(GAIN_ONE); // 1x gain +/- 4.096V 1 bit = 0.125mV
ads.begin();
}
void loop() {
//Serial.print("Differential: "); Serial.print(results); Serial.print("("); Serial.print(trans_volt); Serial.println("mV)");
//double current = calcIrms(2048);
//Serial.print("Just Current:");
//Serial.println(current);
calcVI(20,2000);
//Serial.print("Real Power:");
//Serial.println(realPower);
//Serial.print("Irms:");
//Serial.println(Irms);
//Serial.print("Vrms:");
//Serial.println(Vrms);
if (client.connect(server,80)) { // "184.106.153.149" or api.thingspeak.com
String postStr = apiKey;
postStr +="&field1=";
postStr += String(realPower);
postStr +="&field2=";
postStr += String(Vrms);
postStr +="&field3=";
postStr += String(Irms);
postStr +="&field4=";
postStr += String(powerFactor);
postStr += "\r\n\r\n";
client.print("POST /update HTTP/1.1\n");
client.print("Host: api.thingspeak.com\n");
client.print("Connection: close\n");
client.print("X-THINGSPEAKAPIKEY: "+apiKey+"\n");
client.print("Content-Type: application/x-www-form-urlencoded\n");
client.print("Content-Length: ");
client.print(postStr.length());
client.print("\n\n");
client.print(postStr);
}
client.stop();
//Serial.println("Waiting...");
// thingspeak needs minimum 15 sec delay between updates
delay(20000);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment