How to make an IoT energy monitor

A photo of a DIY energy monitor using a current sensor.

In this post, I’ll explain how I made an IoT energy monitor for my home using a current sensor, an ESP32 and a raspberry pi. I’ll show you why I made it and how you can make one too. Here is a link to the github repo.

Why make an energy monitor

I decided to create an IoT energy monitor to check on my home’s electricity use.

The idea was partly out of curiosity, and also to help me in another project: Installing solar panels on the roof.

Solar panels are expensive and the return on investment can take several years, so I wanted to figure out how many I needed to bring my electricity costs to a minimum.

How the energy monitor works

The theory

Electric power, measured in Watts can be expressed as:

Watts (W) = Current (I) x Voltage (V)

In Mexico voltage is provided at 120V. Other parts of the world use 240V.

If we assume 120V as a constant, by measuring the current in our circuit we can calculate reasonably well the electric power consumption at a moment in time.

We can then log the measurements in a database and then add the usage over time to get the total energy use.

As a side benefit, we can also visualize the data and analyze the information in other ways to get insights.

How I built it

To measure current, I used a SCT-013 non invasive current sensor.

I connected it to an ESP32 micro-controller to read and send the data through WiFi, using the MQTT protocol.

The ESP32 takes measurements every 5 minutes and transforms the sensor value in mA to an estimate in Amps.

Voltage values are hard-coded to 120V but can you could substitute it with a voltage sensor if you want more precise values.

Measurements are then sent through WIFI by the ESP32 to an MQTT broker installed in a Raspberry Pi.

Using a program called Node-Red, the Raspberry then writes the MQTT values into an influxDB Database for storage.

Finally, I used an open source data visualization software called Grafana to display the data in a visually appealing and interactive way.

This way, I can check on my IoT energy monitor from my cell phone or computer at any time when I’m home.

Components of energy monitor

My house has 3 separate electric lines, so I built the energy monitor to receive inputs from three sensors.

An ESP32 micro-controller is needed for the 3 sensor version, but for a 1 sensor version, an ESP8266 will also work. (See the image of the first prototype below).

Hardware component list:

  • 1 ESP32
  • 3 SCT-013 current sensor
  • 3 10uF Capacitors
  • 9 10k Resistors
  • 3 20k Resistors
  • An Electrical connection to power the ESP32
  • A breadboard or PCB
  • A Raspberry Pi

Software:

  • ESP8266 Board library, installed from the Boards Manager
  • Arduino Library: EspMQTTClient.h
  • Emonlib Library (Part of the open energy monitor project)
  • Mosquitto MQTT

Extras:

I designed a 3D printed case for the protect to give it a nicer look. You can find the 3D model here.

For hardware connections, I also made my own PCB using a mini CNC machine. You can also make your wiring look nicer by getting a PCB made by a professional company. Its very cheap, you just have to wait a bit for them to arrive.

How to build the monitor

Electrical diagram

If you want to build the sensor, you can simply follow the electrical diagram below. The circuit for the first sensor is simply duplicated for the other two sensors:

An electrical diagram of the energy monitor using an ESP32 and three SCT-013 current sensors
The electrical diagram of the energy monitor. It uses one ESP32 and three SCT-013 current sensors with some resistors and capacitors.

Designing the wiring diagram

To create the diagram above, I went over several IoT projects that use the SCT-013 current sensor, which I list in the resources section at the end of the post. After that it was a bit of tinkering and lot of trial and error.

I initially got a single sensor to work using a breadboard and an ESP8266. When I was sure it worked, I created a 3D printed version of the diagram to make the wiring easier to understand and share:

Energy monitor prototype using an ESP8266 and a sigle SCT-013 current sensor
The first energy monitor prototype using an ESP8266 and a single SCT-013 current sensor. The diagram is 3D Printed.

Then I went back to the breadboard and added the circuits for the two other sensors. That’s when I figured that the ESP8266 only has one analog input pin, and I needed to switch to an ESP32 If I wanted to use multiple sensors.

An IoT Energy monitor prototype using a breadbord and ESP32 and a three SCT-013 current sensors
The next version of the IoT Energy monitor prototype using a breadbord and ESP32 and a three SCT-013 current sensors

Making the PCB

Once I got it to work it was time to move the circuit to a more permanent setup. Since I had recently bought a mini CNC machine, I decided to make the PCB design and carve the PCB out.

The PCB Diagram for the Energy Monitor using a ESP32 and three SCT-013 current sensors.
PCB wiring diagram Using Autodesk Eagle

I initially made a wiring diagram using Fritzing, a simple electronic design software, but then had a really hard time translating the output file into CNC instructions, so I ended up switching to Auto desk Eagle, which has a plugin to do just that (PCB Gcode).

If you decide to send your PCB design to a manufacturer, Fritzing will work perfectly.

To test everything out, I made a few prototypes using cardboard and then went for the real one:

CNC Machine milling a PCB prototype of the DIY Energy Monitor
CNC Machine milling a PCB prototype of the DIY Energy Monitor

When you use a CNC router, the copper connections that substitute cables must be thicker than in a normal PCB.

Thin connections are a problem with the CNC router, since the router bits can cut into the connectors and break the connection. There is a fair bit of tinkering needed to get it right, but I still think its worth it for rapid prototyping.

Here are a few images of how the PCB came out:

Cardboard and copper PCB tests using the CNC machine for the IoT Energy Monitor
Cardboard and copper PCB tests using the CNC machine for the Energy Monitor
Finished energy monitor PCB in a vice ready to be soldered.
Finished energy monitor PCB in a vice ready to be soldered.

Connecting the components

After making the PCB, I labeled and soldered everything on the other side:

Backside of energy monitor pcb with components ready to solder
Backside of energy monitor pcb with components ready to solder

To protect the copper from corrosion, I added transparent nail polish to the front.

Energy monitor PCB next to ESP32 microcontroller soldered and connected
Energy monitor PCB next to ESP32 micro-controller soldered and connected

Once the PCB was ready, i connected everything and tested it.

Energy monitor PCB back view with components connected to the ESP32 and the SCT-013 sensors
Energy monitor PCB back view with components connected to the ESP32 and the SCT-013 sensors

Some pieces had to be re-soldered because the sensor wires got bent and kept breaking, but in the end everything worked.

The main lesson for this stage was: Don’t add nail polish until you’ve finished testing.

Once everything worked, I got the components in the 3D printed case and glued everything together.

Finished energy monitor with 3D printed case: Back View
Finished energy monitor with 3D printed case: Back View
Finished energy monitor with 3D printed case: Front view without lid
Finished energy monitor with 3D printed case: Front view without lid
Finished energy monitor with 3D printed case: Front view with lid
Finished energy monitor with 3D printed case: Front view with lid

Adding the software

After I finished setting up the sensor, I loaded the Arduino file into the ESP32.

This is the code for the sensor. Its also available in the github repo.

// Code to measure current using three SCT-013 and one ESP32
// Board used: NODEMCU-32S

// Import Emon Library from the open energy monitor project
#include "EmonLib.h"

// Mills Function 
const unsigned long event_interval = 300000; // 5 minute interval, 300 second
unsigned long previous_time = 0; 

// Include MQTT Connection
#include "EspMQTTClient.h" 

// Define input pins
#define ADC_INPUT_1 34 // Sensor 1
#define ADC_INPUT_2 32 // Sensor 2
#define ADC_INPUT_3 35 // Sensor 3

#define WIFI_SSID        "<WiFi_username_here>"
#define WIFI_PASS        "<WiFi_password_here>"

#define BROKER_IP        "<local_ip_address>" 
#define BROKER_USERNAME  "<broker_username>"
#define BROKER_PASSWORD  "<broker_password>"
#define CLIENT_NAME      "<MQTT_name_to_identify_device>"
#define BROKER_PORT      1883
#define lastwill_topic   "<last_will_topic>"
#define lastwill_text    "Current sensor has gone offline unexpextedly."

String  client_name                = CLIENT_NAME;
String  startup_topic              = "<startup_topic>";
String  medidor_corriente1_topic   = "<topic_to_report_sensor_1>";
String  medidor_corriente2_topic   = "<topic_to_report_sensor_2>";
String  medidor_corriente3_topic   = "<topic_to_report_sensor_3>";

// Assumed Voltage
float voltajeRed = 120.0;

// Create 3 instances of EnergyMonitor
EnergyMonitor energyMonitor1;
EnergyMonitor energyMonitor2;
EnergyMonitor energyMonitor3;

// Function to connect to MQTT 
EspMQTTClient client(
                  WIFI_SSID,
                  WIFI_PASS,
                  BROKER_IP,
                  BROKER_USERNAME,
                  BROKER_PASSWORD,
                  CLIENT_NAME,      // Client name to uniquely identify your device
                  BROKER_PORT 


void setup()
{

  Serial.begin(115200);
  
  // Enable debugging messages sent to serial output
  client.enableDebuggingMessages(); 

  // Enable the web updater.
  client.enableHTTPWebUpdater();     
  
  // MQTT Last Will & Testament
  client.enableLastWillMessage( lastwill_topic , lastwill_text);  

  // Pin where sensors are connected
  // Calibration value: Modify when calibrating
  energyMonitor1.current(ADC_INPUT_1, 0.145);
  energyMonitor2.current(ADC_INPUT_2, 0.145);
  energyMonitor3.current(ADC_INPUT_3, 0.145);

}
  //MQTT Innitial Connection
  void onConnectionEstablished()
  {
    client.publish(startup_topic, String(client_name + " is now online."));
    }

void loop()
{
  // MQTT Loop: Must be called once per loop.
  client.loop(); 

  // Mills Loop
  unsigned long current_time = millis();

  // Mills If Statment
  if(current_time - previous_time >= event_interval) {

      // Number of samples to take
      double Irms1 = energyMonitor1.calcIrms(1484);
      // Calculate current
      double potencia1 =  Irms1 * voltajeRed;

      // Number of samples to take
      double Irms2 = energyMonitor2.calcIrms(1484);
      // Calculate current
      double potencia2 =  Irms2 * voltajeRed;

      // Number of samples to take
      double Irms3 = energyMonitor3.calcIrms(1484);
      // Calculate current
      double potencia3 =  Irms3 * voltajeRed;

      // Display info to serial monitor
      Serial.print("Potencia 1 = ");
      Serial.print(potencia1);
      Serial.print("    Irms 1 = ");
      Serial.println(Irms1);

      // Display info to serial monitor
      Serial.print("Potencia 2 = ");
      Serial.print(potencia2);
      Serial.print("    Irms 2 = ");
      Serial.println(Irms2);

     // Display info to serial monitor
      Serial.print("Potencia 3 = ");
      Serial.print(potencia3);
      Serial.print("    Irms 3 = ");
      Serial.println(Irms3);
      Serial.print('\n');
      Serial.print('\n');

      // MQTT Client Publisher
      client.publish(medidor_corriente1_topic, String(potencia1));
      client.publish(medidor_corriente2_topic, String(potencia2));
      client.publish(medidor_corriente3_topic, String(potencia3));
      Serial.print('\n');

      // Update timing for next time
      previous_time = current_time;

  }
}

Calibrating the sensors

To calibrate the sensors, I used the following procedure:

  • I split an extension cord so that the line and neutral wires were separated.
  • Then I hooked up the extension cord to the electrical outlet.
  • I connected a heater to the extension cord.
  • I hooked up the sensors to one of the extension cord lines.
  • I also hooked up a clamp meter to the same extension cord line.
  • I turned the heater on and compare the values in the sensors to the values in the clamp meter.
  • I then adjusted the calibration value in the code, loaded the code again and and tried again until readings matched.

Since all three sensors are hooked up to the same line, I also checked that readings were consistent at the different heating ranges.

The lines of code to calibrate the sensors are:

// Mills Function 
const unsigned long event_interval = 300000; // 5 minute interval, 300 second

I modified this line, to make the sensor report values every 5 seconds instead of every 5 minutes for the calibration test:

  energyMonitor1.current(ADC_INPUT_1, 0.145);
  energyMonitor2.current(ADC_INPUT_2, 0.145);
  energyMonitor3.current(ADC_INPUT_3, 0.145);

Then to calibrate the values, I modified the value of 0.145 until all readings were consistent.

Here is a gif of the procedure:

Gif of the energy monitor sensor calibration process
Energy monitor sensor calibration

Reading the sensor data

Once sensor was calibrated and reporting values it was time to save the data and visualize it.

To do this, I had previously Installed Mosquitto MQTT, InfluxDB and Grafana in a Raspberry Pi 4. Explaining how I did this would take too long, but I followed the instructions explained in this video.

First, I checked that the sensor was sending data correctly. I opened the Arduino serial monitor an got the following confirmation message:

Arduino serial monitor display after uploading the energy monitor code to the ESP32
Arduino serial monitor display after loading the code to the ESP32

Then I used SSH to connect my computer to the Raspberry Pi and opened Mosquitto, the MQTT broker to monitor the values that the ESP32 reported in real time.

In this example, I subscribed to a single sensor topic named “Cordilleras/medidor_corriente1”, not all three sensors.

Terminal output, using Mosquitto MQTT and suscribed to sensor1 topic
Terminal output, using Mosquitto MQTT and suscribed to sensor1 topic

Saving the values to a database

After checking that MQTT was working correctly I oppened Node-Red and configured some nodes to read the MQTT data of the three sensors, saved it as JSON objects, and then wrote them to the InfluxDB database I had previously set up.

Here is an image of the Node-Red setup:

Image of the energy monitor Node-Red setup showing the nodes used to save sensor data into the InfluxDB database

The purple nodes read the MQTT information. The orange function nodes save the information of each sensor as a variable. Green Nodes are for debugging and testing.The blue node executes automatically every 5 minutes.The Orange node saves all three variables as a single payload. Finally, the brown node saves the information into InfluxDB.

To check the readings in the database, I logged in using the command line to see the values:

Terminal showing the InfluxDB database with timestamps and energy readings
Terminal showing the InfluxDB database with timestamps and energy readings

Visualizing the data

Grafana is a great tool for data visualization because its open source, easy to use, and it has great charts that can be adjusted for different time periods.

Below is an image of how the information looks when displaying a 24 hour period.

Energy monitor chart using Grafana showing the the energy usage over a 24 hour period.
Energy monitor chart using Grafana showing the the energy usage over a 24 hour period.

In the graph, you can see how the first electric line (medidor 1) uses more electricity than the second or the third line.

You can also see periodic spikes of about 5 amps. This is the refrigerator motor turning on. Finally, you can see some massive spikes at 8am, 4pm and 8.30pm. This is the microwave turning on during breakfast, lunch and dinner.

Using the sensor data


When comparing the electricity consumption of line 1, 2 and 3, it is clear that loads are unbalanced, since most of the electric consumption is going on in line 1.

I re-arranged the electrical loads so the microwave was in one line, the refrigerator in a separate line and the rest of the electrical outlets in a third line.

During most of the day, the electric load is under 5Amps, which means that generating that amount of energy would be enough to substitute my energy bill during sunlight hours.

To answer the initial question that started this project:
To reduce the electricity for all day, I would have to buy enough solar panels to generate 5 Amps, or 600 Watts (5amps x 120volts) during 24 hours (14,400W/h).

Average panels produce 270 Watts/hour for a 5 hour period per day (acording to solargis‘s estimate for Mexico CIty), or 1,350 Watts per day, I would need approximately 11 solar panels to fully offset my electricity costs with renewable energy.

More resources

I hope you found this post interesting.

If you decide to build a current sensor, below you will find some additional resources that I used to make this project possible.

Feel free to contact me if you need any help.

Pablo.

A video showing how to set up Mosquitto, Node-Red, InfluxDB, and Grafana using a RaspberryPi
Explanation of how an inductive current sensor works and the hardware it needs
Another energy monitor project with a slightly different approach
https://www.youtube.com/watch?v=Z3YSHhS39Bc
A video of how to set up the SCT-013 current sensor
Another very good explanation of how the SCT-013 current sensor works and how to wire it

Full Code

Here is the code used in the ESP32 Micro-controller. For more information make sure to head to the github repo.

// Code to measure water level in a water tank

// Mills Function 
const unsigned long event_interval = 300000; // 5 minute interval, 300 second
//const unsigned long event_interval = 2000; // 2 second interval. For testing
unsigned long previous_time = 0; 

// MQTT Library
#include "EspMQTTClient.h" 

#define WIFI_SSID        "<Wifi_Username_here>"                          // WiFi Username
#define WIFI_PASS        "<Wifi_Password_here>"                          // Wifi Password

#define BROKER_IP        "<MQTT_ip_here>"                                // IP adress of MQTT broker
#define BROKER_USERNAME  "<broker_username_here>"                        // Broker username
#define BROKER_PASSWORD  "<broker_password_here>"                        // Broker password
#define CLIENT_NAME      "<device_name_here>"                            // MQTT client name to identify the device
#define BROKER_PORT      <mqtt_port_here>                                // MQTT Port. No "" needed
#define lastwill_topic   "<lastwill_topic_here>"                         // MQTT topic to report lastwill and testament.
#define lastwill_text    "<lastwill_message_here>"                       // MQTT memssage to report lastwill and testament.

String  client_name       = CLIENT_NAME;                                 // MQTT Topic to report initial value
String  startup_topic     = "<startup_topic_here>";                      // MQTT Topic to report startup
String  water_level_topic = "<reporting_values_topic_here>";             // MQTT topic to report values

// Function to connect to MQTT 
EspMQTTClient client(
                  WIFI_SSID,
                  WIFI_PASS,
                  BROKER_IP,
                  BROKER_USERNAME,
                  BROKER_PASSWORD,
                  CLIENT_NAME,
                  BROKER_PORT
                  );

// Water Sensor pins
#define TRIG 14 //GPIO Number 14, D5
#define ECHO 12 //GPIO Number 12, D6

void setup() { 
  
  Serial.begin(115200); // Serial monitoring 
  
  // Enable  debugging messages sent to serial output
  client.enableDebuggingMessages(); 

  // Enable the web updater.
  client.enableHTTPWebUpdater();     
  
  // MQTT Last Will & Testament
  client.enableLastWillMessage( lastwill_topic , lastwill_text);
  
  // Water level sensor Pin Setup
  pinMode(TRIG, OUTPUT);       // Initializing Trigger Output
  pinMode(ECHO, INPUT_PULLUP); // Initializing Echo Input
  } 

// MQTT Innitial Connection
void onConnectionEstablished() {
  client.publish(startup_topic, String(client_name + " is now online."));
  }
  
  void loop() { 

    // MQTT Loop: Must be called once per loop.
    client.loop(); 

    // Mills Loop
    unsigned long current_time = millis();

    // Mills if Statement
    if(current_time - previous_time >= event_interval) {
      // Set the trigger pin to low for 2uS 
      digitalWrite(TRIG, LOW); 
      delayMicroseconds(2); 

      // Send a 20uS high to trigger ranging
      digitalWrite(TRIG, HIGH);  
      delayMicroseconds(20); 

      // Send pin low again
      digitalWrite(TRIG, LOW);  

      // Read pulse times
      int distance = pulseIn(ECHO, HIGH,26000);  

      //Convert the pulse duration to distance
      distance= distance/58; 

      //Print Result in serial monitor
      Serial.print("Distance ");
      Serial.print(distance);
      Serial.println("cm");

      // MQTT Client Publisher
      client.publish(water_level_topic, String(distance));

      // Mills Update timing for next time
      previous_time = current_time;
    }
    
}

Back to home Page

Browse other projects


6 responses to “How to make an IoT energy monitor”

  1. Hey nice project!

    I would consider increasing the measurement frequency of your sensors drastically to multiple times a second. The ES32 should easily handle 10 measurements per second per sensor.
    Current can be very volatile. For example if you have a fridge that by chance turns on in an intervall of approximately 5 minutes and runs for only 2 Minutes, then you would have a hard time catching this energy consumption.
    However, I think it’s reasonnable to only transmit the data to the server every 5 minutes. In the meantime sum up your fast periodic sampling and calculate the mean consumption for that period of time.

    • Hi Sven. You’re right, I didn’t think of it at the time, but it would definitely be a better method. I’ll give it a shot and make a second version. Thank you for the idea.

  2. how could i make a current sensor to monitor a well pump at a remote location to detect if the pump is running to much. (Broken water line or faucet left open) pump is 220v and would have to send me text messages and chart cycles on a website.
    Thanks

    • Hi Steve. This can be done with Grafana and Telegram, but you would need to have internet at the remote location (if its really remote, and important enough, you could use Starlink). If you do, you could monitor the well pump and set a baseline and get alerts if the pumps runs over that limit.

Leave a Reply

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