RL_smart_meter/RL_smart_meter.html
/*
* Connect to IR-READER to get SMLV1.04 Smart Reader data
*
* features:
* - run WebServer on fix local IP 192.168.178.23
* - read actual smart reader data on request
* (W, kwh, meterNo) and some technical data
*
* - put smart_meter data to rlhome.noip.me
*
* - update tech-data
* http://192.168.178.23/update?W_diff=15
* http://192.168.178.23/update?verbose=1
* http://192.168.178.23/update?fingerprint=hexString...
* http://192.168.178.23/update?fingerprint=82c27a1df141a43e726adfc808144fbf88b04636
*
* design Notes:
* - use serial 57600 Baud to get println
* - use https connect to rlhome.noip.me
* - update SH1 fingerprint
* - use SoftwareSerial to handle 2 Serials (IR-Reader and println)
* - use err counters
*
*
*/
#include <SoftwareSerial.h> // allow more SERIAL ports
#include <ESP8266WiFi.h> // somw basic
#include <WiFiClientSecure.h> // to setup WWW Server and Client
#include <EEPROM.h> // to store data and get these after reboot again
const char* ssid = "Rolf"; // on 8266
const char* password = "xxxxxxxxxxxxxxxxxxxxxxxx";
const int MAX = 512;
static int buffer[MAX]; // buffer
static String SML_buffer; // IR buffer in HEX
static int pos = 0; // pointer into Buffer
static int W = 0; // read Watt
static int kWh = 0; // read kWh
static int W_diff = 10; // W difference
static int W_last = 0; // last read W value
WiFiServer server_8266(80); // allow Port 80 Http requests on server running on 8266
static int err_srv = 0; // error counter since last boot
static int err_client = 0; // error counter client
static int err_read = 0; // error counter for read IR Buffer
static bool verbose = true; // more or less output to Serial
static String meterNo;
SoftwareSerial mySerial (13, 15); // GPIO13 and GPIO15
char fingerprint[] PROGMEM = "82c27a1df141a43e726adfc808144fbf88b04636";
char version[] = "2023-01-13";
int getInteger(int i) {
/* read 4 Bytes from pos i and return as integer
*/
return (
buffer[i + 0] * 0x1000000 +
buffer[i + 1] * 0x10000 +
buffer[i + 2] * 0x100 +
buffer[i + 3]);
}
bool sequence(int i, int b0, int b1, int b2, int b3, int b4, int b5, int b6, int b7) {
return (buffer[i] == b0 &&
buffer[i + 1] == b1 &&
buffer[i + 2] == b2 &&
buffer[i + 3] == b3 &&
buffer[i + 4] == b4 &&
buffer[i + 5] == b5 &&
buffer[i + 6] == b6 &&
buffer[i + 7] == b7);
}
String getMeterNo(int i, int len) {
/*
0B 0A 01 49 54 52 00 03 49 c9 36
|------> 11 Octet String incl. TL
|---> 10 digits
1 I T R 0055167286
*/
int n = getInteger(i + 5);
char m[11];
sprintf (m, "%010d", n);
return String(buffer[i]) + char(buffer[i + 1]) + char(buffer[i + 2]) + char(buffer[i + 3]) + m;
}
bool get_W_kWh(int n) {
kWh = 0; // init
W = 0; // init
meterNo = "";
for (int i = 0; i < n; i++) {
if (kWh == 0 && sequence(i, 0x77, 0x07, 0x01, 0x00, 0x01, 0x08, 0x00, 0xFF)) {
kWh = getInteger(i + 23);
Serial.println("kWh found " + String(kWh));
}
if (W == 0 && sequence(i, 0x77, 0x07, 0x01, 0x00, 0x10, 0x07, 0x00, 0xFF)) {
W = getInteger(i + 15);
Serial.println("W found " + String(W));
}
if (sequence(i, 0x77, 0x07, 0x01, 0x00, 0x60, 0x01, 0x00, 0xFF)) {
meterNo = getMeterNo(i + 14, 10);
Serial.println("meterNo found "+meterNo);
}
if (meterNo.length() > 0 && W > 0 && kWh > 0) return true; // abort if values found
}
Serial.println("failed to read W or kWh or meterNo");
err_read++;
return false; // values not found!!
}
void putData() {
/*
new Data to put to Web_server
*/
digitalWrite(D4, LOW); // turn the LED on
Serial.println("put Data to Server");
W_last = W;
String host = "rlhome.noip.me";
String urlGet = "/body/8266/append.php?power=" + String(W) + "&kwh=" + String(kWh);
const int httpsPort = 443;
WiFiClientSecure httpsClient; //Declare object of class WiFiClient
httpsClient.setFingerprint(fingerprint);
int retry = 0;
while ((!httpsClient.connect(host, httpsPort)) && retry < 50) {
delay(2);
retry++;
}
if (retry >= 50) {
err_client++;
digitalWrite(D4, HIGH); // turn the LED off
return;
}
/*
start to push data
*/
httpsClient.println("GET " + urlGet + " HTTP/1.1");
httpsClient.print("Host: ");
httpsClient.println(host);
httpsClient.println("User-Agent: ESP8266/1.0");
httpsClient.println("Connection: close\r\n\r\n");
digitalWrite(D4, HIGH); // turn the LED off
}
String getValue(String data, char separator, int index) {
/*
String part01 = getValue(application_command,';',0);
*/
int found = 0;
int strIndex[] = {0, -1};
int maxIndex = data.length() - 1;
for (int i = 0; i <= maxIndex && found <= index; i++) {
if (data.charAt(i) == separator || i == maxIndex) {
found++;
strIndex[0] = strIndex[1] + 1;
strIndex[1] = (i == maxIndex) ? i + 1 : i;
}
}
return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}
void do_HTML_request() {
// Prüfen, ob sich ein Client verbunden hat
WiFiClient client = server_8266.available();
if (!client) {
return;
}
// Warten auf Daten vom Client
int retry = 0;
while (!client.available() && retry < 50) {
delay(1);
retry++;
}
if (retry >= 50) {
err_srv++; // inc err counter
return; // exit to avoid endless loop
}
// Erste Zeile des Requests lesen
String request = client.readStringUntil('\r');
Serial.println(request);
if (request.indexOf("get") > 0) {
/* get some data back */
client.flush();
HTMLresponseGet(client);
}
else {
if (request.indexOf("update") > 0) {
String keys = getValue(request, '?', 1);
String key = getValue(keys, '=', 0);
String val = getValue(keys, '=', 1);
if (key.equals("W_diff")) {
W_diff = val.toInt();
EEPROM.put(0, W_diff);
EEPROM.commit();
} else if (key.equals("verbose")) {
verbose = (val.toInt() == 1);
EEPROM.put(1, verbose);
EEPROM.commit();
} else if (key.equals("fingerprint")) {
val.toCharArray(fingerprint, sizeof(fingerprint));
} else {
Serial.println("*** unknown key=" + key);
}
}
client.flush();
HTMLresponse(client);
}
}
//////////////////////////////////////////////////////////////
bool read_SML_buffer() {
// get any incoming bytes from Serial UART:
pos = 0;
if (mySerial.available()) {
digitalWrite(D0, LOW); // turn the LED on
Serial.print("IR Data available");
// read the incoming byte:
while (mySerial.available()) {
buffer[pos % MAX] = mySerial.read();
pos += 1;
if (!mySerial.available()) delayMicroseconds(1200);
}
Serial.print (" Bytes read: ");
Serial.println (pos);
}
digitalWrite(D0, HIGH); // turn the LED off
// read success?
return (pos > 0 && buffer[0] == 0x1B );
}
String hex(int b) {
char hex_buffer[3];
sprintf (hex_buffer, "%02X", b);
return hex_buffer;
}
void get_HEX_buffer(int n) {
SML_buffer = "";
for (int i = 0; i < n; i++) {
if (i % 16 == 0)
SML_buffer += "\n";
int b = buffer[i];
SML_buffer += hex(b) + " ";
}
}
void get_EEPROM() {
/*
read EEPROM data
*/
EEPROM.begin(32); // reserve 32 Bit for EEPROM usage
EEPROM.get(0, W_diff);
if (W_diff > 100) {
EEPROM.put(0, 10);
EEPROM.commit();
}
EEPROM.get(1, verbose);
}
void HTMLresponseGet(WiFiClient client) {
// Anfrage zurücksenden
String html;
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/plain");
client.println("");
client.println("version: \t" + String(version));
client.println("meter: \t" + meterNo);
client.println("W_diff: \t" + String(W_diff));
client.println("err_client:\t" + String(err_client));
client.println("err_srv: \t" + String(err_srv));
client.println("err_read: \t" + String(err_read));
client.println("verbose: \t" + String(verbose));
client.println("W: \t" + String(W));
client.println("kWh: \t" + String(kWh));
if (verbose) {
client.println("SML_buffer:" + SML_buffer);
}
}
void HTMLresponse(WiFiClient client) {
// Anfrage zurücksenden
String html;
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("");
client.println("<!DOCTYPE HTML>");
client.println("<html>");
client.println("<body bgcolor='#ffffdd'>");
client.println("<h2>Welcome to 8266 WIFI smart_meter</h2>");
client.println("<hr>");
client.println("<center>");
html = "<table border=1>";
html += "<tr><th>variable</th><th>value</th><th>description</th></tr>";
html += "<tr><td>meterNo</td><td>" + meterNo + "</td><td>my smart meter number</td></tr>"; html += "<tr><td>W_diff</td><td>" + String(W_diff) + "</td><td>min change required to report<br>";
String IP = WiFi.localIP().toString().c_str();
html += "<a href='http://" + IP + "/update?W_diff=15'>update to 15</a></td></tr>";
html += "<tr><td>W</td><td>" + String(W) + "</td><td>current Watt</td></tr>";
html += "<tr><td>1/10.000 kWh</td><td>" + String(kWh) + "</td><td>summary of kWh in 1/10.000 steps</td></tr>";
html += "<tr><td>err_client</td><td>" + String(err_client) + "</td><td>error connect counter client</td></tr>";
html += "<tr><td>err_srv</td><td>" + String(err_srv) + "</td><td>error connect counter server_8266</td></tr>";
html += "<tr><td>err_read</td><td>" + String(err_read) + "</td><td>error counter for read_IR buffer</td></tr>";
html += "<tr><td>verbose</td><td>" + String(verbose) + "</td><td>used to show SML_buffer aswell.<br>";
html += "<a href='http://" + IP + "/update?verbose=1'>update verbose=1</a></td></tr>";
html += "<tr><td>fingerprint sha-1</td><td>" + String(fingerprint) + "</td><td>used for ssh secure connect<br>";
html += "<a href='http://" + IP + "/update?fingerprint=hexString</a></td></tr>";
html += "<tr><td>get</td><td>data</td><td>get text/plain data<br>";
html += "<a href='http://" + IP + "/get'> get</a></td></tr>";
if (verbose) {
html += "<tr><td>SML V1.04</td><td><pre>" + SML_buffer + "</pre></td><td>buffer read from IR smart meter</td></tr>";
}
html += "</table>";
client.println(html);
client.println("<hr>");
client.println("created by R.LANG "+String(version));
client.println("</body>");
client.println("</html>");
client.flush();
}
///////////////////////////////////////////////////////////////////
void setup() {
digitalWrite(D0, HIGH); // turn the LED off
digitalWrite(D4, HIGH); // turn the LED off
pinMode(D0, OUTPUT); // to control onboard LED
pinMode(D4, OUTPUT); // to control onboard LED
get_EEPROM();
mySerial.begin(9600); // to my IR Reader
Serial.begin(57600); // to debug via COM/USB
while (!Serial) {
delay(100); // wait for serial port to connect. Needed for native USB port only
}
// Mit Wifi verbinden
Serial.print("connect to: " + String(ssid));
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.println("not connected to "+String(ssid)+" "+String(password));
}
Serial.println("8266 WiFi is connected ");
// Start des server_8266s
server_8266.begin();
Serial.println("server_8266 started");
// Print the IP address
Serial.print("my URL: http://");
Serial.println(WiFi.localIP());
}
///////////////////////////////////////////////////////////////////
void loop() {
// read IR
if (read_SML_buffer()) { // read IR
// process Buffer
Serial.println("new SML buffer read");
get_HEX_buffer(pos);
// Serial.println(SML_buffer);
if (get_W_kWh(pos) && abs(W - W_last) > W_diff) {
putData();
}
}
do_HTML_request();
}