Created
December 8, 2017 20:23
-
-
Save gabonator/99d07017259272936691ce7f8c1e7a4d to your computer and use it in GitHub Desktop.
Energy meter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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