Empowering Your Smart Home: Building an Optical Utility Meter Reader

Empowering Your Smart Home: Building an Optical Utility Meter Reader

Please read Liability Disclaimer and License Agreement CAREFULLY


In the era of smart homes and energy efficiency, monitoring and managing your utility consumption has become a key aspect of modern living.

This article introduces a DIY project that combines new technology with a user-friendly approach, presenting an Optical Utility Meter Reader based on the ESP8266 microcontroller, BPW34 photodiode, and OPA2348/OPA2354 operational amplifier.

This intelligent device not only counts LED pulses from your utility meter but also seamlessly connects to your home network via WiFi and MQTT, offering a convenient and efficient way to integrate energy data into your smart home ecosystem.

This device will work only with utility meters that have a LED to indicate the consumption, for example my energy meter has a LED that blinks 1000time/kWh. It is marked on the meter as 1000 imp/kWh followed by a square pulse symbol = 1Wh.

Components and Technology:

  1. ESP8266 for WiFi Connectivity: The ESP8266 is a powerful and cost-effective microcontroller with built-in WiFi capabilities. Leveraging its connectivity features, this Optical Utility Meter ensures seamless communication with your home network, allowing you to access real-time energy data from anywhere.

  2. BPW34 Photodiode for Precision Sensing: The BPW34 photodiode serves as the optical sensor in the meter. Its high sensitivity to light ensures accurate counting of LED pulses from your utility meter. This precision is crucial for providing reliable energy consumption data, allowing you to make informed decisions about your usage patterns.

  3. OPA2348/OPA2354 Op-Amp as a Schmidt Trigger: The OPA2348/2354 op-amp is utilized as a Schmidt trigger, helping to shape and stabilize the incoming signal from the photodiode. This ensures that the ESP8266 receives a clean and well-defined signal for processing, enhancing the overall reliability and accuracy of the meter.

  4. MQTT Integration for Smart Home Connectivity: MQTT (Message Queuing Telemetry Transport) is a lightweight and efficient messaging protocol ideal for IoT devices. The Optical Utility Meter employs MQTT to transmit the pulse count data to a designated MQTT broker. This data can then be easily integrated into your smart home system, providing a comprehensive overview of your energy consumption.

Building the Optical Utility Meter Reader: The construction of this device involves connecting the components, programming the ESP8266 to handle the incoming data, and setting up the MQTT communication. Detailed step-by-step instructions and code snippets will be provided to guide you through the process, making it accessible to both beginners and experienced DIY enthusiasts.

The KiCAD schematic and PCB are provided in order to change them if you feel the need and also to make the understanding of the circuit easier.

The R2 value can be changed depending on the ambient light conditions, the larger it is the larger the voltage at the op-amp, I am using a 1MOhm resistor for R2.

The threshold voltage is set by the voltage divider formed by R10 and R11, I have used 470Ohm for R10 and 10kOhm for R11 to get a trigger voltage of 3.15V and also to be able to keep IO3 of the ESP high at boot.

With the values stated above I can read 1000pulses/sec from a 3mm red led 2cm away from photodiode.

The ESP8266 shall be connect with the VCC pin to the + on the PCB and GND to the - on the PCB. UART is not used so you can also use Receive and Transmit pins as you like.

I have attached also the stl files to 3D print the enclosure parts are: Upper cover and Lower Cover. You will need a small self tapping screw with a diameter smaller than 2mm to fix the PCB in the Lower cover. The Upper Cover, after the power cable is connected through the hole, can be glued in place.

Double sided adhesive tape is used to fix the assembled enclosure on the meter.

Optical Utility Meter Enclosure Back Optical Utility Meter Enclosure Front

I have used JLCPCB to manufacture the PCB and for the first time to 3D print the enclosure parts.

This code is designed to count pulses from a utility meter and publish the total energy consumption to an MQTT broker. It uses an ESP8266 microcontroller, a photodiode, and an op-amp as a Schmidt trigger. The code connects to WiFi, subscribes to an MQTT topic, and sends energy consumption data to the MQTT broker. The pulse count and total energy are stored in EEPROM to persist across power cycles. The code also includes comments to explain each section's purpose and functionality.

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <EEPROM.h>

// Uncomment line below for debug
#define DEBUG

// WiFi credentials
constexpr char *clientID = "YOUR_CLIENT_ID";
constexpr char *ssid = "YOUR_NETWORK_SSID";
constexpr char *password = "YOUR_NETWORK PASSWORD";

// MQTT broker details
constexpr char *mqtt_server = "YOUR_MQTT_SERVER_IP";
constexpr char *mqtt_user = "YOUR_MQTT_USER_NAME";
constexpr char *mqtt_pass = "YOUR_MQTT_SERVER_PASSWORD";
constexpr char *topic_energy = "energy1/Energy";
// Pulses per kWh
constexpr char *topic_ppk = "energy1/ppk";
// MQTT default port
constexpr uint16_t mqtt_port = 1883;

#ifdef DEBUG
// Serial communication speed
constexpr uint32_t serialSpeed = 115200;

// Pulses per kWh
uint16_t ppk = 1;

// Pin for interrupt (pulse counting)
constexpr uint8_t interruptPin = 2; // GPIO2

// EEPROM settings
constexpr uint8_t sizeEEPROM = 6;           // EEPROM size in bytes
constexpr uint8_t ppkLocation = 0;          // EEPROM address for pulses per kWh
constexpr uint8_t totalEnergyLocation = 2;  // EEPROM address for totalEnergy

// Delay for WiFi connection and MQTT reconnect
constexpr uint16_t delayWiFi = 1000;
// Holds the pulses counted value. It is lost at reset or power loss
// as the hardware is lacking power loss ciruit. To overcome this
// the total energy value can be set from MQTT
volatile uint16_t pulseCount = 0;
// Holds the total energy value
uint32_t totalEnergy = 0;

#ifdef DEBUG
  uint32_t timer = 0;

WiFiClient espClient;
PubSubClient client(espClient);

// Function to set up WiFi connection
void setup_wifi() {
  IPAddress staticIP(xxx, xxx, xxx, xxx);
  IPAddress gateway(xxx, xxx, xxx, xxx);
  IPAddress subnet(xxx, xxx, xxx, xxx);
  IPAddress dns(xxx, xxx, xxx, xxx);
  WiFi.config(staticIP, subnet, gateway, dns);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    #ifdef DEBUG

// Function to reconnect to MQTT broker
void reconnect() {
  while (!client.connected()) {
    if (client.connect(clientID, mqtt_user, mqtt_pass)) {
      // First publish the ppk value
      String ppkString = String(ppk);
      client.publish(topic_ppk, ppkString.c_str());
    } else {
      #ifdef DEBUG

// Function to publish total energy to MQTT broker
void mqttPublish() {
  if (client.connected()) {
    String energyString = String(totalEnergy);
    client.publish(topic_energy, energyString.c_str());
  } else {

// Interrupt service routine for pulse counting
void ICACHE_RAM_ATTR countPulse() {

// Callback function for handling MQTT messages
void callback(char *topic, byte *payload, unsigned int length) {
  String payloadStr;
  for (int i = 0; i < length; i++) {
    payloadStr += (char)payload[i];
  if (strcmp(topic, topic_energy) == 0) {
    totalEnergy = payloadStr.toInt();
    EEPROM.put(totalEnergyLocation, totalEnergy);
  if (strcmp(topic, topic_ppk) == 0) {
    ppk = payloadStr.toInt();
    EEPROM.put(ppkLocation, ppk);

void setup() {
  #ifdef DEBUG
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), countPulse, FALLING);
  EEPROM.get(ppkLocation, ppk);
  EEPROM.get(totalEnergyLocation, totalEnergy);
  client.setServer(mqtt_server, mqtt_port);
  // Add your MQTT broker username and password
  //client.connect(clientID, mqtt_user, mqtt_pass);
  #ifdef DEBUG
    Serial.println("Done setup");
    timer = millis();


void loop() {
  if (!client.connected()) {
  if (pulseCount >= ppk) {
    #ifdef DEBUG
      timer = millis() - timer;
      Serial.print("Pulse count = ");Serial.print(pulseCount);Serial.print(" In "); Serial.print(timer); Serial.println(" ms");
      timer = millis();
    pulseCount = 0;
    EEPROM.put(totalEnergyLocation, totalEnergy);

Feel free to add debugging messages like:

if (!client.connected()) {
   Serial.println("MQTT connection lost. Reconnecting...");

Or use proper MQTT topics name

// Dynamically create MQTT topic based on device ID
constexpr char *mqtt_topic_energy = "your_mqtt_base_topic/ESP8266DeviceID/energy";

Remember to thoroughly test any modifications to ensure they align with your project requirements. Additionally, consider the specific needs of your application and adjust the code accordingly.

Conclusion: By building your Optical Utility Meter with ESP8266, BPW34 photodiode, and OPA2348 op-amp, you take a significant step towards creating a smarter, more energy-efficient home. The integration of WiFi and MQTT ensures that you can effortlessly monitor and analyze your energy consumption data within your existing smart home ecosystem. Empower your living space with data-driven insights and contribute to a more sustainable and efficient future.

Comments powered by CComment

Who’s online

We have 60 guests and no members online