Skip to content

Commit 3fc5c38

Browse files
committed
ESP32 MicroPython improvements. Mostly nicer graphs.
* Realtime graph scrolls nicely and is capped at 2 minutes. * Energy graph updates during download and goes most recent to least. * Also updated Wifi to fallback to AP mode. * Only setting the time once to avoid jumps.
1 parent 99a6ce4 commit 3fc5c38

File tree

7 files changed

+9293
-122
lines changed

7 files changed

+9293
-122
lines changed

esp32-micropython/.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ __pycache__/
55
dist/
66
node_modules/
77

8-
settings.py
8+
settings.json

esp32-micropython/README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ Flash the ESP32 with Micropython: https://micropython.org/download#esp32
1818
To upload both the server and client code to the flash memory, you need rshell. Install Python, then the required packages:
1919
`pip install -r requirements.txt`
2020

21-
To build and package the client code, you need node.js. Install that.
21+
To build and package the client code, you need node.js. Install that and do:
22+
`npm install`
2223

2324
Then, update the makefile to match your serial port settings.
2425

esp32-micropython/client/index.js

+163-84
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ function getTimeParameter(){
1919
}
2020

2121
async function drawRealtime(){
22+
var minutes_to_show = 2;
23+
var max_minutes_to_show = 10;
2224

2325
var min_time = new Date();
2426
min_time.setSeconds(0);
@@ -42,15 +44,17 @@ async function drawRealtime(){
4244
}]
4345
},
4446
options: {
47+
tooltips: {
48+
enabled: false,
49+
},
4550
scales: {
4651
xAxes: [{
4752
type: "time",
4853
position: "bottom",
4954
time: {
5055
unit: 'minute',
5156
stepSize: 1,
52-
min: min_time
53-
}
57+
},
5458
}],
5559
yAxes: [{
5660
scaleLabel: {
@@ -65,6 +69,15 @@ async function drawRealtime(){
6569
}
6670
});
6771

72+
function adjustAxisForTimeProgression(chart, minutes_to_show){
73+
var ticks = chart.options.scales.xAxes[0].ticks;
74+
ticks.max = new Date();
75+
ticks.min = new Date(ticks.max.getTime() - minutes_to_show*60000);
76+
chart.update();
77+
}
78+
79+
setInterval(function() {adjustAxisForTimeProgression(chart, minutes_to_show);}, 50);
80+
6881
async function processData(data){
6982
var records = data.split("\n");
7083

@@ -85,16 +98,20 @@ async function drawRealtime(){
8598
return [in_power, out_power];
8699
}
87100

88-
async function updateChart(chart, in_power, out_power){
89-
for (const value of in_power){
90-
data = chart.data.datasets[0].data;
91-
data.push(value);
92-
}
93-
for (const value of out_power){
94-
data = chart.data.datasets[1].data;
95-
data.push(value);
101+
function addToRecentData(data, new_data){
102+
data.push(...new_data);
103+
104+
// Removing data that won't be shown anymore:
105+
var current_time = new Date();
106+
while (data.length > 0 && (current_time - data[0].x.getTime())/1000/60 > max_minutes_to_show){
107+
data.shift();
96108
}
97-
chart.update();
109+
}
110+
111+
async function updateChart(chart, in_power, out_power){
112+
addToRecentData(chart.data.datasets[0].data, in_power);
113+
addToRecentData(chart.data.datasets[1].data, out_power);
114+
chart.update(0);
98115
}
99116

100117
var socket = new WebSocket(WS_URL);
@@ -127,35 +144,58 @@ async function drawRealtime(){
127144
}
128145

129146
async function drawPowerUsage(){
130-
function processLogResponse(response_text, meta){
147+
function processLogResponse(response_text, offset){
131148
var data = [];
132149
var records = response_text.split("\n");
133150
for (const record of records){
134151
var values = record.split(",");
135-
values[0] = Number(values[0]) + meta["start_time_offset"];
152+
values[0] = Number(values[0]) + offset;
136153
values[1] = Number(values[1]);
137154
values[2] = Number(values[2]);
138155
data.push(values);
139156
}
140157
return data;
141158
}
142159

143-
async function getData(){
160+
async function* getData(){
144161
var response = await fetch(API_URL + '/data/meta.json');
145162
var meta = await response.json();
146-
var data = [];
147163

148-
for (const [key, value] of Object.entries(meta["logs"])){
149-
console.log("Downloading " + key + '.csv');
150-
var response = await fetch(API_URL + '/data/' + key + '.csv');
151-
var response_text = await response.text();
152-
data = data.concat(await processLogResponse(response_text, value));
164+
var logs = [];
165+
for (const [log_number, log_meta] of Object.entries(meta["logs"])){
166+
logs.push({
167+
number: log_number,
168+
start_time_offset: log_meta.start_time_offset,
169+
start_time: log_meta.start_time,
170+
})
153171
}
172+
console.log("Number of log files:");
173+
console.log(logs.length);
154174

155-
return data;
175+
176+
// Skip data that doesn't have complete time information:
177+
logs = logs.filter(function(a){
178+
return !(a.start_time_offset === null);
179+
});
180+
181+
// Load the newest data first as that's most likely to be viewed first:
182+
logs.sort(function(a, b) {
183+
var a_value = a.start_time_offset + a.start_time;
184+
var b_value = b.start_time_offset + b.start_time;
185+
return b_value - a_value;
186+
})
187+
188+
// Download the data:
189+
for (const log of logs){
190+
console.log(`Downloading ${log.number}.csv (${new Date(log.start_time_offset + log.start_time)})`);
191+
192+
var response = await fetch(API_URL + '/data/' + log.number + '.csv');
193+
var response_text = await response.text();
194+
yield await processLogResponse(response_text, log.start_time_offset);
195+
}
156196
}
157197

158-
async function processData(data, bucketize){
198+
function processData(data, bucketize){
159199
var buckets = {};
160200
var in_energy = [];
161201
var out_energy = [];
@@ -167,7 +207,7 @@ async function drawPowerUsage(){
167207
var bucket = bucketize(time);
168208
var bucket_value = buckets[bucket];
169209
if (typeof(bucket_value) == "undefined"){
170-
console.log("Initializing bucket " + new Date(bucket));
210+
// console.log("Initializing bucket " + new Date(bucket));
171211
bucket_value = {"in": 0, "out": 0};
172212
buckets[bucket] = bucket_value;
173213
}
@@ -178,12 +218,10 @@ async function drawPowerUsage(){
178218
}
179219
}
180220

181-
console.log(buckets);
182-
183221
for (const key of Object.keys(buckets).sort()){
184222
var time = new Date(Number(key));
185-
in_energy.push({x: time, y: buckets[key]["in"]});
186-
out_energy.push({x: time, y: -buckets[key]["out"]});
223+
in_energy.push({x: time, y: Math.round(buckets[key]["in"])});
224+
out_energy.push({x: time, y: Math.round(-buckets[key]["out"])});
187225
}
188226

189227
return [in_energy, out_energy];
@@ -198,66 +236,107 @@ async function drawPowerUsage(){
198236
return bucket_date.getTime();
199237
}
200238

201-
var data = await getData();
202-
var [in_energy, out_energy] = await processData(data, bucketize_15_min);
239+
function createChart(ctx, in_energy, out_energy){
240+
var min_time = new Date();
241+
min_time.setHours(0);
242+
min_time.setMinutes(0);
243+
min_time.setSeconds(0);
244+
min_time.setMilliseconds(0);
203245

204-
var min_time = new Date();
205-
min_time.setHours(0);
206-
min_time.setMinutes(0);
207-
min_time.setSeconds(0);
208-
min_time.setMilliseconds(0);
246+
var max_time = new Date(min_time);
247+
max_time.setDate(min_time.getDate() + 1);
209248

210-
var max_time = new Date(min_time);
211-
max_time.setDate(min_time.getDate() + 1);
212249

213-
var ctx = document.getElementById('today_power').getContext('2d');
214-
var chart = new Chart(ctx, {
215-
type: 'bar',
216-
data: {
217-
datasets: [{
218-
label: "In",
219-
data: in_energy,
220-
fill: false,
221-
borderColor: "blue",
222-
backgroundColor: "blue",
223-
}, {
224-
label: "Out",
225-
data: out_energy,
226-
fill: false,
227-
borderColor: "orange",
228-
backgroundColor: "orange",
229-
}]
230-
},
231-
options: {
232-
scales: {
233-
xAxes: [{
234-
type: "time",
235-
position: "bottom",
236-
stacked: true,
237-
gridLines: {
238-
offsetGridLines: false
239-
},
240-
// barThickness: 2,
241-
time: {
242-
unit: 'hour',
243-
stepSize: 3,
244-
min: min_time,
245-
max: max_time,
246-
}
247-
}],
248-
yAxes: [{
249-
stacked: true,
250-
scaleLabel: {
251-
display: true,
252-
labelString: "Energy [Wh]"
253-
},
254-
ticks: {
255-
beginAtZero: true
256-
}
250+
var chart = new Chart(ctx, {
251+
type: 'bar',
252+
data: {
253+
datasets: [{
254+
label: "In",
255+
data: [],
256+
fill: false,
257+
borderColor: "blue",
258+
backgroundColor: "blue",
259+
}, {
260+
label: "Out",
261+
data: [],
262+
fill: false,
263+
borderColor: "orange",
264+
backgroundColor: "orange",
257265
}]
266+
},
267+
options: {
268+
scales: {
269+
xAxes: [{
270+
type: "time",
271+
position: "bottom",
272+
stacked: true,
273+
gridLines: {
274+
offsetGridLines: false
275+
},
276+
time: {
277+
unit: 'hour',
278+
stepSize: 3,
279+
},
280+
ticks: {
281+
min: min_time,
282+
max: max_time,
283+
}
284+
}],
285+
yAxes: [{
286+
stacked: true,
287+
scaleLabel: {
288+
display: true,
289+
labelString: "Energy [Wh]"
290+
},
291+
ticks: {
292+
beginAtZero: true
293+
}
294+
}]
295+
}
296+
}
297+
});
298+
299+
energy_chart = chart;
300+
301+
return chart;
302+
}
303+
304+
function updateDataInPlace(existing_data, new_data){
305+
for (const new_value of new_data){
306+
var found = false;
307+
for (var existing_value of existing_data){
308+
if (existing_value.x.getTime() == new_value.x.getTime()){
309+
found = true;
310+
existing_value.y = new_value.y;
311+
break;
312+
}
313+
}
314+
if (!found){
315+
existing_data.push(new_value);
258316
}
259317
}
260-
});
318+
}
319+
320+
function updateChart(chart, in_energy, out_energy){
321+
updateDataInPlace(chart.data.datasets[0].data, in_energy);
322+
updateDataInPlace(chart.data.datasets[1].data, out_energy);
323+
chart.update();
324+
}
325+
326+
var ctx = document.getElementById('today_power').getContext('2d');
327+
var chart = createChart(ctx);
328+
329+
var all_data = [];
330+
331+
332+
for await (const data of getData()) {
333+
all_data.push(...data);
334+
var [in_energy, out_energy] = processData(all_data, bucketize_15_min);
335+
updateChart(chart, in_energy, out_energy)
336+
}
337+
338+
console.log("Done updating chart with with all the data!");
339+
261340
}
262341

263342
async function drawDiskUsage(){
@@ -303,9 +382,9 @@ async function drawDiskUsage(){
303382
async function main(){
304383
// await fetch(API_URL + "/set_time?" + getTimeParameter(), {"method": "POST"});
305384

306-
await drawRealtime();
307-
await drawDiskUsage();
308-
await drawPowerUsage();
385+
drawRealtime();
386+
drawDiskUsage();
387+
drawPowerUsage();
309388
}
310389

311390
console.log('Loading...');

0 commit comments

Comments
 (0)