How To Build IoT Tamper-Proof Energy Meter Using Arduino & WiFi

watch the video

In this project tutorial, we will design, construct, and program an IoT tamper-proof energy meter using Arduino and WiFi LAN dashboard. This project’s research aims to design and model an energy meter with a Wi-Fi-based anti-theft/tamper mode that precisely measures the power consumed, detects tampering activity, and notifies the authorized personnel through SMS or Email.

IoT tamper-proof energy meter

Components Needed For Tamper-Proof Energy Meter Project

ComponentsQuantity
PZEM Module with Current Transformer1
NodeMCU board1
2004 LCD module1
Dc switch1
I2C LCD module1
Single Channel Relay Module1
5V power supply1
Veroboard, wire and PCB sockets1
6×6 casing1
AC Socket1
2.5mm Wire3 yards

The Materials needed for this project design is detailed in the table above.

Tamper-Proof Energy Meter: The Circuit Diagram

Circuit diagram for IoT tamper-proof energy meter
Circuit diagram for IoT tamper-proof energy meter

The Single phase energy module PZEM as shown in the picture above is connected with having its current transformer running through the Live wire of the Public Utility Input. This current transformer is responsible for sensing the amount of current consumes by the Load.

assembling the tamper-proof energy meter

The PZEM module is programmed via serial communication protocol. This connection is  made to read the load voltage, open voltage, power factor once the MCU is connected to the module. To program this module, the connection is made as shown above. We used the Arduino IDE to program the energy module.

Programming The Project Design

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <EEPROM.h>
//include a library to h elp communicate with the LCD via I2C
#include <LiquidCrystal_I2C.h>
//include a library for the PZEM module. note that it depends on UART in this case we sed a software serial to define other pins as UART
#include <PZEM004Tv30.h>
#include <Wire.h>
#include "RTClib.h"

RTC_DS3231 rtc;

//create and instance of the LiquidCrystal, this name will be use as a class to call all the functions of the lcd library
LiquidCrystal_I2C lcd(0x27, 20, 4);
//create an instance of the PZEM004Tv30 library, this name will be used to call the functions available in the pzem library.
//the pins of the UART is also declared
PZEM004Tv30 pzem(D3, D4); // (RX, TX) OF THE SOFTWARE-SERIAL

const char* ap_ssid = "MOYIN";
const char* ap_password = "a1b2c3d4e5";
const char* ssid = "Moyin";
const char* password = "password";
const char* host = "maker.ifttt.com";
const char* apiKey = "bBoafo8uqrZq72TzdrU0ye";

const int tamper_slot = 1;
const int quota_slot = 100;
const int tamper_button = D5;
const int relay = D6;
bool tamper_flag;
bool buttonState;
bool email_Logic = false;
float quota;
int On = LOW;
int Off = HIGH;


//create global variables to hold the values of parameters to be read from the module later in the program
// we declare these variables as float because they will hold floating poin numbers, that is numbers with decimal points
float voltage;
float current;
float power;
float energy;
float frequency;
float pf;
double energy_unit;

ESP8266WebServer server(80);

//Check if header is present and correct
bool is_authenticated() {
  Serial.println("Enter is_authenticated");
  if (server.hasHeader("Cookie")) {
    Serial.print("Found cookie: ");
    String cookie = server.header("Cookie");
    Serial.println(cookie);
    if (cookie.indexOf("ESPSESSIONID=1") != -1) {
      Serial.println("Authentication Successful");
      return true;
    }
  }
  Serial.println("Authentication Failed");
  return false;
}

//login page, also called for disconnect
void handleLogin() {
  String msg;
  if (server.hasHeader("Cookie")) {
    Serial.print("Found cookie: ");
    String cookie = server.header("Cookie");
    Serial.println(cookie);
  }
  if (server.hasArg("DISCONNECT")) {
    Serial.println("Disconnection");
    server.sendHeader("Location", "/login");
    server.sendHeader("Cache-Control", "no-cache");
    server.sendHeader("Set-Cookie", "ESPSESSIONID=0");
    server.send(301);
    return;
  }
  if (server.hasArg("USERNAME") && server.hasArg("PASSWORD")) {
    if (server.arg("USERNAME") == "smartech" &&  server.arg("PASSWORD") == "1234") {
      server.sendHeader("Location", "/");
      server.sendHeader("Cache-Control", "no-cache");
      server.sendHeader("Set-Cookie", "ESPSESSIONID=1");
      server.send(301);
      Serial.println("Log in Successful");
      return;
    }
    msg = "Wrong username/password! try again.";
    Serial.println("Log in Failed");
  }
  String Home_Page = "<!DOCTYPE html>";
  Home_Page += "<html> <head> <meta name='viewport' content='width=device-width, initial-scale=1'>";
  Home_Page += "<style> body {font-family: Arial, Helvetica, sans-serif;} input[type=text], input[type=password] { width: 100%; padding: 12px 20px; margin: 8px 0; display: inline-block; border: 1px solid #ccc; box-sizing: border-box; }";
  Home_Page += "button { background-color: #04AA6D; color: white; padding: 14px 20px; margin: 8px 0; border: none; cursor: pointer; width: 100%; }";
  Home_Page += "button:hover { opacity: 0.8; } .cancelbtn { width: auto; padding: 10px 18px; background-color: #f44336; }";
  Home_Page += ".imgcontainer { text-align: center; margin: 24px 0 12px 0; position: relative; }";
  Home_Page += "img.avatar { width: 40%; border-radius: 50% }";
  Home_Page += ".container { padding: 16px; }";
  Home_Page += "span.psw { float: right; padding-top: 16px; }";
  Home_Page += ".modal { display: none; position: fixed; z-index: 1; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgb(0,0,0); / background-color: rgba(0,0,0,0.4); padding-top: 60px }";
  Home_Page += ".modal-content { background-color: #fefefe; margin: 5% auto 15% auto; border: 1px solid #888; width: 80%; }";
  Home_Page += ".close { position: absolute; right: 25px; top: 0; color: #000; font-size: 35px; font-weight: bold;}";
  Home_Page += ".close:hover, .close:focus { color: red; cursor: pointer; }";
  Home_Page += ".animate { -webkit-animation: animatezoom 0.6s; animation: animatezoom 0.6s }";
  Home_Page += "@-webkit-keyframes animatezoom { from {-webkit-transform: scale(0)} to {-webkit-transform: scale(1)} }";
  Home_Page += "@keyframes animatezoom { from {transform: scale(0)} to {transform: scale(1)} }";
  Home_Page += "@media screen and (max-width: 300px) { span.psw { display: block; float: none; } .cancelbtn { width: 100%; } } </style> </head> <body>";
  Home_Page += "<h2>Tamper-proof Energy Meter</h2>";
  Home_Page += "<h4>Click button below to access device settings.</h4>";
  Home_Page += "<button onclick=\"document.getElementById('id01').style.display='block'\" style='width:auto;'>Login</button>";
  Home_Page += "<div id='id01' class='modal'> <form class='modal-content animate' action='/login' method='post'> <div class='imgcontainer'> <span onclick=\"document.getElementById('id01').style.display='none'\" class='close' title='Close Modal'>&times;</span>";
  Home_Page += "<svg class = 'avater' version='1.0' xmlns='http://www.w3.org/2000/svg'  width='180.000000pt' height='115.000000pt' viewBox='0 0 336.000000 261.000000' preserveAspectRatio='xMidYMid meet'>";
  Home_Page += "<g transform='translate(0.000000,261.000000) scale(0.100000,-0.100000)' fill='#270F88' stroke='none'>";
  Home_Page += "<path d='M800 2486 c-90 -24 -164 -87 -207 -173 l-28 -58 -3 -627 -2 -628 -24 -23 c-52 -53 -51 -143 2 -192 74 -70 192 -43 228 50 19 51 15 76 -21 129 l-25 37 1 597 c0 426 4 608 12 636 8 24 26 51 50 70 l39 31 937 0 937 0 36 -25 c20 -13 44 -38 55 -55 17 -28 18 -76 21 -953 2 -888 2 -925 -17 -966 -14 -33 -29 -48 -63 -65 -44 -21 -45 -21 -917 -21 l-873 0 -35 30 c-58 51 -155 37 -202 -29 -25 -35 -28 -99 -6 -142 36 -69 138 -91 207 -44 l36 25 885 0 c938 0 919 -1 994 46 42 27 97 89 124 142 l24 47 0 975 0 975 -27 51 c-32 60 -93 118 -157 148 -46 21 -49 21 -991 23 -728 1 -955 -1 -990 -11z m-130 -1599 c-1 -39 -43 -48 -64 -14 -9 15 -7 21 8 32 21 16 56 4 56 -18z m168 -686 c8 -4 12 -19 10 -32 -2 -18 -9 -24 -28 -24 -19 0 -26 6 -28 24 -3 20 11 41 28 41 3 0 11 -4 18 -9z'/>";
  Home_Page += "<path d='M2276 1685 l-37 -25 -514 0 -513 0 -26 -68 c-14 -37 -26 -70 -26 -73 0 -6 46 -49 315 -294 54 -49 103 -95 109 -100 6 -6 52 -46 101 -90 50 -43 159 -144 243 -224 149 -142 153 -146 125 -152 -15 -3 -156 -7 -313 -8 -157 -1 -541 -6 -855 -10 -548 -8 -570 -7 -589 11 -37 34 -90 43 -141 24 -52 -20 -82 -54 -91 -101 -18 -99 83 -187 178 -156 19 6 44 20 54 30 19 17 71 19 894 30 481 6 929 11 995 11 l120 0 27 74 26 74 -146 134 c-81 74 -158 143 -171 154 -35 28 -144 127 -241 220 -47 45 -121 115 -165 155 -44 41 -107 100 -140 131 l-60 58 398 0 397 0 42 -27 c125 -83 272 43 204 175 -19 38 -81 72 -129 72 -20 0 -50 -10 -71 -25z m112 -102 c14 -27 3 -43 -29 -43 -24 0 -29 4 -29 23 0 13 3 27 7 30 12 13 42 7 51 -10z m-2160 -1034 c3 -28 -16 -43 -43 -33 -15 6 -21 45 -8 58 16 16 48 1 51 -25z'/> </g></svg>  </div>";
  Home_Page += "<div class='container'> <label for='uname'><b>Username</b></label>";
  Home_Page += "<input type='text' placeholder='Enter Username' name='USERNAME' required>";
  Home_Page += "<label for='psw'><b>Password</b></label>";
  Home_Page += "<input type='password' placeholder='Enter Password' name='PASSWORD' required>";
  Home_Page += "<button type='submit'>Let Me In</button>";
  Home_Page += "<label> <input type='checkbox' checked='checked' name='remember'> Remember me </label> </div>";
  Home_Page += "<div class='container' style='background-color:#f1f1f1'>";
  Home_Page += "<button type='button' onclick=\"document.getElementById('id01').style.display='none'\" class='cancelbtn'>Cancel</button>";
  Home_Page += "<span class='psw'>Forgot <a href='#'>password?</a></span> </div> </form> <br>" + msg + "</br> </div>";
  Home_Page += "<script> var modal = document.getElementById('id01'); window.onclick = function(event) { if (event.target == modal) { modal.style.display = 'none'; } } </script> </body> </html>";

  server.send(200, "text/html", Home_Page);
}

//root page can be accessed only if authentication is ok
void handleRoot() {
  Serial.println("Enter handleRoot");
  String header;
  if (!is_authenticated()) {
    server.sendHeader("Location", "/login");
    server.sendHeader("Cache-Control", "no-cache");
    server.send(301);
    return;
  }
  if (server.hasHeader("Cookie")) {
    Serial.print("Found cookie: ");
    String cookie = server.header("Cookie");
    Serial.println(cookie);
  }
  if (server.hasArg("DISCONNECT")) {
    Serial.println("Disconnection");
    server.sendHeader("Location", "/login");
    server.sendHeader("Cache-Control", "no-cache");
    server.sendHeader("Set-Cookie", "ESPSESSIONID=0");
    server.send(301);
    return;
  }
  //char temp[100000];
  int sec = millis() / 1000;
  //int sec = counter % 60;
  //unsigned long min_counter = millis() / 3600000;
  int min = sec / 60;
  int hr = min / 60;

  String temp;//, 50000,
  temp += "<!DOCTYPE html><html><head>";
  temp += "<meta http-equiv='refresh' content='5'/> <meta name='viewport' content='width=device-width, initial-scale=1.0'/><title>Smart Meter</title><style>";
  temp += "body {horizontal-align:center; background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; width: 100%; float: center;}";
  temp += "table {font-size:15px;margin-left: auto;margin-right: auto;text-align:center; font-family: arial, sans-serif; border-collapse: collapse; width: parent;}";
  temp += "td, th { border: 1px solid #dddddd; text-align: left; padding: 8px;} tr:nth-child(even) {background-color: #dddddd;}div {float: center; width: parent;overflow-x:auto;}";
  temp += "h1{text-align:center;width:auto;font-family:Segoe UI,Arial,sans-serif;font-weight:400;margin:10px 0}";
  temp += "h4{text-align:center;font-size:48px!important; position:relative;}p {text-align:center;}</style></head>";
  temp += "<body><div><h1>Tamper-proof Energy Meter </h1><h4>Designed by Moyin_Oluwa </h4></div>";
  temp += "<p>Running Time   " + String(hr) + ":" + String(min % 60) + ":" + String(sec % 60) + "</p><br><br><br>";
  temp += "<p>Please see table below for your real-time energy consumption</p><br>";
  temp += "<div><table><tr><th>Voltage</th><th>Current</th><th>Power</th><th>Energy</th><th>Frequency</th><th>Power Factor</th></tr><tr>";
  temp += "<td>" + String(voltage) + "V</td>";
  temp += "<td>" + String(current) + "A</td>";
  temp += "<td>" + String(power)   + "kW</td>";
  temp += "<td>" + String(energy)  + "kWH</td>";
  temp += "<td>" + String(frequency) + "Hz</td>";
  temp += "<td>" + String(pf) + "</td></tr></table>";
  temp += "<p ><h3> You can access this device <a href='/setting'>SETTINGS PAGE</a> here..</p></div>";
  temp += "<p ><h3> You can access this page until you <a href='/login?DISCONNECT=YES'>Log Out</a></p></body></html>";

  server.send(200, "text/html", temp);
}

//no need authentication
void handleNotFound() {
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i = 0; i < server.args(); i++) {
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
}

void handleSetting() {
  Serial.println("Enter settings mode");
  String header;
  String restart_msg;
  String tamper_msg;
  String energy_msg;
  String reading_msg;
  if (!is_authenticated()) {
    server.sendHeader("Location", "/login");
    server.sendHeader("Cache-Control", "no-cache");
    server.send(301);
    return;
  }
  if (server.hasHeader("Cookie")) {
    Serial.print("Found cookie: ");
    String cookie = server.header("Cookie");
    Serial.println(cookie);
  }
  if (server.hasArg("DISCONNECT")) {
    Serial.println("Disconnection");
    server.sendHeader("Location", "/login");
    server.sendHeader("Cache-Control", "no-cache");
    server.sendHeader("Set-Cookie", "ESPSESSIONID=0");
    server.send(301);
    return;
  }
  if (server.hasArg("ENERGY")) {
    String energy_obj = server.arg("ENERGY");
    if (energy_obj.toInt() != 0) {
      quota += energy_obj.toInt();
      EEPROM.write(quota_slot, quota);
      if (EEPROM.commit()) {
        energy_msg = energy_obj + " - Units has been added to the user energy quota....\n\n";
        Serial.println(energy_msg);
        Serial.println(quota);
      }
    }
  }

  if (server.hasArg("READING")) {
    if (server.arg("READING") == "on") {
      pzem.resetEnergy();
      reading_msg = "Meter Reading Cleared......\n\n";
      Serial.println(reading_msg);
    }
  }
  if (server.hasArg("TAMPER")) {
    if (server.arg("TAMPER") == "on") {
      EEPROM.write(tamper_slot, false);
      EEPROM.write(quota_slot, 0);
      Serial.println(energy_msg);
       Serial.println(quota);
     if (EEPROM.commit()) {
        Serial.println("Device tamper erased");
        tamper_msg = "Tamper mode deactivated, Please restart device to see it working.\n User Existing Quota will be lost too. \n\n";
        Serial.println(tamper_msg);
      }
    }
  }
  if (server.hasArg("METER")) {
    if (server.arg("METER") == "on") {
      restart_msg = "Restarting Device............\n\n";
      Serial.println(restart_msg);
    }
  }

  String settings = "<!DOCTYPE html>";
  settings += "<html><head><meta name='viewport' content='width=device-width, initial-scale=1'></head>";
  settings += "<body><h2>Admin Settings Page</h2><br><br><br>";
  settings += "<form action='#' method='post'><label for='energy quota'>Energy:</label>";
  settings += "<input type='number' placeholder='Enter Energy Units' name='ENERGY'> <br><br>";
  settings += "<label> <input type='checkbox' id='myCheck' name='METER'> Restart Meter </label><br><br>";
  settings += "<label><input type='checkbox' name='READING'> Clear Readings </label><br><br>";
  settings += "<label><input type='checkbox' name='TAMPER'> Clear Tamper</label><br><br>";
  settings += "<button type='submit'>* SAVE *</button></form>";
  settings += "You can access this page until you <a href=\"/login?DISCONNECT=YES\">Log Out</a><br><br><br>";
  settings += energy_msg + "<br><br>" + reading_msg + "<br><br>" + tamper_msg + "<br><br>" + restart_msg + "</body></html>";
  server.send(200, "text/html", settings);

  energy_msg = "";
  reading_msg = "";
  tamper_msg = "";
  restart_msg = "";

  if (server.hasArg("METER")) {
    if (server.arg("METER") == "on") {
      Serial.println("Please Wait....");
      Serial.println("Disconnection");
      server.sendHeader("Location", "/login");
      server.sendHeader("Cache-Control", "no-cache");
      server.sendHeader("Set-Cookie", "ESPSESSIONID=0");
      server.send(301);
      Serial.println(restart_msg);
      delay(2000);
      ESP.restart();
      return;
    }
  }
}

ICACHE_RAM_ATTR void tamper_monitor() {
  tamper_flag = true;
  EEPROM.write(tamper_slot, true);
  if (EEPROM.commit()) {
    Serial.println("Tamper DETECTED!!!");
  }
}

void setup(void) {
  Wire.begin(); // Start the I2C
  lcd.init();
  rtc.begin();  // Init RTC
  // Turn on the blacklight and print a message.
  lcd.backlight();//turn on the lcd backlight
  lcd.setCursor(0, 0); //set the coordinate for the lcd cursor. this is the point where the lcd start writing.
  lcd.print("Hello, Moyin!");// ouput the text om the lcd screen
  delay(3000);
  Serial.begin(115200);
  EEPROM.begin(512);
  delay(1000);
  pinMode(tamper_button, INPUT);
  pinMode(relay, OUTPUT);
  tamper_flag = EEPROM.read(tamper_slot);
  quota = EEPROM.read(quota_slot);

  attachInterrupt(digitalPinToInterrupt(tamper_button), tamper_monitor, FALLING);
 
if (!rtc.begin() || rtc.lostPower()) {
    Serial.println("RTC lost power, lets set the time!");
    tamper_flag = true;
  EEPROM.write(tamper_slot, true);
  if (EEPROM.commit()) {
    Serial.println("Tamper DETECTED!!!");
     }
     rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    }
   
  WiFi.mode(WIFI_AP_STA);
  Serial.println("Current Mode is Access Point Only");
  Serial.println("Configuring Access Point...");
  WiFi.softAP(ap_ssid, ap_password);
  IPAddress myIP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(myIP);
  Serial.println("");

   Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
    delay(3000);
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot);
  server.on("/login", handleLogin);
  server.on("/setting", handleSetting);
  server.onNotFound(handleNotFound);

  //here the list of headers to be recorded
  const char * headerkeys[] = {"User-Agent", "Cookie"} ;
  size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*);
  //ask server to track these headers
  server.collectHeaders(headerkeys, headerkeyssize);
  server.begin();
  Serial.println("HTTP server started");
   
     }


void loop(void) {
  server.handleClient();
  voltage = pzem.voltage();//request for the voltage reading and store it in out earlier declared volatge
  if ( !isnan(voltage) ) { //if the reading is  available, display it on cosole
    Serial.print("Voltage: "); Serial.print(voltage); Serial.println("V");
  } else {//if not availabe print an error message as below
    Serial.println("Error reading voltage");
  }
  current = pzem.current();
  if ( !isnan(current) ) {
    Serial.print("Current: "); Serial.print(current); Serial.println("A");
  } else {
    Serial.println("Error reading current");
  }

  power = pzem.power();
  if ( !isnan(power) ) {
    Serial.print("Power: "); Serial.print(power); Serial.println("W");
  } else {
    Serial.println("Error reading power");
  }

  energy = pzem.energy();
  if ( !isnan(energy) ) {
    Serial.print("Energy: "); Serial.print(energy, 3); Serial.println("kWh");
  } else {
    Serial.println("Error reading energy");
  }

  frequency = pzem.frequency();
  if ( !isnan(frequency) ) {
    Serial.print("Frequency: "); Serial.print(frequency, 1); Serial.println("Hz");
  } else {
    Serial.println("Error reading frequency");
  }

  pf = pzem.pf();
  if ( !isnan(pf) ) {
    Serial.print("PF: "); Serial.println(pf);
  } else {
    Serial.println("Error reading power factor");
  }

  while (tamper_flag == true) {
    server.handleClient();
    digitalWrite(relay, Off);
    Serial.println("Tamper Mode Activated");
    delay(1000);
    lcd.setCursor(0, 0);
    lcd.print("Tamper Mode Activate");
    lcd.setCursor(0, 1);
    lcd.print("   Meter disabled!  ");
    if(email_Logic != true){
  if(sendRequest()){
    email_Logic = true;
  }
  else{email_Logic = false;}
  }
  }

  // the following lines tell the MCU to write the measured values on the lcd at designated coordinates
  energy_unit = ((quota / 1000.0) - energy);
  lcd.setCursor(0, 2);
  lcd.print("Energy:  Volt: Unit:");
  lcd.setCursor(0, 3);
  lcd.print(energy, 3);
  lcd.print("kWH    ");
  lcd.setCursor(10, 3);
  lcd.print(voltage, 0);
  lcd.print("v   ");
  lcd.setCursor(15, 3);
  lcd.print(energy_unit , 3);
  lcd.print("");



  Serial.println();
  Serial.print("Remaining Energy unit is: ");
  Serial.println(energy_unit, 8);
  Serial.print("Remaining quota is: ");
  Serial.println(quota);

  //    Serial.println(reset_state);

  if ((energy_unit) <= 0) {
    digitalWrite(relay, Off);
    lcd.setCursor(0, 0);
    lcd.print("Out of Energy Quota!");
    lcd.setCursor(0, 1);
    lcd.print("  Buy More Units  ");
  }
  else {
    digitalWrite(relay, On);
    lcd.setCursor(0, 0);
    lcd.print("Power:  P.F:   Curr:");
    lcd.setCursor(0, 1);
    lcd.print(power, 1);
    lcd.print("W    ");
    lcd.setCursor(8, 1);
    lcd.print(pf * 100, 1);
    lcd.print("%    ");
    lcd.setCursor(14, 1);
    lcd.print(current, 3);
    lcd.print("A");
    lcd.print("");
  }
  delay(2000);
} 

Explanation of the Source Code

The source code written above also contains HTML, CSS and a little bit of Javascript functions. The whole source code aims at promoting the WiFi LAN based dashboard where the user and admin can monitor the project remotely.

Bare minimum sketch

The picture diagram above shows the Bare minimum source code (also called sketch) on the Arduino Software IDE. It illustrates only two functions: the setup() function and the loop() function. This program code is used to reset every module to it has nothing running inside previously. Hence we applied this also here. The setup() function runs any program which is within it once and proceeds to the function while the loop function runs any command or syntax placed within it repeatedly. The setup() function was used as shown below to set the baudrate (communication speed) between the personal computer (PC) and the microcontroller.

Source code for tamper-proof energy meter

The program code began with calling the vital libraries (important pieces of code needed in the program). These libraries are the Softwareserial library and the PZEM library. The former tells the complier that we are using the software serial method for communication with the PZEM module rather than the hardware. This means that we are defining our own pins for the serial communication pins; namely, the Receiver and Transmitter pins (this is shown in code line 24 and 25 respectively). We called the PZEM library to make used of its function syntax. Which is shown how it is passed to serial communication in code line 29 and 30. As mentioned above, the serial communication is set to 115200 baudrate in the function setup() at code line 24.

Source code for tamper-proof energy meter

The function begins with printing the hexadecimal address of the PZEM module and the AC voltage read by the sensor in floating variable. Other AC power parameter was also read by the sensor. These were current, power, energy, frequency and power factor (PF). This is shown in code line 42 through 48. We used an if condition to check when there was error reading any of the parameters.  This was to be displayed on the Serial monitor.  However if there wasn’t any error in reading any of the above parameters, the parameters read would be displayed using the else statement condition shown in code line 66 through 71.

Interfacing and Programming LCD

connecting I2C LCD module to LCD

The 2004 LCD was interfaced to the design as shown in the figure above; using the Intra-Intra Circuit (I2C). This is very possible using I2C LCD module. It allowed us to communicate to the LCD module using 2-wire method rather than the 4-wire data or 8-wire data method. This 2-wire method are the Serial Data (SDA) and the Serial Clock (SCL) wire.

This allowed us to use the I2C backlight() function to turn on the LED of the LCD module and print a display of welcome message. And cleared the LCD screen after 3 seconds of displaying the message. The loop() displays the parameters on the LCD module. This si shown in code line 89 through 110. The syntax makes use of cursor position in its lcd.setCursor() function. This means that the LCD was segmented into rows and columns. The first row was row 0. And since it was a 20×4 LCD module, the last row was row 3. The print() function prints string data type or character to where the cursor is placed.

Results

Testing the Tamper-proof energy meter

When powered the LCD displays the name of the user and a welcome message. and it will also display the project name and energy parameters that are metered.

Testing the Tamper-proof energy meter

Interfacing Tamper-Proof Mechanism

The system was needed detect theft by noticing when a user has tampered with it by opening the casing. To get this done, we used a pushbutton which we placed at the top of the case cover as shown in the diagram below.

tamper=proof mechanism

The program code for the schematic above is written thus:

const int tamper_slot = 1;
const int quota_slot = 100;
const int tamper_button = D5;
const int relay = D6;
bool tamper_flag;
bool buttonState;
bool email_Logic = false;
float quota;
int On = LOW;
int Off = HIGH;

The program allowed us to detect intrusion as one would open the meter when it is powered. As the system was meant to be placed on High tension distribution pole. The opening would mean that the user would want to bypass the legal output channel to avoid paying for bills.

However, this mechanism of theft demanded we attached a 3.3V LiPo battery to keep the system on even when there is no Utility supply. This battery is recharged when the Utility supply returns. The downside to this method is that if the Utility supply is not restored for a long period of time, the battery dies of overuse and undercharged state.

To solve this problem, we had to think of way to use an RTC (real time clock) module. This was able to keep the time and wait for a system mode of No Power.  Each RTC chip comes with an external battery that allows it to keep time even when the design it is connected to goes offline. This external battery was removed and the and the RTC was powered directly by the utility supply via the power supply. When the device is being tampered with, the pushbutton would go into tamper mode and the RTC would record the power interruption.

The IoT dashboard for Tamper-proof energy meter
The IoT dashboard for Tamper-proof energy meter

Here is the WiFI dashboard of the project displayed on a mobile port. The source code above has web development script of HTML that renders the code to produce such and interface as shown here. It also has inline CSS and a little bit of JS code. Once the user connects to the LAN WiFi, he or she can go to the IP address and see these web results.

The admin page

And this is the admin dashboard. For the admin, he can log in and with his secured login details and do some admin functions. like reset the meter and if the user has tampered with the meter, and it is locked, he could swipe the tampered mode and restart the meter.

Conclusion

So far we have designed and constructed an IoT tamper-proof energy meter. For us to send these values to the cloud, we will add the Cayenne libraries and export the measured and metered energy parameters to Cayenne mydevices designed dashboard. See this project on how to do that. The Cayenne dashboard will also allow us to set notifications or alerts in the form of email or SMS depending on the factors we set for. Leave us a comment about what you think about the project design.

Read More

Leave a Reply

Your email address will not be published. Required fields are marked *