Project Home Sensors – First Results

Goal of the project

Get a better understanding of the electricity and gas used in my home, which hopefully leads to gaining more control on my electricity and gas bill.

My home

The heating is solely provided by floor heating. Gas is used by a boiler to heat up cold water, which is then pumped through water tubes just near the tiles on the floor of the ground floor and bathroom. Since the larger part of my energy bill comes from gas usage, my focus in this project will be just on that.

Set-up

Components used

  • 3x 400 Point Breadboard
  • 3x MB102 Breadboard Power Supply
  • 3x 9V 1A AC-DC Voltage Adapters
  • 1x LM35 Temperature sensor
  • 1x Cable with one RJ11 connector to fit the P1 port of the smart meter
  • 1x 10k Ohm resistor, because of hardware inversion of the smart meter signal (0->1; 1-> 0)
  • Quite some 10cm Female-Female jumper cables: used extra jumper cables from ESP8266 to breadboard so I could easier connect to the USB-to-TTL FT232 converter – allowed me to prevent from lose connections on the adapter plate caused by adding and removing jumper cables every time I need to program the little chip
  • 1x USB-to-TTL FT232 converter module, for uploading code tot ESP8266

Libraries used in code

  • Self made NTP library using example code https://www.arduino.cc/en/Tutorial/UdpNTPClient; Retrieves time and date from a time server using the Network Time Protocol.
  • ESP8266WiFi. Connecting to WiFi, used for requesting date and time
  • ThingerWifi. Connecting to Thinger platform and data exchange

Libraries used during debugging phase

  • OLED.h (esp8266-OLED-master). Printing debug output to 0.96” 128x64 I2C OLED display
  • EEPROMAnything.h. Storing debug output permanently in ROM of ESP8266
  • user_interface.h. Obtaining detailed reset information from ESP8266

Challenges: Smart meter data logger

Most challenging of this project so far was the smart meter logger. On the internet quite some good instructions can be found on how to retrieve and interpret data messages form the smart meter. It for example entails a hardware inversion of the signal (not supported by the default HardwareSerial functions, only by the SoftwareSerial library), filtering out the character bits, and parity checking the bits received.
The tricky part came a bit later, when I discovered the ESP crashed randomly. There was not a trivial line of code responsible for the crashes, so I learned about the great DEBUG mode of thinger and about the ‘ESP Exception Decoder’ Arduino IDE plugin. It turned out that the WiFi connection somehow got lost, while the temperature logger ESP and floor heating pump ESP did not have this problem at al. Reading some post on the internet I got a bit suspicious about the combination of ESP8266, WiFi and the SofwareSerial library. Changing the SoftwareSerial library for the default HardwareSerial did the trick. The Smart meter data logger ESP was stable right away.

Example message as received from the smart meter:
/ISk5\2MT382-1004
0-0:96.1.1(5A424556303035313833383732303133)
1-0:1.8.1(03269.476kWh) [cumulative electricity usage, night tariff]
1-0:1.8.2(03306.834
kWh) [cumulative electricity usage, day tariff]
1-0:2.8.1(00000.003kWh)
1-0:2.8.2(00000.005
kWh)
0-0:96.14.0(0001)
1-0:1.7.0(0000.12kW) [present electricity usage]
1-0:2.7.0(0000.00
kW)
0-0:17.0.0(0999.00*kW)
0-0:96.3.10(1)
0-0:96.13.1()
0-0:96.13.0()
0-1:24.1.0(3)
0-1:96.1.0(4730303135353631313037383336383133)
0-1:24.3.0(170430190000)(00)(60)(1)(0-1:24.2.1)(m3)
(01929.567) [cumulative gas usage]
0-1:24.4.0(1)
!

Dashboard

The dashboard shows the relation between the living room temperature, floor heating pump status, and gas usage. The dashboard isn’t perfect yet, since the time scale of the floor heating pump graph does not match the other ones. This is because the floor heating data bucket used for this dashboard only contains data points for the moments the floor heating pump was on.

What can we see on this dashboard?

  • Around 06h00 it was rather cold in the living room; The floor heating pump was turned on and gas was used. Note that the cumulative gas usage is only updated once every hour by the smart meter, so the rest of the hour you will read the same values from the P1 port (this only holds for gas, not for electricity).
  • At 18h00 the floor heating pump was also on, probably because of its internal safety to turn the pump on every 24 hour to prevent it from mechanical breakdown.
  • We prepared dinner between 19h00-20h00, which you can see from the gas usage.
  • Between 07h00 and 09h00 gas was used as well, however the floor heating pump was not turned on during this timeframe. Weird… Have to investigate this further (see next section).

Outlook: Improvements & Extensions

  • Improvement: Let another ESP (e.g. the temperature sensor ESP) check whether the floor heating pump ESP is on or off. Why? It allows met to create a clearer dashboard, showing 0 or 1 instead of only allows met to create a clearer dashboard, showing 0 or 1 instead of only dots when the floor heating pump ESP is on.
  • Extension: Get a closer look at the communication between my thermostat and boiler. This implies I will have to take a look at the OpenTherm protocol which is used for the communication. Why? Sometimes I detect gas usage (not from cooking or using hot water) while the floor heating is off. It might be the case that the boiler is not turned on long enough, because of which the floor heating pump is not turned on (pump is only turned on when the temperature of the water supplied is higher than 35 degrees Celsius).
  • Extension: Place a temperature sensor on the top floor (open connection with ground floor via stairs) and a temperature sensor outside in the garden; Maybe even a complete weather station outside. Why? I am curious how the temperature on the top floor relates to the temperature on the ground floor. And I am curious how the weather outside affects the temperature in my home.
  • Extension: Dashboards showing the energy consumption per day per hour. Why? It gives me a better picture of what part of my energy consumption is spend on which part of the day or week.

Code for the Smart Meter Data Logger

// libraries
#include <ESP8266WiFi.h> // connecting to wifi
#include <ThingerWifi.h> // thinger communication via wifi
#include <NTPTime_ThijsNugteren.h> // time sync using Network Time Protocol

// parameters for sync with NTP server
char ssid[] = "your-ssid";
char password[] = "your-password";
char deviceName[] = "SmartMeter";

// constants for thinger communication
#define USERNAME "your-username"
#define DEVICE_ID "SmartMeter"
#define DEVICE_CREDENTIAL "your-credential"
#define SSID_ "your-ssid"
#define SSID_PASSWORD "your-password"
ThingerWifi thing(USERNAME, DEVICE_ID, DEVICE_CREDENTIAL);
  
// variables used in thinger communication
String electricityLowTariffCumulative = String("");
String electricityHighTariffCumulative = String("");
String electricityPresentValue = String("");
String gasCumulative = String("");
long timestampDate;
long timestampTime;
int messageCounter = 0;

// variables used in code
boolean parityError = false;
boolean messageError = false;
uint8_t line = 0;
char c;
String data = "";

void setup() 
{
  // start serial communication
  Serial.begin(9600);
  delay(250);
  
  // get NTP time, restart ESP if not successful
  if (!getNTPtimeUDP(ssid, password, deviceName, 2)){ESP.restart();}

  // thinger.io config
  thing.add_wifi(SSID_, SSID_PASSWORD);
  thing[deviceName] >> [](pson& out){
    out["Electricity - present value"] = electricityPresentValue.toFloat();
    out["Electricity - low tariff cumulative value"] = electricityLowTariffCumulative.toFloat();
    out["Electricity - high tariff cumulative value"] = electricityHighTariffCumulative.toFloat();
    out["Gas - cumulative value"] = gasCumulative.toFloat();
    out["Timestamp - date"] = timestampDate;
    out["Timestamp - time"] = timestampTime;
    out["Message counter"] = messageCounter;
  };
  
}

void loop()
{ 
  if (Serial.available()) { 
    c = Serial.read();
    
    // parity checking (even parity)
    uint8_t parityBit = bitRead(c,7);
    uint8_t bitSum = bitRead(c,0) + bitRead(c,1) + bitRead(c,2) + bitRead(c,3) + bitRead(c,4) + bitRead(c,5) +     bitRead(c,6);
    if (parityBit != (bitSum % 2)){
      parityError = true;
    }
    
    // remove parity bit
    c &= ~(1 << 7); // bitwise AND with B01111111. Removing first bit (parity bit)
    data.concat(c);
    if (c == 10){ // 'LF' line feed
      // extract metering data, may still contain errors though!
          if (line == 3){
        electricityLowTariffCumulative = data.substring(10,19);
      }
      else if (line == 4){
        electricityHighTariffCumulative = data.substring(10,19);
      }
      else if (line == 8){
        electricityPresentValue = data.substring(10,17);
      }
      else if (line == 17){
        gasCumulative = data.substring(1,10);
      } 
      if (data.indexOf("!") > -1){
        if (line != 19){ // message does not contain the expected number of lines
          messageError = true;
        }
        if (!parityError and !messageError){
          messageCounter++; // correct message
          if (messageCounter == 10000){messageCounter = 1;} // prevent from overflow   
          timestampDate = year()*10000 + month()*100 + day(); // format timestampDate: YYYYMMDD
          timestampTime = hour()*10000 + minute()*100 + second(); // format timestampTime: HHMMSS      
        }
        // prepare for next data message
        line = 0;
        messageError = false;
        parityError = false;
        data = "";
      }
      else{
        line = line + 1;  
        data = "";
      } //  message not finished yet
    } // line not finished yet
  } // character available
  else{
    yield();
    thing.handle();
  }
}