11 January, 2016

Build your own fully customizable Energy Monitor for 30 bucks

Arduino UNO with a 2.4 "TFT monitor the consumption of a house
The crisis continues, so If you want to know how much electricity do you need every hour of the day, or switch power contract to the TDH or reduce the contracted power, or make estimates, you can install an expensive energy monitor, or, as we will see in this post, we manufacture ourselves .
IMPORTANT : If your supplier has telemetry on your electricity bill, chances are that hourly consumption can be consulted on the website of the supplier.

Version 1.0 of the program and debugged

I have arisen this little project from the free time of a couple of weeks, an energy monitor based on Arduino, an RTC and a small TFT screen, with many possibilities of use. It's not as good as the "Mirubee Mirubox", that it tells you with graphics and all your habits, but you save € 90, learning Arduino and gives enough info... and you can always improve the program and Arduino to get something that comes close;.) When I finished the project I discovered that I was not the only with good thoughts ... as always . There are many similar projects, most receive LED pulses from the digital counter...

Materials used:

Copy Arduino UNO R3 with TFT 2.4 "

Clock circuit with DS1307

KWh meter with pulse output (opto transistor closes the circuit)

Installing Kwh counter

The first thing I did was install it to check its operation; I simply inserted the pulse counter and the differential between the main output; on this page Nergiza is explained how to install an ICP , it is not the same but will give you the idea where to put it, you have to go between the PCI (Power Control Switch) and the differential . (I've since then, but does not affect the operation of the differential).
There is one detail; it is not necessary to place a big lead in the Neutral because they do not need to load pass by it (used only as a reference so I connect it with 2 mm. cable to Neutral connected to the  differential is worth enough), it is important that the cables connected to 1 and 3 screws are just as big as the power differential phase because there's going to hold the whole load.
In the pole 1 from the main switch and pole 3 have to go to all secondary interruptors.
Important not use this meter for loads exceeding 30A (6600W at 220V); It spoils it, cutting off the current. For that there are other counters more capable and more expensive.

Having no 4mm cable, I used two of 2.5mm in connector 3 (bottom right meter)
Abstain from opening the box if you have no experience with electricity. You can not break the seal of the Power Control Switch (corresponding to the contracted power, if you have electronic meter then this will be included in it).  This switch is housed in a box that is located "just before the rest of devices in a separate sealable compartment". It can be placed in the same box as the general control and protection devices, which should always be near the front door. Never in bedrooms, bathrooms or other rooms in our case is to the left of the picture switches:

Connecting the external clock to UNO

The clock is essential for timekeeping , so we have to connect one battery to keep time in the event that the Arduino is disconnected.
In the next picture you can see the role of the special pins; we need the SDA and SCL clock for the circuit:

As the screen cover all pins of the ONE, I decided to solder directly to the back of the plate with thin wires to circuit connecting the basic clock pin ; + 5V, COM and SDA and SCL. 

RTC module connected to + 5V, COM and SDA and SCL
To update you when I used the following program ( Nick Pyner Arduino Forum ):

//Arduino 1.0+ Only
//Arduino 1.0+ Only
// pre-set the time in the void then use reset button to set it!

#include "Wire.h"
#define DS1307_ADDRESS 0x68
byte zero = 0x00; //workaround for issue #527

void setup(){

void loop(){


void setDateTime(){

 byte second =      30; //0-59
 byte minute =      24; //0-59
 byte hour =        0; //0-23
 byte weekDay =     5; //1-7
 byte monthDay =    19; //1-31
 byte month =       4; //1-12
 byte year  =       13; //0-99

 Wire.write(zero); //stop Oscillator


 Wire.write(zero); //start 



byte decToBcd(byte val){
// Convert normal decimal numbers to binary coded decimal
 return ( (val/10*16) + (val%10) );

byte bcdToDec(byte val)  {
// Convert binary coded decimal to normal decimal numbers
 return ( (val/16*10) + (val%16) );

void printDate(){

 // Reset the register pointer

 Wire.requestFrom(DS1307_ADDRESS, 7);

 int second = bcdToDec(Wire.read());
 int minute = bcdToDec(Wire.read());
 int hour = bcdToDec(Wire.read() & 0b111111); //24 hour time
 int weekDay = bcdToDec(Wire.read()); //0-6 -> sunday - Saturday
 int monthDay = bcdToDec(Wire.read());
 int month = bcdToDec(Wire.read());
 int year = bcdToDec(Wire.read());

 //print the date EG   3/1/11 23:59:59
 Serial.print("     ");

Preparations for programming

Unfortunately I can not read the pulses using IRQ's with this placed screen because it uses digital pins 2 and 3 of UNO, which are the only ones who have to handle interruptions. 
Therefore optimizing the program to do it fast enough reading sensor and avoid losing pulseshas taken much of their time as they draw on the screen consumes a lot of time (even a simple "delete screen"). But I managed to get something functional without losing pulses. 
But you can well use another combination of hardware for the same use, or directly send the data wirelessly to another part , etc, and if yes you could use pins  interruptions  of ONE or plate used, one for use with the pulses and thus not lost even one,  as shown in the example Arduino . In addition, the meter gives 238-1 1000 pulses / kWh , other equipment giving more pulses and more accurate fuel consumption, but we do not need nor is it recommended unless we use another kind of display that does not disable us pins interruptions  (also could use another full plate as the Due or Mega ).

What a great time I step programming :)

Another issue was how to get the time; luckily had an RTC circuit , and after reading about it to catch when an external clock and use DS1307 RTC chip, which is accurate enough for what we need (discriminate hours), implemented in the program, taking note the hour winter / summer.

This Chinese screen is pretty bad , as discussed in this forum  and in the other (at least my version, they randomly go blank after a while, although the Arduino continues to collect data, and getting better release after release); Also, if you feed the Arduino with external source, is likely to cool the microSD card as you use a ... on the other hand bookstores Adafruit I used are quite slow doing things according to what (0.3 seconds to clear the display is a barbarity), but hey, a gift horse ... serviceable with what is; the false economy of many Chinese products .
So I have fun turning to a program that works, do not lose pulses, collect the data I need and also displayed on the screen; with everything I used bookstores and 23Ks of 32Ks memory has the UNO (with IDE 1.0.6, with 1.6.6 is reduced to 17Ks), and can be further reduced capando bookstores. Another thing is to find the perfect library for your screen , as evidenced by the many users with problems. I used the Adafruit, with a changed to remove the correct colors (in the pictures you can see that at first did not match the colors) line. Since the library is very slow handling text , have resorted to overwrite the data previous watts in black (putting us in the same position), and write only the numeric value and "W". In addition the screen leaves us little free PINES; but just I needed a digital PIN to count pulses, and are free as 0 and 1, I used the 0 (which you can change at the beginning of the program code). The basic operation of the program is as follows:

  1. In the part of  setup synchronize the internal clock with the external RTC initialize and read screen data stored in the EEPROM of the previous session  (important data start uncommenting the lines indicated in the code the first time, and then re- load the program back to discuss them with // as long stored in data format and it will get garbage on the screen).
  2. In the  main routine check if activated pulse , if any, but it was not before, then compare the elapsed time between pulses to  determine the instantaneous power W, and update the screen with the value , as well as update values Watts tip and count the time if you are over the slogan Watts (in order to know if we should lose the contracted power).
  3. Every minute, if we have time to update the screen with all the information, the show  (comparing the time it took last time between pulses), if no time, the update only every two hours to lose the minimum pulse possible.
  4. Every half hour, if there have been changes, we update the information in the EEPROM , indicating on screen. By my calculations, each cell 2 times FLASH EEPROM is updated every day, so even if we did every 10 minutes and not be damaged even within 45 years (100,000 enduring scriptures).
Version 1.0 of the program and debugged

For now, the program shows on screen:
  1. Instantaneous power in W.
  2. Recorded maximum instantaneous power.
  3. Total daily consumption in kWh.
  4. Total consumption in kWh night.
  5. Minutes during which it has exceeded the setpoint (For if we are always consuming less than contracted, to lower power).
  6. Total kWh per hour of accumulated day (saved if you go light).
  7. Current date and time.
  8. Date and time of last saved the EEPROM data (when it occurs).

You have the project on GitHub , and this would be the program v. 1.0: Energy Monitor TFT display with Arduino program:

/ *
 * FILE: Monitoring DDS238-1
 * AUTHOR: David Losada
 * DATE: 25/11/2015
 URL: http://miqueridopinwino.blogspot.com/monta-tu-propio-monitor-de-energia-por-30-eurillos.html
 * PURPOSE: to check consumption Prototype s / times
 * Calculation TDH in winter; in summer it is 13 to 23h http://nergiza.com/tarifa-nocturna-sigue-existiendo-es-rentable/
 * ALL: Consider whether it is summer or winter (winter set)
 * Configured for kWh meter  DDS238-1:  Give a pulse every Watt / h (1000 pulses / KWh)
 * (Change variables start to work with you use).
 * This program is free software; you can redistribute it and / or
 * Modify it under the terms of the GNU General Public License
 * Version 2 as published by the Free Software Foundation.
 * /


#include // Core graphics library
#include // Hardware-specific library
#include // To use the EEPROM
// Arduino Tiny I2C RTC http://zygzax.com

Control // The pins for the LCD can be Assigned to any digital or
// Analog pins ... but we'll use the analog pins as esta Allows us to
// Double up the pins With the touch screen (see the example paint TFT).
A3 // #define LCD_CS goes to Chip Select Analog 3
A2 // #define LCD_CD Command / Data goes to analog 2
A1 // #define LCD_WR Write LCD goes to Analog 1
A0 // #define LCD_RD LCD Analog Read goes to 0

A4 // #define LCD_RESET Can Alternately just connect to Arduino's reset pin

// When using only the BREAKOUT BOARD, Use These 8 data lines to the LCD:
// For the Arduino Uno, Duemilanove, Diecimila, etc .:
D0 // Connects to digital pin 8 (Notice These Are
D1 // Connects to digital pin 9 in order NOT!)
D2 // Connects to digital pin 2
D3 // Connects to digital pin 3
D4 // Connects to digital pin 4
D5 // Connects to digital pin 5
D6 // Connects to digital pin 6
D7 // Connects to digital pin 7
// For the Arduino Mega, use digital pins 22 Through 29
// (On the two-row header at the end of the board).

// Assign human-readable names to some common 16-bit values ​​Color:
#define  BLACK 0x0000
#define  BLUE 0x001F
#define  RED 0xF800
#define  GREEN 0x07E0
0x07FF #define CYAN
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
WHITE #define 0xFFFF

// If using the shield, all monitoring and data lines are fixed, and
// A simpler OPTIONALLY declaration can be used:
// Adafruit_TFTLCD TFT;

PowerSensor int = 0; // Pin input pulse electric meter
Luggage int = 2300; // Watts on which we want to control the minutes that we got through it to see if it's worth the power down
float PulsosKW = 1000; // HERE important to define that apply to your device; pulses per KW
// (With a maximum sensor 1000 we will have 1.3 pulses / sec. For a consumption of 5 KW)
// That is, they will spend up 1.33 sec. between pulses (1.3 + 0.03 lasting)
// Energy: 1000 imp = 1 kWh -> 1 imp = 1 Wh; 
unsigned long hours [] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 , 0.0}; // Array to store the pulses by time
long millisAnt = 0; // Milliseconds to check past tense
long millisPrevious = 0; // Find out the time since the last pulse
Tiempo_Trans float = 0; // We save the most from each detected pulse
long millisEEPROM = 0; // Bran Kwh consumed in EEPROM
long millisPant = 0; We save time interval // Full Screen
long millisRTC = 0; // Update clock
Pulse boolean = false; // Detected pulse
NoPulso boolean = false; // There was not detected after © s pulse of a pulse
unsigned long PulsosDia = 0; // Total counter KWh
unsigned long PulsosNoche = 0;
long Watts = 0; // Calculate watts
long VatiosAnt = 0;
long MaxVatios = 0; // Peak consumption watts
unsigned long TimeMax = 0; // Ms with watts> 2300
long TimePrevious = 0; // Time control screen-painting
long TimeFinal = 0;

void setup () {
  Serial.begin (9600);

// Uncomment the following lines to synchronize time by Serial
// If (Serial.available ()) 
// {
// ProcessSyncMessage time_t t = ();
// If (t> 0)
// {
RTC.set // (t); // Set the RTC and the system time to the received value
// SetTime (t);          
} //
} //
// DigitalClockDisplay ();  
// Delay (1000);
} //

  // Internal sync time to the RTC
  setSyncProvider (RTC.get); // The function to get the time from the RTC
  Serial.println ();
  if (timeStatus ()! = TimeSet) 
     Serial.println ("Unable to sync with the RTC");
     Serial.println ("RTC has Set the system time");
  Wire.begin ();
  //RTC.begin ();
  // If we remove the comment from the following line, the time and date is set by the computer
  //RTC.adjust(DateTime(__DATE__, __TIME__));
  Serial.println (F ("Working Arduino OK"));

  Serial.println (F ("Using Adafruit 2.8 \" TFT Shield Arduino pinout "));
  Serial.println (F ("Using Adafruit 2.8 \" TFT pinout Breakout Board "));

  Serial.print ("TFT size is"); Serial.print (tft.width ()); Serial.print ("x"); Serial.println (tft.height ());
  tft.reset ();

  uint16_t tft.readID identifier = ();

  // We sent by the serial ID if there is a problem with the screen
  if (identifier == 0x9325) { 
    Serial.println (F ("Found ILI9325 LCD driver"));
  } Else if (identifier == 0x9327) {
    Serial.println (F ("Found ILI9327 LCD driver"));
  } Else if (identifier == 0x9328) {
    Serial.println (F ("Found ILI9328 LCD driver"));
  } Else if (identifier == 0x7575) {
    Serial.println (F ("Found HX8347G LCD driver"));
  } Else if (identifier == 0x9341) {
    Serial.println (F ("Found ILI9341 LCD driver"));
  } Else if (identifier == 0x8357) {
    Serial.println (F ("Found HX8357D LCD driver"));
  } Else if (identifier == 0x0154) {
    Serial.println (F ("Found S6D0154 LCD driver"));
  } Else {
    Serial.print (F ("Unknown chip LCD driver:"));
    Serial.println (identifier, HEX);
  tft.begin (identifier);
  pinMode (PowerSensor, INPUT);
  // We take the pulse values ​​saved
   for (int i = 0; i <24 div="" i="" nbsp="">
     Hours [i] = EEPROMReadlong (i * 4);
   TimeMax = EEPROMReadlong (200);
   MaxVatios = EEPROMReadlong (204);

void loop () {
  // Comment out the following lines to reset the EEPROM used in the program
  0 // fill it with EEPROM data pulses
  // For (int i = 0; i <24 div="" i="" nbsp="">
  // EEPROMWritelong (i * 4.0);
  } //
  // EEPROMWritelong (200.0);
  // EEPROMWritelong (204.0);
  I //Serial.println("Borrado EEPROM finished ");
  // While (1) {delay (500);} // infinite loop to delete only once

  time_t t = now (); // Take the time
  digitalRead sensorState = int (PowerSensor);
  if (sensorState == 0) // There are few pulses per second, each lasting> = 30 msec; the important thing is "capture"
      if (Pulse) {// If detectó pulse previously, is recorded and displayed on the screen consumption WATTS
        // This operation takes 29 ms in the Arduino UNO;
        Tiempo_Trans = millis () - millisPrevious;
        millisPrevious = millis ();
        Pulse = false;
        // Save the pulse at the appropriate time
        Hours [hour (t)] = Hours [hour (t)] + 1;
        // 3600 seconds = 1 kWh = 1000 pulses 
        // We get the pulses per second that we have had and we get the W
        W = ((3600 / PulsosKW) / (Tiempo_Trans / 1000)) * 1000; // All variables used in the cálculo be float for accuracy
        if (MaxVatios
          MaxVatios = Watts; 
        if (Watts> = setpoint) {// Save the time during which exceed Watts Watts to be monitored (low power)
          TimeMax = TimeMax + (Tiempo_Trans);
        // Every time there is a pulse is updated only Watts / time to speed up drawing; well just it takes 29 ms
        tft.setCursor (120, 0);
        tft.setTextColor (BLACK); tft.setTextSize (3);
        tft.print (VatiosAnt);
        tft.print ("W");
        tft.setCursor (120.0);
        tft.setTextColor (YELLOW);
        tft.print (Watts);
        tft.print ("W");
        VatiosAnt = Watts;  
    else {
      Pulse = true; // Mark is detected pulse
  // Keep in mind that writing all this takes 1.7 seconds in the UNO; I prefer not to refresh the screen before losing pulses
  // I added timers to get the value of the previous time and if the time between pulses> time it takes to refresh screen-> Cool
  // To me I left in standby mode from time to time Need? xq idea goes, but is arranged to cool
  // Also we write if you just switched data (millis is reset every 50 days)
  // And if none of that is not met, update at least every two hours even if we lose a pulse
  if (millis () - millisPant> = 60000 && && Pulse (Tiempo_Trans> TimeFinal-TimePrevious) or millis () <2000 -="" millis="" millispant="" or=""> = 7200000) 
    TimePrevious = millis (); // Start time counter  
    millisPant = millis ();
    tft.reset (); // We reset the screen; I do every minute because I sometimes go blank
    uint16_t tft.readID identifier = (); // Read the ID screen, in my case 9325
    Serial.print ("Display ID");
    Serial.println (identifier);
    tft.begin (identifier); // Initialize the screen
    tft.setRotation (1); Rotated 90º // the display into landscape   
    tft.fillScreen (BLACK); //It is very slow
    tft.setCursor (0, 0);
    tft.setTextColor (YELLOW); tft.setTextSize (3);
    tft.print ("WATTS");
    tft.setCursor (120, 0); // We write establish position Watts
    tft.print (Watts);
    VatiosAnt = Watts;
    tft.println ("W");
    // We calculate PulsosDia and Night
    // TODO: Change the program to calculate consumption according to summer schedule dates given clock
    // For now only counts the pulses according to the hours and day and calculated KWh Night
    PulsosNoche = 0; // Start variables
    PulsosDia = 0;
    for (int i = 0; i <24 div="" i="" nbsp="">
      if (i> 21 or i <12 12:00="" 22:00="" div="" from="" hours="" to="" winter="">
      Hours PulsosNoche = PulsosNoche + [i];
      else {
      Hours PulsosDia = PulsosDia + [i];
    We // show all day and night
    tft.setTextColor (magenta);
    tft.print ("WATTS MAX");
    tft.println (MaxVatios);    
    tft.print ("TOTAL DAY:");
    tft.print (PulsosDia / PulsosKW, 1); // We indicate Kw day 1 decimal
    tft.println ("kWh");
    tft.print ("TOT NIGHT:");
    tft.print (PulsosNoche / PulsosKW, 1);
    tft.println ("kWh");
    // Show minutes during which the slogan was exceeded
    tft.setTextColor (WHITE);
    tft.setTextSize (2);
    tft.print ("Minutes> 2300Wh:");
    tft.println (TimeMax / 60000.1); // With 1 decimal
    // Print on screen KW per hour for more info
    tft.setTextColor (CYAN);
    for (int i = 0; i <24 div="" i="" nbsp="">
     tft.print (i); 
     tft.print (":"); 
     tft.print (Hours [i] / PulsosKW, 1); 
     tft.print ("");
     if (i == 3 or 7 or i == i i == == 11 or 15 or 19 or i i == == 23) {
      tft.println (); 
    // Show date and time
    tft.setTextColor (GREEN);
    tft.print (day (t));
    tft.print (+ "/");
    tft.print (month (t));
    tft.print (+ "/");
    tft.print (year (t)); 
    tft.print ("");
    tft.print (hour (t));  
    tft.print (+ "");
    tft.println (minute (t));
    //tft.print (":");
    TimeFinal = millis (); // End time counter
  // Every half hour saved to the EEPROM data have changed (not often do more to prevent premature aging of the FLASH)
  if (millis () - millisAnt> = 1800000) { 
    for (int i = 0; i <24 div="" i="" nbsp="">
     if (! EEPROMReadlong (i * 4) = Hours [i]) {// check if the value has changed; multiples of 4 to store the value
        EEPROMWritelong (i * 4 hours [i]); // Store the value of daily consumption
     if (EEPROMReadlong (200)! = TimeMax) {// We save time value in ms exceeding 2300W
        EEPROMWritelong (200, TimeMax);
     if (EEPROMReadlong (204)! = MaxVatios) {// We keep watts maximum value
        EEPROMWritelong (204, MaxVatios);
     tft.setTextColor (GREEN);
     tft.setTextSize (1);
     tft.setCursor (0.230);
     tft.print ("Data saved");
     tft.print (day (t));
     tft.print (+ "/");
     tft.print (month (t));
     tft.print (+ "/");
     tft.print (year (t)); 
     tft.print ("");
     tft.print (hour (t));  
     tft.print (+ "");
     tft.println (minute (t));

  delay (20);
// This function will write a 4 byte (32bit) long to the EEPROM at
// the specified address to address + 3.
EEPROMWritelong void (int address, long value)
      // Decomposition from a long to 4 bytes by using BitShift.
      One = Most significant // -> Four = Least significant byte
      = four byte (value & 0xFF);
      three byte = ((value >> 8) & 0xff),
      two byte = ((value >> 16) & 0xFF);
      one byte = ((value >> 24) & 0xFF)       . // Write the 4 bytes into the EEPROM memory       EEPROM.write (address, four);       EEPROM.write (address + 1, three);       EEPROM.write (address + 2, two);       EEPROM.write (address + 3, one);       } long EEPROMReadlong (long address)       {       . // Read the 4 bytes from the EEPROM memory       long four = EEPROM.read (address)       long three = EEPROM .read (address + 1)       = two long EEPROM.read (address + 2)       = one long EEPROM.read (address + 3)       . // Return the Recomposed long by using BitShift       return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);       }


To test it is enough to join the Arduino pin 0 to pin COM and drop; every time this operation is made it will make a pulse (or more depending on the quality of the connection). Finally place the powered screen as you want (I used a 12VDC transformer, but if you have one of lower voltage between 9 VDC and 6 VDC, better (to extend the life of the regulator that brings the Arduino), and the box we want, I used a Tapper because it will not be there forever), we must economize.

I paste it to the screen by placing a bit of silicone on the sides
The silicone has not worked very well, but with a sponge pressure at least has remained in place.

I put the plug into a power strip to save me welding it ;)

Finally we remove the lid of the box main differential, and I connected the power source of the Arduino to a secondary switch, and the positive (important for it to work), I mean the PIN 0 to positive counter kWh (SO +) and . the other negative (SO-)
I screwed the tapper to the wall (the hanging cable is the USB to update the Arduino:

The display unfinished version of the program with a couple of mistakes

And voila! Chapucilla other works, after two days and we see that we should switch to the TDH 2.0 , but better to wait for a week more;) to apply. The vast majority of households make more than 30% of spending for the night, so I foresee a change in mass to 2.0 TDH saving between 10 and 40% in the electricity bill ... In the next project!

More info:
How I got the bill down in 25 houses : Nergiza.com
Arduino Data Logger
Using the Excontrol DDS238-1 
Change schedule
Full description of the pins of Arduino UNO 
What can hire minimum power on Nergiza.com 
Making sure to reduce power hiring to Minimum on Nergiza 

No comments :

Post a Comment