Skip to content

Instantly share code, notes, and snippets.

@roman3x
Forked from gabonator/energymeter.ino
Created July 20, 2018 04:39
Show Gist options
  • Save roman3x/cde7169c9c73bd00d7ac39c3f971b95d to your computer and use it in GitHub Desktop.
Save roman3x/cde7169c9c73bd00d7ac39c3f971b95d to your computer and use it in GitHub Desktop.
Energy meter
// BC04-A bluetooth module
void setup()
{
// |\ |
// ____| \|________
// | /|
// |/ | / /
// V V
//
// CdS
// +-------[XXXXXXX]---+
// | 2.7k |
// o ---[===]---o o
// a3 a1 a0
//
// IN 0V 5V
pinMode(A0, OUTPUT);
digitalWrite(A0, 1);
pinMode(A1, OUTPUT);
digitalWrite(A1, 0);
pinMode(13, OUTPUT);
Serial.begin(9600);
delay(1000);
Serial.print("AT\r\n");
delay(200);
Serial.print("AT\r\n");
}
int inhibit = 0;
int iter = 0;
int led = 0;
long last = 0;
void loop()
{
while (Serial.available())
Serial.read();
// Wait 20 ms
while (millis() < last+20)
{
// BT06 sync!
if (Serial.available())
{
char c = Serial.read();
static char c0 = 0;
if (c0=='+' && c=='C') // +CONNECTING<<....
Serial.write('+');
c0 = c;
}
}
last += 20;
int i = analogRead(A3);
if (iter++ == 50)
{
led ^= 1;
digitalWrite(13, led);
Serial.print(".\n");
iter = 0;
}
if (inhibit > 0)
{
inhibit--;
return;
}
if (i > 500)
{
Serial.print("*\n");
inhibit = 10;
}
}
<html>
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<script>
var globalInfo = false;
var globalInfoReceived = 0;
var first = true;
var buffer = "";
socket = new WebSocket("ws://"+window.location.hostname+":2222");
socket.onopen = function()
{
console.log("Connected!");
}
socket.onclose = function()
{
console.log("Connection lost!");
}
socket.onerror = function()
{
console.log("Connection lost!");
}
socket.onmessage = function(msg)
{
buffer += new String(msg.data);
var lines = buffer.split("\n");
while ( lines.length > 1 )
Receive( lines.shift() );
buffer = lines.join("\n");
}
function Receive(msg)
{
if (msg.indexOf("R") != -1)
{
onRestart();
return;
}
if (msg==".")
{onTick();
return;
}
if (msg=="*")
{onPulse();return}
console.log("Unknown message: "+msg);
}
</script>
<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>
<canvas id="graf" width="300" height="50"></canvas>
<canvas id="moj" width="50" height="50"></canvas>
<div id="con"></div><br>
<div id="con2"></div>
<div id="long" style="min-width: 310px; height: 200px; margin: 0 auto"></div>
<script>
var can = document.getElementById("moj");
var ctx = can.getContext("2d");
var wasPulse = 0;
var wasTick = 0;
var mem = [];
var met = [];
var buftime = 20; // 20 s
var buflen = buftime*50; // 20 s @ 20hz
var pu = [];
var maxpu = 5;
var cang = document.getElementById("graf");
var ctxg = cang.getContext("2d");
var pulseTicks = [];
var restarted = false;
function tick()
{
return (new Date).getTime();
}
function getConsumption(count)
{
if (count == -1)
count = pulseTicks.length-1;
if (pulseTicks.length < count+1 || count < 1)
return 0;
var duration = (pulseTicks[pulseTicks.length-1] - pulseTicks[pulseTicks.length-1-count])/1000;
var pauses = count;
var durationPerPause = duration/pauses;
var _1kW_250pulses_per_hour = 1600/3600; //250/3600;
var power = 1000/durationPerPause/_1kW_250pulses_per_hour;
return Math.floor(power)/1000;
}
function onRestart()
{
restarted = true;
}
function onTick()
{
wasTick = 1;
}
function onPulse()
{
ctx.fillStyle = "rgba(240, 0, 0, 1)";
ctx.fillRect(0, 0, can.width, can.height);
wasPulse = 1;
var now = tick();
pulseTicks.push(tick());
while (now - pulseTicks[0] > 300000)
pulseTicks.shift();
var t = (new Date()).getTime();
var c3 = getConsumption(3);
if (c3 != 0)
h.series[1].addPoint([t, c3], false, h.series[1].data.length >= 20);
var c10 = getConsumption(10);
if (c10 != 0)
{
h.series[2].addPoint([t, c10], false, h.series[2].data.length >= 20);
document.getElementById("con").innerHTML = "average consumption = " + c10.toFixed(3) + " kW";
}
var c0 = getConsumption(1);
if (c0 != 0)
h.series[0].addPoint([t, c0], true, h.series[0].data.length >= 20);
}
function longUpdate()
{
var t = (new Date()).getTime();
var c10 = getConsumption(-1);
l.series[0].addPoint([t, c10], true, l.series[0].data.length >= 50);
}
function blend()
{
var red = 0;
if (wasPulse)
{
if (restarted && pu.length > 0)
{
var last = pu[pu.length-1];
for (var i =0; i<pu.length; i++)
pu[i] -= last;
restarted = false;
return;
}
red = 1;
pu.push(0);
if (pu.length > maxpu)
pu.shift();
}
mem.push(wasPulse);
met.push(wasTick);
wasPulse = 0;
wasTick = 0;
if (mem.length > buflen)
mem.shift();
if (met.length > buflen)
met.shift();
ctx.fillStyle = "rgba(255, 255, 255, 0.2)";
ctx.fillRect(0, 0, can.width, can.height);
ctxg.fillStyle = "white";
ctxg.fillRect(0, 0, cang.width, cang.height);
ctxg.lineWidth = 1.1;
ctxg.strokeColor = "#000000";
ctxg.beginPath();
ctxg.moveTo(0, cang.height*0.9)
for (var i=0; i<mem.length; i++)
ctxg.lineTo(i/buflen*cang.width, cang.height*(0.9 - mem[i]*0.7));//cang.height*0,9 + mem[i]*cang.height*0.8);
ctxg.stroke();
ctxg.strokeColor = "#b0b0b0";
ctxg.lineWidth = 0.5;
for (var i=0; i<met.length; i++)
{
if (!met[i]) continue;
ctxg.beginPath();
ctxg.moveTo(i/buflen*cang.width, cang.height*0.1);
ctxg.lineTo(i/buflen*cang.width, cang.height*0.2);
ctxg.stroke();
}
}
Highcharts.setOptions({
global: {
useUTC: false
}
});
var h = Highcharts.chart('container', {
chart: {
//type: 'spline',
//animation: Highcharts.svg, // don't animate in old IE
marginRight: 10
},
title: {
text: 'Live power consumption in kW'
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150
},
yAxis: {
title: {
text: 'Value'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
tooltip: {
formatter: function () {
return '<b>' + this.series.name + '</b><br/>' +
Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) + '<br/>' +
Highcharts.numberFormat(this.y, 2);
}
},
legend: {
enabled: false
},
exporting: {
enabled: false
},
series: [{
name: 'Data 1 pulse avg',
data: []
}, {
name: 'Data 3 pulse avg',
data: []
}, {
name: 'Data 10 pulse avg',
data: []
}]
});
var l = Highcharts.chart('long', {
chart: {
//type: 'spline',
//animation: Highcharts.svg, // don't animate in old IE
marginRight: 10
},
title: {
text: 'Long term measurement [kW]'
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150
},
yAxis: {
title: {
text: 'Value'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
tooltip: {
formatter: function () {
return '<b>' + this.series.name + '</b><br/>' +
Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) + '<br/>' +
Highcharts.numberFormat(this.y, 2);
}
},
legend: {
enabled: false
},
exporting: {
enabled: false
},
series: [{
name: 'Long average',
data: []
}]
});
setInterval(blend, 20);
setInterval(longUpdate, 10000);
//setInterval(onPulse, 3600*1000/250/10);
// f = 250 per hour => 250/60/60 per second
// T = 1/(250/60/60) = 3600/250 s = 3600*1000/250 ms
</script>
</html>
var fs = require('fs');
var BTSP = require('bluetooth-serial-port');
var serial = new BTSP.BluetoothSerialPort();
var buf = "";
// websocket
var wsconns = [];
var ws = require("nodejs-websocket")
var server = ws.createServer(function (conn) {
wsconns.push(conn);
console.log("ws: New connection, clients=" + wsconns.length);
conn.on("text", function (str) {
serial.write(Buffer.from(str), function() {});
});
conn.on("close", function (code, reason) {
wsconns.splice(wsconns.indexOf(conn), 1);
console.log("Connection closed, clients=" + wsconns.length);
});
conn.on("error", function()
{
console.log("ws error");
});
}).listen(2222);
// web
var express = require("express");
var app = express();
app.get("/", function(req, res) {
res.sendFile(__dirname + "/index.html")
});
app.get(/^(.+)$/, function(req, res){
res.sendFile(__dirname + req.params[0]);
});
app.listen(8888, function() {
console.log("web: Server started at localhost:8888");
});
// bluetooth
var buffer = "";
address = "ab-03-67-78-29-69";
//address = "ab-04-b9-56-34-02";
channel = 1;
var reconnect = false;
var deadTimer = null;
serial.on('data', function(data) {
if (deadTimer)
clearTimeout(deadTimer);
deadTimer = setTimeout(()=>{
serial.close();
for (wsc in wsconns)
wsconns[wsc].sendText("R\n");
console.log("dead!");
}, 2000);
var s =data.toString().split("\n").join("");
if (s.length)
process.stdout.write(s);
for (wsc in wsconns)
wsconns[wsc].sendText(data);
});
serial.on('closed', function() {
if (reconnect)
clearTimeout(reconnect);
console.log("bt: connection closed, reconnecting...");
reconnect = setTimeout(btReconnect, 5000);
});
function btReconnect()
{
if (reconnect)
{
clearTimeout(reconnect);
reconnect = null;
}
console.log("bt: connecting...");
serial.connect(address, channel, function() {
console.log('bt: connected');
}, function () {
if (reconnect)
clearTimeout(reconnect);
console.log("bt: cannot connect, reconnecting...");
reconnect = setTimeout(btReconnect, 1000);
});
}
btReconnect();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment