Here is a really simple sketch used to measure Power consumption of my house.
It simply "snif" the LED blinking on my powermeter any time 1Wh has elapsed.
I use a very cheap light sensor to trigger an interrupt on my ESP board
you can find them on ebay searching for : Lm393 Light Sensor Module For Arduino
I have attached the module into a piece of wood so that no light can enter into the led (but of course the Power meter led). The LED is thus jus in front of the blinking led.
Of course this is a dirty prototype, I will unsolder the led later
Then every minute (it's a parameter) I upload the pulses count to thingspeak.
Here is the result with french legend !
And finally here is the code.
Most of it is to setup the ESP with SSID, password and ApiKey + Channel ID + refresh rate. I use the excellent WifiManager library for that.
The debouncing is done this way :
if (abs(millis() - LastPulse) > 100 ) //debounce light sensor --> 10 Wh/s max !
{
LastPulse = millis();
PulseCount++;
#ifdef xDEBUG
Serial.print("\nPulseCount = ");
Serial.println(PulseCount);
#endif
}
As you see I trigger immediately when Led is On, but I "wait" 100ms to allow a second pulse.
The drawback is that you only allow 10 pulses (Wh) per second... which is 10*60*60 = 36 kW/h ... It's enough for me !
Enjoy !
JP
The full code :
#include <FS.h> //this needs to be first, or it all crashes and burns...
#include <ESP8266WiFi.h> //https://github.com/esp8266/Arduino
#include <WiFiClient.h>
//#include <WiFiServer.h>
//#include <WiFiUdp.h>
//needed for Wifi Manager library
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h> //https://github.com/tzapu/WiFiManager
#include <ArduinoJson.h> //https://github.com/bblanchon/ArduinoJson
#include <Ticker.h> //used to call Thingspeak periodically
Ticker TimerTS;
//define your default values here, if there are different values in config.json, they are overwritten.
char Channel_ID[11] = ""; //ThingSpeak Channel ID
char ApiKey[17] = ""; //
char TimeOut[17] = ""; //TimeOut to refresh ThingSpeak
int iTimeOut=10; // same into integer value
#define TRIGGER_PIN 13 //interrupt from light sensor
long PulseCount = 0;
long SavePulseCount = 0;
long LastPulse = 0;
#define DEBUG
#define xDEBUG
WiFiServer server(80);
const char* host = "api.thingspeak.com";
int httpConnectCounter= 0;
bool DoTS = false; //flag for triggering ThingSpeak
bool shouldSaveConfig = false; //flag for saving data after WifiManager
//*********************************************
//callback notifying WifiManager to save config
void saveConfigCallback () {
Serial.println("Should save config");
shouldSaveConfig = true;
}
//*********************************************
//callback for WattMeter interrupt
void WattMeter_Interrupt()
{
if (abs(millis() - LastPulse) > 100 ) //debounce light sensor --> 10 Wh/s max !
{
LastPulse = millis();
PulseCount++;
#ifdef xDEBUG
Serial.print("\nPulseCount = ");
Serial.println(PulseCount);
#endif
}
}
//*********************************************
// put your setup code here, to run once :
void setup() {
Serial.begin(115200);
Serial.println();
pinMode(TRIGGER_PIN, INPUT); //interrupt on this pin
attachInterrupt(TRIGGER_PIN, WattMeter_Interrupt, RISING);
//clean FS, for testing
//SPIFFS.format();
//read configuration from FS json
Serial.println("mounting FS...");
if (SPIFFS.begin()) {
Serial.println("mounted file system");
if (SPIFFS.exists("/config.json")) {
//file exists, reading and loading
Serial.println("reading config file : ");
File configFile = SPIFFS.open("/config.json", "r");
if (configFile)
{
//Serial.println("opened config file");
size_t size = configFile.size();
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
DynamicJsonBuffer jsonBuffer;
JsonObject& json = jsonBuffer.parseObject(buf.get());
json.printTo(Serial);
if (json.success())
{
strcpy(Channel_ID, json["Channel_ID"]);
strcpy(ApiKey, json["ApiKey"]);
strcpy(TimeOut, json["TimeOut"]);
}
else {
Serial.println("failed to load json config");
}
}
}
}
else
{
Serial.println("failed to mount FS");
}
//end read
// The extra parameters to be configured (can be either global or just in the setup)
// After connecting, parameter.getValue() will get you the configured value
// (id, name, placeholder, prompt default length)
WiFiManagerParameter custom_Channel_ID("Channel_ID", "channel ID", Channel_ID, 7);
WiFiManagerParameter custom_ApiKey("ApiKey", "ThingSpeak API Key", ApiKey, 17);
WiFiManagerParameter custom_TimeOut("TimeOut", "Refresh rate", TimeOut, 17);
//WiFiManager
//Local intialization. Once its business is done, there is no need to keep it around
WiFiManager wifiManager;
//set config save notify callback
wifiManager.setSaveConfigCallback(saveConfigCallback);
//set custom ip for portal (default : 192.168.4.1)
//wifiManager.setAPStaticIPConfig(IPAddress(192,168,4,1), IPAddress(192,168,4,1), IPAddress(255,255,255,0));
//set static ip (default = DNS)
//wifiManager.setSTAStaticIPConfig(IPAddress(192,168,0,99), IPAddress(192,168,0,1), IPAddress(255,255,255,0));
//add all your parameters here
wifiManager.addParameter(&custom_ApiKey);
wifiManager.addParameter(&custom_Channel_ID);
wifiManager.addParameter(&custom_TimeOut);
//reset settings - for testing
//wifiManager.resetSettings();
//set minimu quality of signal so it ignores AP's under that quality
//defaults to 8%
//wifiManager.setMinimumSignalQuality();
//sets timeout until configuration portal gets turned off
//useful to make it all retry or go to sleep
//in seconds
wifiManager.setTimeout(120);
//fetches ssid and pass and tries to connect
//if it does not connect it starts an access point with the specified name
//here "AutoConnectAP"
//and goes into a blocking loop awaiting configuration
//if (!wifiManager.autoConnect("AutoConnectAP", "password")) {
if (!wifiManager.autoConnect("Config AP", "password")) {
Serial.println("failed to connect and hit timeout : reset now ");
delay(3000);
//reset and try again, or maybe put it to deep sleep
ESP.reset();
delay(5000);
}
//if you get here you have connected to the WiFi
Serial.println("connected...yeey :)");
//read updated parameters
strcpy(ApiKey, custom_ApiKey.getValue());
strcpy(Channel_ID, custom_Channel_ID.getValue());
strcpy(TimeOut, custom_TimeOut.getValue());
//save the custom parameters to FS
if (shouldSaveConfig) {
Serial.println("saving config");
DynamicJsonBuffer jsonBuffer;
JsonObject& json = jsonBuffer.createObject();
json["ApiKey"] = ApiKey;
json["Channel_ID"] = Channel_ID;
json["TimeOut"] = TimeOut;
File configFile = SPIFFS.open("/config.json", "w");
if (!configFile) {
Serial.println("failed to open config file for writing");
}
//json.printTo(Serial);
json.printTo(configFile);
configFile.close();
//end save
}
Serial.println("local ip");
Serial.println(WiFi.localIP());
String sTimeOut;
sTimeOut = TimeOut;
iTimeOut= sTimeOut.toInt() ;
#ifdef x²DEBUG
Serial.print("\nrefresh rate (s) : ");
Serial.println(iTimeOut);
#endif
TimerTS.attach(iTimeOut, DoTimerTS);
}
//*********************************************
// put your main code here, to run repeatedly :
void loop()
{
if (DoTS == true)
{
#ifdef DEBUG
Serial.print("connecting to ");
Serial.println(host);
#endif
// Use WiFiClient class to create TCP connections
httpConnectCounter++;
WiFiClient client;
const int httpPort = 80;
if (!client.connect(host, httpPort))
{
Serial.println("http connection failed, will retry");
if (httpConnectCounter < 5)
{
delay(20);
return; // retry connection
}
else
{
// 20 times no success give up, reset board
Serial.println("http give up, will restart");
delay(1000);
abort();
}
}
DoTS = false;
//the connection is Ok
httpConnectCounter = 0;
SavePulseCount = PulseCount; // pick up the Watt values (1 flash = 1Wh)
PulseCount = 0;
// We now create a URL for the request
String url = "/update";
url += "?key=";
url += ApiKey;
url += "&field1=";
url += SavePulseCount;
#ifdef xDEBUG
Serial.print("Requesting URL: ");
Serial.println(url);
#endif
// This will send the request to the server
client.print(String("GET ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
delay(200);
// Read all the lines of the reply from server and print them to Serial
if (client.available())
{
// print first line
String line = client.readStringUntil('\r');
#ifdef xDEBUG
Serial.print(line);
#endif
}
while (client.available()) String line = client.readStringUntil('\r');
Serial.println();
Serial.println("closing connection");
client.stop();
// all done
}
}
// *******************************************
// interrupt routine to trigger ThingSpeak
void DoTimerTS()
{
DoTS = true;
}