Derek
Published © CC BY

WiFi Gas Meter (esp8266 arduino wifi)

Arduino-based Gas Pulse IoT Meter, reporting to nodejs/MySQL, visualised in Grafana

IntermediateShowcase (no instructions)8 hours9,024
WiFi Gas Meter (esp8266 arduino wifi)

Things used in this project

Hardware components

Elster BK-G4
×1
Elster IN-Z61 Pulse Attachment
×1
USB-A to Micro-USB Cable
USB-A to Micro-USB Cable
×1
Pushbutton switch 12mm
SparkFun Pushbutton switch 12mm
The button is only needed to test the project. Under normal operation the In-Z61 would pulse.
×1
Jumper wires (generic)
Jumper wires (generic)
×5
Breadboard (generic)
Breadboard (generic)
×1
NodeMCU ESP8266 Breakout Board
NodeMCU ESP8266 Breakout Board
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Schematics

Fritz Schematic

Circuit ON

LED ON when the meter pulses

IN-Z61 In Situ

The IN-Z61 reads the meter, at approx 9.5 it turns ON, and at approx 0.5 it turns off. Match this with PIN mode INPUT_PULLUP

Gas Meter by Minute

After running the meter for some hours, we see the number of pulses per minute in Grafana

Code

NodeJS Server

JavaScript
This server will receive measurement from the device(s). I runs on my home computer, and posts the data to a MySQL server
var moment = require('moment');
const mysql = require('mysql');
const express = require('express');
const app = express();

const port = 3000;

// MySQL config
var db_conf = {
  host     : '192.168.1.200',
  user     : 'myuser',
  password : 'secret',
  database : 'mydb'
};

var dateTimeFormat = 'YYYY-MM-DD HH:mm:ss'; // MySQL timestamp

// handle "GET /""
app.get('/', (request, response) => {
  response.json({ status: 'ok'
  })
});

app.get('/device/:device/metric/:metric/value/:value', (request, response) => {
  console.log(request.params);

  var timestamp = moment.utc().format( dateTimeFormat );
  var device = request.params["device"];
  var workerid = "device."+device;
  var metric = request.params["metric"];
  var value  = request.params["value"];

	var conn = mysql.createConnection(db_conf);
	var sql = 'INSERT INTO log (`timestamp`,`workerid`,`metric`,`value`) VALUES (?, ?, ?, ?)';
  var logValues = [timestamp, workerid, metric, value];
  conn.query(sql, logValues, function( err, res, flds ) {
      if (err) throw err;
      console.log( 'log '+metric+':'+timestamp +' / '+value );
      conn.end();
  });

  response.json({
    device: request.params["device"],
    metric: request.params["metric"],
    value: request.params["value"],
  })
});

app.listen(port, (err) => {
  if (err) {
    return console.log('something bad happened', err)
  }

  console.log(`server is listening on ${port}`)
});

MySQL Schema

SQL
CREATE TABLE `log` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `timestamp` timestamp NULL DEFAULT NULL,
  `workerid` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `metric` varchar(50) COLLATE utf8_bin DEFAULT NULL,
  `value` decimal(25,10) DEFAULT NULL
  PRIMARY KEY (`id`),
  KEY `timestamp` (`timestamp`,`workerid`,`metric`,`value`),
  KEY `workerid` (`workerid`,`timestamp`,`value`)
);

Grafana Metric (Minute)

SQL
SELECT
  UNIX_TIMESTAMP(`timestamp`) as time_sec,
  `value` as value,
  "Pulse" as metric
FROM log
WHERE $__timeFilter(`timestamp`)
AND workerid = 'device.8f:12:18:22:4f:dc' -- your MAC addy here!
ORDER BY `timestamp` ASC

Grafana Metric (Hour)

SQL
SELECT 
  UNIX_TIMESTAMP(DATE_ADD(the_date, INTERVAL the_hour HOUR)) as time_sec, 
  the_sum as value,
  'hourly' as metric
from (
  select
    DATE(`timestamp`) as the_date,
    HOUR(`timestamp`) as the_hour,
    SUM(value) AS the_sum
  FROM log c
  WHERE workerid = "device.8f:12:18:22:4f:dc" -- your MAC addy here
  AND  $__timeFilter(`timestamp`)
  GROUP BY 1,2 
  ORDER BY `timestamp` ASC
) x

Arduino device code

Arduino
Use Arduino IDE - push this onto the ESP8266 device
#include <SPI.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
//#include <WiFiUdp.h>
#include <ESP8266WiFiType.h>
//#include <ESP8266WiFiAP.h>
#include <WiFiClient.h>
#include <WiFiClientSecure.h>
#include <WiFiServer.h>
//#include <ESP8266WiFiScan.h>
#include <ESP8266WiFiGeneric.h>
#include <ESP8266WiFiSTA.h>
#include <ESP8266HTTPClient.h>

/*****************************************/
const int led = LED_BUILTIN; //13;//the led attach to
const int pin = D2; // NodeMCU uses internal pin numbers!

char ssid[] = ".....";  //  your network SSID (name)
char pass[] = ".....";  // your network password

int wifiStatus = WL_IDLE_STATUS;     // the Wifi radio's status

// Variables will change:
int ledState = HIGH;      // the current state of the output pin
int pinState;             // the current reading from the input pin
int lastPinState = LOW;   // the previous reading from the input pin

HTTPClient http;
String host = "192.168.1.10"; // my home computer
int port = 3000;              // match the port in the nodeJS
String url;

const long interval = 60*1000; // 60 seconds - for "production"
//const long interval = 10*1000; // 10 seconds - for testing!
volatile long pulseCount = 0;
volatile long lastReport = 0;

WiFiClient client;

void setup() {
  Serial.begin(9600);
  Serial.println("Starting...");
  pinMode(led, OUTPUT);
  pinMode(pin,INPUT_PULLUP);
  digitalWrite(led, HIGH);
  attachInterrupt(digitalPinToInterrupt(pin), onPulse, CHANGE);

  Serial.print("Connecting WiFi ");
  WiFi.disconnect();
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, pass);
  while(WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(250);
  }

  Serial.println("Connected WiFi");
  printWifiInfo();  
}
// ======================================================================
void loop() {
  if(millis()-lastReport > interval){
    getHttp();
  }
}
// ======================================================================
void slog(String log) {
  Serial.print("LOG: ");
  Serial.println(log);
}
// ======================================================================
void printWifiInfo() {
  Serial.println("==== WIFI DETAILS ====");
  Serial.print("IP : ");
  Serial.println(WiFi.localIP());
  //WiFi.printDiag(Serial); // <== this will print password!
} 
// ======================================================================
void onPulse() {
  detachInterrupt(pin);
  long reading = digitalRead(pin);
  digitalWrite( led, reading );
  switch( reading ) {
    case LOW:  // SWITCH DOWN = ON
      slog("PULSE");
      pulseCount++;
      //getHttp(); // THIS WILL FAIL, must be in the loop
      break;
    case HIGH: // SWITCH UP = OFF
      break;
  }
  attachInterrupt(digitalPinToInterrupt(pin), onPulse, CHANGE);
}
// ======================================================================
void getHttp() {
  url = String("/device/")+getMacAddress()
    + String("/metric/count")
    + String("/value/")+pulseCount;
  slog(url);
  http.begin(host,port,url);
  int httpCode = http.GET();
  if( httpCode ) {
    if( httpCode == 200) {
      pulseCount = 0;
      String payload = http.getString();
      Serial.println( payload );
    }
    lastReport = millis();
  }
  Serial.println("closing connection");
  http.end();
}
// ======================================================================
String getMacAddress() {
  byte mac[6];
  WiFi.macAddress(mac);
  return 
     String( mac[5], HEX ) +":"
    +String( mac[4], HEX ) +":"
    +String( mac[3], HEX ) +":"
    +String( mac[2], HEX ) +":"
    +String( mac[1], HEX ) +":"
    +String( mac[0], HEX );
}
// ======================================================================

Credits

Derek

Derek

1 project • 1 follower

Comments