ESP8266 Interrupt Driven Meter Monitor

A network node that will sit in my meter box and read meter pulses. The node will store accumulated readings based on pulses/min.

Similar projects worth following
Well it works..

So my plan is to have a visual monitor of my household energy consumption inside my home. I'd like to see when we are chewing through the kwh. The plan is to have two interacting devices - a receiver and a transmitter - over time I'm going to add more transmitters to monitor other things, but for now just electricity consumption. My electricity has an LED that pulses as I use kwh, about 100 pulses I think for each kwh.

The transmitter uses a lm393 light detector - this is just a LDR hooked up to a voltage comparator. This is used to drive a GPIO pin on an ESP8266. Then some simple software picks up the pulses via an Interrupt handler, and increments a counter. The counter is sampled every minute and then reset, the result is sent via a TCP connection to the receiver.

Couple of things that might be interesting:

1 - I'm using a breadboard Power supply

2 - The case is a very cheap plastic conduit junciton box

3 - The ESP device is a V2 ESP-07 with an external antenna

4 - the wiring is created using my favorite looming technique - preterminated Dupont flywires and simple dupont shells. I like the neatness of it!

  • Some Software - Very early test code

    stuart goggin03/10/2015 at 13:25 0 comments

    Here is some sample code that seems to work OK. Need to test with a real pulse generator.

    big thanks to

    on which this code is based.

    #include "c_types.h"
    #include "ip_addr.h"
    #include "ets_sys.h"
    #include "espconn.h"
    #include "osapi.h"
    #include "mem.h"
    #include "gpio.h"
    #include "os_type.h"
    #include "user_config.h"
    #include "user_interface.h"
    #include "driver/uart.h"
    #define user_procTaskPrio        0
    #define user_procTaskQueueLen    1
    void ICACHE_FLASH_ATTR network_init();
    os_event_t    user_procTaskQueue[user_procTaskQueueLen];
    static void user_procTask(os_event_t *events);
    static volatile os_timer_t pulse_timer;
    static volatile os_timer_t debounce;
    LOCAL os_timer_t network_timer;
    static struct espconn global_udp_connect;                   // Connection struct (see espconn.h)
    static esp_tcp global_tcp;                                  // TCP connect var (see espconn.h)
    static struct espconn global_tcp_connect;                   // Connection struct (see espconn.h)
    static uint8_t udp_conn_ok = FALSE;                         // Bool to know if udp connection set
    static uint8_t tcp_conn_ok = FALSE;                         // Bool to know if tcp connection set
    static uint16_t doppler_counter = 0;                        // Doppler counter
    static uint16_t pulse_count =0;
    char* itoa(int i, char b[])  // Convert Integer to ASCII!!
        char const digit[] = "0123456789";
        char* p = b;
            *p++ = '-';
            i *= -1;
        int shifter = i;
        do{ //Move to where representation ends
            shifter = shifter/10;
        *p = '\0';
        do{ //Move back, inserting digits as u go
            *--p = digit[i%10];
            i = i/10;
        return b;
    void debounce_func(void *arg)
    // This routine is called on every INTERRUPT.  the idea is to increment a counter as many times as needed
    // to record the pulse.  Due to noise, we need to implement a debounce algorithm.
    // This is handled by the pulse timer func.
        ETS_GPIO_INTR_DISABLE();                        // Disable gpio interrupts
        uint16_t drop_copy = doppler_counter;           // copy doppler val
        doppler_counter = 0;                            // not good!
        ETS_GPIO_INTR_ENABLE();                         // Enable gpio interrupts
        if (drop_copy > 0)
    void pulse_timer_func(void *arg)
        uint16_t drop_copy = pulse_count;           // copy doppler val
        pulse_count = 0;                            // not good!
        if (GPIO_REG_READ(GPIO_OUT_ADDRESS) & BIT2)     // If GPIO2 high
            gpio_output_set(0, BIT2, BIT2, 0);          // Set GPIO2 to LOW
        else                                            // Else
            gpio_output_set(BIT2, 0, BIT2, 0);          // Set GPIO2 to HIGH
    // Send pulse counter
        if (tcp_conn_ok == TRUE)
            uint8_t data[10] = " \x00\x00\x00\x00\x00";
            itoa(drop_copy, data+1);
            espconn_sent(&global_tcp_connect, data, 10);
    void doppler_int_handler(int8_t key)
        // Increment counter
        //Not that sure what this does yet and where the register is used for
        uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
        //clear interrupt status
        GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & BIT(0));
    static void ICACHE_FLASH_ATTR tcpNetworkRecvCb(void *arg, char *data, unsigned short len) 
        struct espconn *tcpconn=(struct espconn *)arg;
    static void ICACHE_FLASH_ATTR tcpNetworkConnectedCb(void *arg) 
        struct espconn *tcpconn=(struct espconn *)arg;
        espconn_regist_recvcb(tcpconn, tcpNetworkRecvCb);
        os_printf("TCP connected\n\r"); 
        uint8_t data[20] = "Hello you!\r\n";
        espconn_sent(tcpconn, data, 20);
        tcp_conn_ok = TRUE;
    static void ICACHE_FLASH_ATTR tcpNetworkReconCb(void *arg, sint8 err) 
        os_printf("TCP reconnect\n\r");
        tcp_conn_ok = FALSE;
    static void ICACHE_FLASH_ATTR tcpNetworkDisconCb(void *arg) 
        os_printf("TCP disconnect\n\r");
        tcp_conn_ok = FALSE;
    static void ICACHE_FLASH_ATTR init_tcp_conn(void) 
        global_tcp_connect.type=ESPCONN_TCP;                                    // We want to make a TCP connection
        global_tcp_connect.state=ESPCONN_NONE;                                  // Set default state to none
        global_tcp_connect.proto.tcp=&global_tcp;                               // Give a pointer to our TCP var
    Read more »

  • Some useful comments

    stuart goggin03/10/2015 at 13:19 0 comments

    Monitoring TCP traffic on a Ubuntu box

    nc -l -p 888

    • -l = Listen
    • -p = Port

    The Hardware

    ESP8266 as the main controller

    LM393 light sensor such as this one:

    Image result for lm 393 light sensor

    Desolder the sensor and remote mount it. It's just a cheap LDR. Plan is to stick it onto the LED on the meter.

    Powersupply - Breadboard 3.3v 5v supply

    Software Overview

    Pulse Detect line to ESP8266 - I'm using GPIO in my sample circuit - but have to switch to GPIO 2 as it makes reprogramming tricky.

    ESP8266 Interrrupt to pick up changes in light levels. Due to noise - need to decouple the ISR. So plan is to use a software debounce approach. A standard ISR will count the pulses - which might be several thousand per pulse. A second timer will operate every 10msec, and if the count of ISR pulses is > 1 then increment a second counter by 1. This will ensure that the circuit does not pick up noise.

    The software will record pulses per minute. A minute timer will kick off and reset the pulse count. It will then open a TCPIP connection to

    • The owl - report the pulse count for the last minute

    The data will be written to a ESP File and stored for later retrieval as required.

View all 2 project logs

Enjoy this project?



Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates