Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
Mitch K
Published © CC BY-NC-SA

Whole Home Energy Monitor

This is a project for a energy tracker that monitors the whole house. It uses the YHDC SCT-013-000 current sensor.

BeginnerShowcase (no instructions)4 hours11,666
Whole Home Energy Monitor

Things used in this project

Hardware components

SparkFun ESP8266 Thing - Dev Board
SparkFun ESP8266 Thing - Dev Board
×1
ADS1115
×1
YHDC SCT-013-000
×1
.96 OLED I2C
×1
Generic Button
×1
resistor 3.3k
voltage divider
×2
resistor 18ohm
burden resistor
×1

Software apps and online services

Cayenne
myDevices Cayenne

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

Case Top

Case Bottom

Schematics

Circuit diagram

Code

energyMonitor.ino

Arduino
#include <Wire.h>
#include <SPI.h>
#include <ADS1115.h>
#include <U8g2lib.h>
#include <CayenneMQTTESP8266.h>
#include <ESP8266WiFi.h>
#include <time.h>
#include <EEPROM.h>
#include <ESP8266WiFi.h>

ADS1115 adc0(ADS1115_DEFAULT_ADDRESS);

U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

char ssid[] = "IoT";
char wifiPassword[] = "";

char username[] = "";
char password[] = "";
char clientID[] = "";



//////////////////////////////////////////////

const int boardLED = 5;
const int screenButton = 4;

// amper variables
unsigned long startTime = 0;
unsigned long endTime = 0;
float mv;
int runTime = 0;
int readingCount = 0;
float offset = 0;
float adjusted = 0;
int squaredReading = 0;
unsigned long allReadingsSqrd = 0;
double dividedRMS = 0;
double finalNum = 0;


// Kwh variables
int watts = 0;
int kwhCount = 0;
int runningCount = 0;
float runningTotal;
float dayKwh = 0.0;
float dayKwhAccrue = 0;
float cumulativeKwh = 0.0;
unsigned long kwhTime = 0;
float countKwhNow = true;
float cumulativeKwhTotal;


// time variables
const int timezone = -5;  // hours
const int dst = 0;
byte timeHour;
byte timeMin;

// eepromAddresses & screen
const int eepromAddress = 0;
const int eepromAddress1 = 1;
byte screenState = 0;
bool screenOn = true;
unsigned long screenTimer = 0;
bool online = false;





void setup(void)
{
	pinMode(boardLED, OUTPUT);
	pinMode(screenButton, INPUT_PULLUP);

	digitalWrite(boardLED, HIGH);


	Wire.begin();
	// Wire.setClock(400000L);

	Serial.begin(9600);

	EEPROM.begin(512);
	cumulativeKwhTotal = EEPROM.read(eepromAddress);
	// runningTotal = EEPROM.read(eepromAddress1);

	u8g2.begin();
	u8g2.enableUTF8Print();
	u8g2.setFontDirection(0);
	u8g2.clearBuffer();


	u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font
	u8g2.setCursor(0, 15);
	u8g2.print("Starting...");
	u8g2.sendBuffer();


	Serial.println("Starting...");
	
	adc0.initialize();
	byte test = adc0.testConnection();

	if (test == 1)
	  {
		  Serial.println("ADS1115 connected");
		  u8g2.setCursor(0, 30);
		  u8g2.print("ADS1115 connected");
		  u8g2.sendBuffer();
	  }
	else
	  {
		  Serial.println("ADS1115 failed");
		  u8g2.setCursor(0, 30);
		  u8g2.print("ADS1115 failed");
		  u8g2.sendBuffer();

		  delay(30000);
		  ESP.restart();
	  }

	adc0.showConfigRegister();
	Serial.println("");
	delay(1000); //view register

	adc0.setMode(ADS1115_MODE_CONTINUOUS);
	// adc0.setMode(ADS1115_MODE_SINGLESHOT);

	adc0.setRate(ADS1115_RATE_860);

	// adc0.setGain(ADS1115_PGA_0P256); 0.0078125mV
	// adc0.setGain(ADS1115_PGA_0P512); 0.015625mV
	// adc0.setGain(ADS1115_PGA_1P024); // 0.03125mV
	adc0.setGain(ADS1115_PGA_2P048); // 0.0625mV
	// adc0.setGain(ADS1115_PGA_4P096); 0.125mV
	// adc0.setGain(ADS1115_PGA_6P144); 0.1875mV

	adc0.showConfigRegister();
	// Serial.println("");
	// Serial.println("");


	u8g2.setCursor(0, 45);
	u8g2.print("Setting Time...");
	u8g2.sendBuffer();

	// Serial.println("Setting Time");
	setTime();
	delay(500);

	u8g2.setCursor(0, 60);
	u8g2.print("Starting Cayenne...");
	u8g2.sendBuffer();

	Cayenne.begin(username, password, clientID, ssid, wifiPassword);
	Cayenne.virtualWrite(22, 0, "digital_sensor", "d");
}



void loop()
{
	Cayenne.loop();
	getCurrent();
	

	if (millis() - kwhTime > 5000)
	  {
		  getKwh();
		  kwhTime = millis();

		  refreshTime();
	  }
		

	screenState = digitalRead(screenButton);
	if (screenState == 0)
	  {
		  delay(350);
		  screenState = digitalRead(screenButton);
		  if (screenState == 0)
		    {
			    screenOn = true;
			    screenTimer = millis();
			    Cayenne.virtualWrite(22, 1, "digital_sensor", "d");
		    }
		  else
		    {
			    screenOn = false;
			    u8g2.clearDisplay();
			    Cayenne.virtualWrite(22, 0, "digital_sensor", "d");
		    }
	  }


	if (millis() - screenTimer > 20000)
	  {
		  if (screenOn == true)
		    {
			    Cayenne.virtualWrite(22, 0, "digital_sensor", "d");
		    }
		  u8g2.clearDisplay();
		  screenOn = false;

	  }
}




void getCurrent()  //calculate current
{

	endTime = millis() + 1000;
	runTime = 1000;
	readingCount = 0;
	adjusted = 0;
	allReadingsSqrd = 0;


	offset = ( adc0.getConversionP1GND() * 0.0625 );

	while (runTime >= 0)
	  {
		  runTime = endTime - millis();

		  mv = ( adc0.getConversionP0GND() * 0.0625 );
		  readingCount++;

		  adjusted = offset - mv;
		  squaredReading = adjusted * adjusted;
		  allReadingsSqrd += squaredReading;
	  }

	dividedRMS = float( allReadingsSqrd / readingCount );
	finalNum = sqrt(dividedRMS);
	finalNum = ( finalNum / 10 ) * 1.25;


	// Serial.println(offset);
	// Serial.println(mv);
	// Serial.println(adjusted);
	// Serial.println(squaredReading);
	// Serial.println(allReadingsSqrd);
	// Serial.println(" --- ");
	// Serial.println(dividedRMS);
	// Serial.println(finalNum);
	if (screenOn == true)
	  {
		  u8g2.clearBuffer();                           // clear the internal memory

		  if (online == true)
		    {
			    u8g2.setCursor(45, 15);
			    u8g2.print("Online");
		    }
		  else
		    {
			    u8g2.setCursor(40, 15);
			    u8g2.print("Offline!");
		    }

		  u8g2.setCursor(80, 30);
		  u8g2.print(finalNum);
		  u8g2.setCursor(0, 30);
		  u8g2.print("kWh:");
		  u8g2.setCursor(80, 45);
		  u8g2.print(finalNum * 120);
		  u8g2.setCursor(0, 45);
		  u8g2.print("Watts:");

		  u8g2.setCursor(80, 60);
		  u8g2.print(cumulativeKwhTotal);
		  u8g2.setCursor(0, 60);
		  u8g2.print("Month Total:");

		  u8g2.sendBuffer();
	  }

	Cayenne.virtualWrite(0, finalNum, "counter", "null");
	Cayenne.virtualWrite(1, finalNum, "counter", "null");
}




void getKwh()  // calculate kWh & month total
{
	watts = finalNum * 120;

	dayKwh = ( watts * 24 ) / 1000;

	kwhCount++;

	dayKwhAccrue += dayKwh;

	cumulativeKwh = dayKwhAccrue / kwhCount;


	if (timeMin == 0 && countKwhNow == true)
	  {
		  cumulativeKwhTotal += cumulativeKwh;
		  countKwhNow = false;
		  EEPROM.write(eepromAddress, cumulativeKwhTotal);
		  EEPROM.commit();

		  kwhCount = 0;
		  dayKwhAccrue = 0;
		  cumulativeKwh = 0;
	  }

	if (timeMin == 1)
	  {
		  countKwhNow = true;
	  }

	
	Cayenne.virtualWrite(2, dayKwh, "counter", "null");
	Cayenne.virtualWrite(3, cumulativeKwh, "counter", "null");
	Cayenne.virtualWrite(4, cumulativeKwh, "counter", "null");
	Cayenne.virtualWrite(5, cumulativeKwhTotal, "counter", "null");
}




void setTime() // sets time
{
	WiFi.mode(WIFI_STA);
	WiFi.begin(ssid, wifiPassword);

	while (WiFi.status() != WL_CONNECTED)
	  {
		  delay(1);
	  }

	configTime(timezone * 3600, dst, "pool.ntp.org", "time.nist.gov");
	delay(500);

	WiFi.disconnect();
}



void refreshTime()  // updates time to dashboard & variables
{
	time_t now;
	struct tm * timeinfo;
	time(&now);
	timeinfo = localtime(&now);

	timeHour = ( timeinfo->tm_hour );
	timeMin = ( timeinfo->tm_min );

	Cayenne.virtualWrite(10, timeHour, "counter", "null");
	Cayenne.virtualWrite(11, timeMin, "counter", "null");
}






CAYENNE_CONNECTED()
{
	digitalWrite(boardLED, LOW);
	online = true;
}

CAYENNE_DISCONNECTED()
{
	digitalWrite(boardLED, HIGH);
	online = false;
}



CAYENNE_IN(22) // turns screen on & starts timer
{
	screenState = getValue.asInt();

	if (screenState == 1)
	  {
		  screenOn = true;
		  screenTimer = millis();
	  }
}


CAYENNE_IN(23) // trigger chip restart
{
	byte restartChip = getValue.asInt();
	delay(1000);

	if (restartChip == 1)
	  {
		  Cayenne.virtualWrite(23, 0, "digital_sensor", "d");
		  Cayenne.loop(); // writes reboot button back low before restart
		  delay(1500);

		  ESP.restart();
	  }
}

CAYENNE_IN(24)  //reset month total
{
	byte resetTotal = getValue.asInt();
	delay(1000);

	if (resetTotal == 1)
	  {
		  cumulativeKwhTotal = 0;
		  EEPROM.write(eepromAddress, cumulativeKwhTotal);
		  EEPROM.commit();

		  Cayenne.virtualWrite(24, 0, "digital_sensor", "d");
		  Cayenne.loop();
	  }
}

Credits

Mitch K

Mitch K

10 projects • 34 followers
Maker, designer, & all around fun to be around!

Comments