Sometimes it might be useful to monitor a battery's voltage or current usage over time. Here we're using an Espruino board and a few passive components to monitor voltage and current, log them, and display them as a graph on the PC.
Connect as in the circuit above. We're using:
But any analog-capable inputs will do.
We're using the Averager Library here to store data in a format that is easy to graph:
var Averager = require("Averager").Averager;
var volts, amps;
var voltAvr = new Averager({scale:1000});
var ampAvr = new Averager({scale:1000});
function onSecond() {
amps = analogRead(D29)*3.3;
volts = analogRead(D31)*3.3*(100+24)/24 - amps;
voltAvr.add(volts);
ampAvr.add(amps);
var data = {
v:Math.round(volts*100)/100,
a:Math.round(amps*100)/100
};
NRF.setAdvertising({},{
name:"\xF0\x9F\x9A\x98", // car emoji, see https://www.espruino.com/BLE+Emoji
manufacturer:0x0590,
manufacturerData:JSON.stringify(data)
});
}
setInterval(onSecond, 1000);
NRF.setTxPower(4);
It's easy to extend this with the BTHome Library to allow your
battery level to appear in Home Assistant. We've kept the Averager
in
so the webpage below will still work, but it's easy to remove:
var Averager = require("Averager").Averager;
var volts, amps;
var voltSum=0, ampSum=0, sumCount=0; // average for BTHome
var voltAvr = new Averager({scale:1000});
var ampAvr = new Averager({scale:1000});
const BAT_MIN = 11.63, BAT_MAX= 12.64;
function updateAdvertising() {
// work out values based on average over last minute
if (sumCount==0) sumCount=1;
var volts = voltSum / sumCount;
var amps = ampSum / sumCount;
voltSum=0;
ampSum=0;
sumCount=0;
// set advertising
NRF.setAdvertising(require("BTHome").getAdvertisement([
{
type : "battery",
v : E.clip(100*(volts-BAT_MIN)/(BAT_MAX-BAT_MIN),0,100)
},
{
type : "temperature",
v : E.getTemperature()
},
{
type : "voltage",
v : volts
},
{
type : "current",
v : amps
},
]), {
name:"\xF0\x9F\x9A\x98", // car emoji, https://www.espruino.com/BLE+Emoji
// not being connectable/scannable saves power (but you'll need to reboot to connect again with the IDE!)
//connectable : false, scannable : false,
});
}
function onSecond() {
amps = analogRead(D29)*3.3;
volts = analogRead(D31)*3.3*(100+24)/24 - amps;
voltAvr.add(volts);
ampAvr.add(amps);
voltSum += volts;
ampSum += amps;
sumCount++;
var data = {
v:Math.round(volts*100)/100,
a:Math.round(amps*100)/100
};
}
setInterval(onSecond, 1000);
setInterval(updateAdvertising, 60000); // only update advertising once a minute
updateAdvertising();
NRF.setTxPower(4);
You'll need to serve this off a HTTPS link - see the Web Bluetooth page for more information.
Note: you can try this out as-is just by clicking the Try Me!
link.
<html>
<head>
</head>
<body>
<div id="data"></div>
<script src="https://www.puck-js.com/puck.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js"></script>
<script>
var dataElement = document.getElementById("data");
var voltData, ampData;
var csvData = "";
function addChart(id, voltData, ampData, title) {
var labels = voltData.current.map((y,x)=>x);
if (id == "hrChart") labels = voltData.current.map((y,x)=>`${x>>2}:${((x&3)*15).toString().padStart(2,0)}`);
if (id == "moChart") labels = "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sept,Oct,Nov,Dec".split(",");
new Chart(document.getElementById(id), {
type: 'line',
data: {
labels : labels,
datasets : [
{ data: voltData.current, label: 'Volts', borderColor: "red",
backgroundColor: 'rgba(0, 0, 0, 0)', fill: false, },
{ data: voltData.last, label: 'Last Volts', borderColor: "darkred",
backgroundColor: 'rgba(0, 0, 0, 0)', fill: false, },
{ data: voltData.avr, label: 'Average Volts', borderColor: "grey",
backgroundColor: 'rgba(0, 0, 0, 0)', fill: false, },
{ data: ampData.current.map(x=>x*10), label: 'Amps x10', borderColor: "green",
backgroundColor: 'rgba(0, 0, 0, 0)', fill: false, },
{ data: ampData.last.map(x=>x*10), label: 'Last Amps x10', borderColor: "darkgreen",
backgroundColor: 'rgba(0, 0, 0, 0)', fill: false, },
{ data: ampData.avr.map(x=>x*10), label: 'Average Amps x10', borderColor: "grey",
backgroundColor: 'rgba(0, 0, 0, 0)', fill: false, },
]
},
options: {
responsive: true,
title: {
display: true,
text: title
},
tooltips: {
mode: 'index'
}
}
});
}
function getData() {
// get the data
dataElement.innerHTML = '<span style="font-size:300%;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)">Loading...</span>';
Puck.write(`\x10voltAvr.print()\n`,data=>{
// if we have a JS prompt at the end of what's printed, remove it
if (data.endsWith(">"))
data = data.slice(0,-1);
voltData = JSON.parse(data);
Puck.write(`\x10ampAvr.print()\n`,data=>{
// if we have a JS prompt at the end of what's printed, remove it
if (data.endsWith(">"))
data = data.slice(0,-1);
ampData = JSON.parse(data);
// Draw graphs
dataElement.innerHTML = `
<div><canvas id="hrChart"></div>
<div><canvas id="dayChart"></div>
<div><canvas id="moChart"></div>`;
addChart('hrChart', voltData.hours, ampData.hours, "Hourly");
addChart('dayChart', voltData.days, ampData.days, "Daily");
addChart('moChart', voltData.months, ampData.months, "Monthly");
});
});
}
Puck.modal(function() {
getData();
});
</script>
</body>
</html>
This page is auto-generated from GitHub. If you see any mistakes or have suggestions, please let us know.