Merge pull request #3490 from pinq-/master

Updates on accrec, pebbleapp and bthome app.
pull/3503/head
Rob Pilling 2024-07-10 20:11:42 +01:00 committed by GitHub
commit 1e82772de6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 247 additions and 169 deletions

View File

@ -4,3 +4,4 @@
Trigger on 1.04g now, and record 10 samples before trigger Trigger on 1.04g now, and record 10 samples before trigger
0.03: Bangle.js 2 compatibility 0.03: Bangle.js 2 compatibility
0.04: Minor code improvements 0.04: Minor code improvements
0.05: Can record 100hz, z-axis color changed to yellow, autosave to file, no need select, delete old records

View File

@ -1,179 +1,253 @@
//var acc; //var acc;
var HZ = 100; var HZ = 100;
var SAMPLES = 5*HZ; // 5 seconds var SAMPLES = 6 * HZ; // 6 seconds
var SCALE = 5000; var SCALE = 2000;
var THRESH = 1.04; var THRESH = 1.4;
var accelx = new Int16Array(SAMPLES); var accelx = new Int16Array(SAMPLES);
var accely = new Int16Array(SAMPLES); // North var accely = new Int16Array(SAMPLES); // North
var accelz = new Int16Array(SAMPLES); // Into clock face var accelz = new Int16Array(SAMPLES); // Into clock face
var timestep = new Int16Array(SAMPLES); // Into clock face
var accelIdx = 0; var accelIdx = 0;
var lastAccel; var lastAccel;
function accelHandlerTrigger(a) {"ram" var timestep_start = 0;
if (a.mag*2>THRESH) { // *2 because 8g mode
tStart = getTime();
g.drawString("Recording",g.getWidth()/2,g.getHeight()/2,1);
Bangle.removeListener('accel',accelHandlerTrigger);
Bangle.on('accel',accelHandlerRecord);
lastAccel.forEach(accelHandlerRecord);
accelHandlerRecord(a);
} else {
if (lastAccel.length>10) lastAccel.shift();
lastAccel.push(a);
}
}
function accelHandlerRecord(a) {"ram"
var i = accelIdx++;
accelx[i] = a.x*SCALE*2;
accely[i] = -a.y*SCALE*2;
accelz[i] = a.z*SCALE*2;
if (accelIdx>=SAMPLES) recordStop();
}
function recordStart() {"ram"
Bangle.setLCDTimeout(0); // force LCD on
accelIdx = 0;
lastAccel = [];
Bangle.accelWr(0x18,0b01110100); // off, +-8g
Bangle.accelWr(0x1B,0x03 | 0x40); // 100hz output, ODR/2 filter
Bangle.accelWr(0x18,0b11110100); // +-8g
Bangle.setPollInterval(10); // 100hz input
setTimeout(function() {
Bangle.on('accel',accelHandlerTrigger);
g.clear(1).setFont("6x8",2).setFontAlign(0,0);
g.drawString("Waiting",g.getWidth()/2,g.getHeight()/2);
}, 200);
}
function accelHandlerTrigger(a) {
function recordStop() {"ram" "ram"
//console.log("Length:",getTime()-tStart); if (a.mag * 2 > THRESH) { // *2 because 8g mode
Bangle.setPollInterval(80); // default poll interval timestep_start = getTime();
Bangle.accelWr(0x18,0b01101100); // off, +-4g g.drawString("Recording", g.getWidth() / 2, g.getHeight() / 2, 1);
Bangle.accelWr(0x1B,0x0); // default 12.5hz output Bangle.removeListener('accel', accelHandlerTrigger);
Bangle.accelWr(0x18,0b11101100); // +-4g Bangle.on('accel', accelHandlerRecord);
Bangle.removeListener('accel',accelHandlerRecord); lastAccel.forEach(accelHandlerRecord);
E.showMessage("Finished"); accelHandlerRecord(a);
showData(); } else {
} if (lastAccel.length > 10) lastAccel.shift();
lastAccel.push(a);
function showData() {
g.clear(1);
var w = g.getWidth()-20; // width
var m = g.getHeight()/2; // middle
var s = 12; // how many pixels per G
g.fillRect(9,0,9,g.getHeight());
g.setFontAlign(0,0);
for (var l=-8;l<=8;l++)
g.drawString(l, 5, m - l*s);
function plot(a) {
g.moveTo(10,m - a[0]*s/SCALE);
for (var i=0;i<SAMPLES;i++)
g.lineTo(10+i*w/SAMPLES, m - a[i]*s/SCALE);
}
g.setColor("#0000ff");
plot(accelz);
g.setColor("#ff0000");
plot(accelx);
g.setColor("#00ff00");
plot(accely);
// work out stats
var maxAccel = 0;
var tStart = SAMPLES, tEnd = 0;
var vel = 0, maxVel = 0;
for (var i=0;i<SAMPLES;i++) {
var a = accely[i]/SCALE;
if (a>0.1) {
if (i<tStart) tStart=i;
if (i>tEnd) tEnd=i;
} }
if (a>maxAccel) maxAccel=a; }
vel += a/HZ;
if (vel>maxVel) maxVel=vel; function accelHandlerRecord(a) {
} "ram"
g.reset(); var i = accelIdx++;
g.setFont("6x8").setFontAlign(1,0); accelx[i] = a.x * SCALE * 2; // *2 because of 8g mode
g.drawString("Max Y Accel: "+maxAccel.toFixed(2)+" g",g.getWidth()-14,g.getHeight()-50); accely[i] = -a.y * SCALE * 2;
g.drawString("Max Y Vel: "+maxVel.toFixed(2)+" m/s",g.getWidth()-14,g.getHeight()-40); accelz[i] = a.z * SCALE * 2;
g.drawString("Time moving: "+(tEnd-tStart)/HZ+" s",g.getWidth()-14,g.getHeight()-30); timestep[i] = (getTime() - timestep_start) * 1000;
//console.log("End Velocity "+vel); if (accelIdx >= SAMPLES) recordStop();
g.setFont("6x8").setFontAlign(0,0,1); }
g.drawString("FINISH",g.getWidth()-4,g.getHeight()/2);
setWatch(function() { function recordStart() {
showMenu(); "ram"
}, global.BTN2?BTN2:BTN); Bangle.setLCDTimeout(0); // force LCD on
accelIdx = 0;
lastAccel = [];
Bangle.accelWr(0x18, 0b01110100); // off, +-8g
Bangle.accelWr(0x1B, 0x03 | 0x40); // 100hz output, ODR/2 filter
Bangle.accelWr(0x18, 0b11110100); // +-8g
Bangle.setPollInterval(10); // 100hz input
setTimeout(function() {
Bangle.on('accel', accelHandlerTrigger);
g.clear(1).setFont("6x8", 2).setFontAlign(0, 0);
g.drawString("Waiting", g.getWidth() / 2, g.getHeight() / 2);
}, 200);
}
function recordStop() {
"ram"
//console.log("Length:",getTime()-tStart);
Bangle.setPollInterval(80); // default poll interval
Bangle.accelWr(0x18, 0b01101100); // off, +-4g
Bangle.accelWr(0x1B, 0x0); // default 12.5hz output
Bangle.accelWr(0x18, 0b11101100); // +-4g
Bangle.removeListener('accel', accelHandlerRecord);
E.showMessage("Finished");
showData(true);
}
function showData(save_file) {
g.clear(1);
let csv_files_N = require("Storage").list(/^acc.*\.csv$/).length;
let w_full = g.getWidth();
let h = g.getHeight();
var w = g.getWidth() - 20; // width
var m = g.getHeight() / 2; // middle
var s = 12; // how many pixels per G
g.fillRect(9, 0, 9, g.getHeight());
g.setFontAlign(0, 0);
for (var l = -8; l <= 8; l++)
g.drawString(l, 5, m - l * s);
function plot(a) {
g.moveTo(10, m - a[0] * s / SCALE);
for (var i = 0; i < SAMPLES; i++)
g.lineTo(10 + i * w / SAMPLES, m - a[i] * s / SCALE);
}
g.setColor("#FFFA5F");
plot(accelz);
g.setColor("#ff0000");
plot(accelx);
g.setColor("#00ff00");
plot(accely);
// work out stats
var maxAccel = 0;
var tStart = SAMPLES,
tEnd = 0;
var max_YZ = 0;
for (var i = 0; i < SAMPLES; i++) {
var a = Math.abs(accely[i] / SCALE);
let a_yz = Math.sqrt(Math.pow(accely[i] / SCALE, 2) + Math.pow(accelz[i] / SCALE, 2));
if (a > 0.1) {
if (i < tStart) tStart = i;
if (i > tEnd) tEnd = i;
}
if (a > maxAccel) maxAccel = a;
if (a_yz > max_YZ) max_YZ = a_yz;
}
g.reset();
g.setFont("6x8").setFontAlign(1, 0);
g.drawString("Max X Accel: " + maxAccel.toFixed(2) + " g", g.getWidth() - 14, g.getHeight() - 50);
g.drawString("Max YZ Accel: " + max_YZ.toFixed(2) + " g", g.getWidth() - 14, g.getHeight() - 40);
g.drawString("Time moving: " + (tEnd - tStart) / HZ + " s", g.getWidth() - 14, g.getHeight() - 30);
g.setFont("6x8", 2).setFontAlign(0, 0);
g.drawString("File num: " + (csv_files_N + 1), w_full / 2, h - 20);
g.setFont("6x8").setFontAlign(0, 0, 1);
g.drawString("FINISH", g.getWidth() - 4, g.getHeight() / 2);
setWatch(function() {
if (save_file) showSaveMenu(); // when select only plot, don't ask for save option
else showMenu();
}, global.BTN2 ? BTN2 : BTN);
} }
function showBig(txt) { function showBig(txt) {
g.clear(1); g.clear(1);
g.setFontVector(80).setFontAlign(0,0); g.setFontVector(80).setFontAlign(0, 0);
g.drawString(txt,g.getWidth()/2, g.getHeight()/2); g.drawString(txt, g.getWidth() / 2, g.getHeight() / 2);
g.flip(); g.flip();
} }
function countDown() { function countDown() {
showBig(3); showBig(3);
setTimeout(function() {
showBig(2);
setTimeout(function() { setTimeout(function() {
showBig(1); showBig(2);
setTimeout(function() { setTimeout(function() {
recordStart(); showBig(1);
}, 800); setTimeout(function() {
recordStart();
}, 800);
}, 1000);
}, 1000); }, 1000);
}, 1000);
} }
function showMenu() { function showMenu() {
Bangle.setLCDTimeout(10); // set timeout for LCD in menu Bangle.setLCDTimeout(10); // set timeout for LCD in menu
var menu = { var menu = {
"" : { title : "Acceleration Rec" }, "": { title: "Acceleration Rec" },
"Start" : function() { "Start": function() {
E.showMenu(); E.showMenu();
if (accelIdx==0) countDown(); if (accelIdx == 0) countDown();
else E.showPrompt("Overwrite Recording?").then(ok=>{ else E.showPrompt("Overwrite Recording?").then(ok => {
if (ok) countDown(); else showMenu(); if (ok) countDown();
}); else showMenu();
}, });
"Plot" : function() { },
E.showMenu(); "Plot": function() {
if (accelIdx) showData(); E.showMenu();
else E.showAlert("No Data").then(()=>{ if (accelIdx) showData(false);
showMenu(); else E.showAlert("No Data").then(() => {
}); showMenu();
}, });
"Save" : function() { },
E.showMenu(); "Storage": function() {
if (accelIdx) showSaveMenu(); E.showMenu();
else E.showAlert("No Data").then(()=>{ if (require("Storage").list(/^acc.*\.csv$/).length)
showMenu(); StorageMenu();
}); else
}, E.showAlert("No Data").then(() => {
"Exit" : function() { showMenu();
load(); });
}, },
}; "Exit": function() {
E.showMenu(menu); load();
},
};
E.showMenu(menu);
} }
function showSaveMenu() { function showSaveMenu() {
var menu = { E.showPrompt("Save recording?").then(ok => {
"" : { title : "Save" } if (ok)
}; SaveFile();
[1,2,3,4,5,6].forEach(i=>{ else
var fn = "accelrec."+i+".csv"; showMenu();
var exists = require("Storage").read(fn)!==undefined; });
menu["Recording "+i+(exists?" *":"")] = function() {
var csv = "";
for (var i=0;i<SAMPLES;i++)
csv += `${accelx[i]/SCALE},${accely[i]/SCALE},${accelz[i]/SCALE}\n`;
require("Storage").write(fn,csv);
showMenu();
};
});
menu["< Back"] = function() {showMenu();};
E.showMenu(menu);
} }
showMenu(); function SaveFile() {
let csv_files_N = require("Storage").list(/^acc.*\.csv$/).length;
//if (csv_files_N > 20)
// E.showMessage("Storage is full");
// showMenu();
let csv = "";
let date = new Date();
let fn = "accelrec_" + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds() + "_" + (csv_files_N + 1) + ".csv";
E.showMessage("Saveing to file \n" + fn);
for (var i = 0; i < SAMPLES; i++)
csv += `${timestep[i]},${accelx[i]/SCALE},${accely[i]/SCALE},${accelz[i]/SCALE}\n`;
require("Storage").write(fn, csv);
showMenu();
}
//Show saved csv files
function StorageMenu() {
var menu = {
"": {
title: "Storage"
}
};
let csv_files = require("Storage").list(/^acc.*\.csv$/);
var inx = 0;
csv_files.forEach(fn => {
inx++;
menu[inx + ". " + fn] = function() {
StorageOptions(fn);
};
});
menu["< Back"] = function() {
showMenu();
};
E.showMenu(menu);
}
function StorageOptions(file) {
let menu = {
"": {
title: "Options"
},
"Plot": function() {
showMenu();
},
"Delete": function() {
E.showMenu();
E.showPrompt("Delete recording?").then(ok => {
if (ok)
DeleteRecord(file);
else
StorageMenu();
});
},
"< Back": function() {
StorageMenu();
},
};
E.showMenu(menu);
}
function DeleteRecord(file) {
E.showMessage("Deleteing file \n" + file);
require("Storage").erase(file);
StorageMenu();
}
showMenu();

View File

@ -37,7 +37,7 @@ function getData() {
</div>`; </div>`;
promise = promise.then(function() { promise = promise.then(function() {
document.querySelector(`.btn[fn='${fn}'][act='save']`).addEventListener("click", function() { document.querySelector(`.btn[fn='${fn}'][act='save']`).addEventListener("click", function() {
Util.saveCSV(fn.slice(0,-4), "X,Y,Z\n"+fileData[fn]); Util.saveCSV(fn.slice(0,-4), "Time,X,Y,Z\n"+fileData[fn]);
}); });
document.querySelector(`.btn[fn='${fn}'][act='delete']`).addEventListener("click", function() { document.querySelector(`.btn[fn='${fn}'][act='delete']`).addEventListener("click", function() {
Util.showModal("Deleting..."); Util.showModal("Deleting...");

View File

@ -2,7 +2,7 @@
"id": "accelrec", "id": "accelrec",
"name": "Acceleration Recorder", "name": "Acceleration Recorder",
"shortName": "Accel Rec", "shortName": "Accel Rec",
"version": "0.04", "version": "0.05",
"description": "This app puts the Bangle's accelerometer into 100Hz mode and reads 2 seconds worth of data after movement starts. The data can then be exported back to the PC.", "description": "This app puts the Bangle's accelerometer into 100Hz mode and reads 2 seconds worth of data after movement starts. The data can then be exported back to the PC.",
"icon": "app.png", "icon": "app.png",
"tags": "", "tags": "",

View File

@ -4,3 +4,4 @@
Set 'n' for buttons in Bangle.btHomeData correctly (avoids adding extra buttons on end of advertising) Set 'n' for buttons in Bangle.btHomeData correctly (avoids adding extra buttons on end of advertising)
0.04: Fix duplicate button on edit->save 0.04: Fix duplicate button on edit->save
0.05: Use the bleAdvert module 0.05: Use the bleAdvert module
0.06: button number can't be 0. Now generates number automatically

View File

@ -1,7 +1,7 @@
{ "id": "bthome", { "id": "bthome",
"name": "BTHome", "name": "BTHome",
"shortName":"BTHome", "shortName":"BTHome",
"version":"0.05", "version":"0.06",
"description": "Allow your Bangle to advertise with BTHome and send events to Home Assistant via Bluetooth", "description": "Allow your Bangle to advertise with BTHome and send events to Home Assistant via Bluetooth",
"icon": "icon.png", "icon": "icon.png",
"type": "app", "type": "app",

View File

@ -11,10 +11,14 @@
require("Storage").writeJSON("bthome.json",settings) require("Storage").writeJSON("bthome.json",settings)
} }
// Get id number for button that is sent to bthome
function getNewIdNumber(){
return [1, 2, 3, 4, 5, 6, 7, 8, 9].find(id => settings.buttons.every(button => id != button.n));
}
function showButtonMenu(button, isNew) { function showButtonMenu(button, isNew) {
var isNew = false;
if (!button) { if (!button) {
button = {name:"home", icon:"home", n:0, v:"press"}; button = {name:"home", icon:"home", n:getNewIdNumber(), v:"press"};
isNew = true; isNew = true;
} }
var actions = ["press","double_press","triple_press","long_press","long_double_press","long_triple_press"]; var actions = ["press","double_press","triple_press","long_press","long_double_press","long_triple_press"];
@ -51,10 +55,6 @@
format : v => actions[v], format : v => actions[v],
onchange : v => button.v=actions[v] onchange : v => button.v=actions[v]
}, },
/*LANG*/"Button #" : {
value : button.n, min:0, max:3,
onchange : v => button.n=v
},
/*LANG*/"Save" : () => { /*LANG*/"Save" : () => {
if (isNew) settings.buttons.push(button); if (isNew) settings.buttons.push(button);
saveSettings(); saveSettings();

View File

@ -6,4 +6,5 @@
0.05: Minor code improvements 0.05: Minor code improvements
0.06: Use the clockbg library to allow custom image backgrounds 0.06: Use the clockbg library to allow custom image backgrounds
0.07: Fix automatic coloring of middle of clockinfo images if clockinfo image goes right to the top or left 0.07: Fix automatic coloring of middle of clockinfo images if clockinfo image goes right to the top or left
0.08: Use new clockinfo lib with function to render images wirh borders 0.08: Use new clockinfo lib with function to render images wirh borders
0.09: Add date on the bottom

View File

@ -38,6 +38,9 @@ let draw = function() {
g.reset(); g.reset();
g.setBgColor(theme.bg).clearRect(0, h2, w, h3); g.setBgColor(theme.bg).clearRect(0, h2, w, h3);
g.setColor(theme.fg).fillRect(w / 2 - 30, h3 + 5, w / 2 + 30, h); // refresh date background
g.setFontLECO1976Regular22().setFontAlign(0, -1);
g.setColor(theme.bg).drawString(date.getDate() + "." + (date.getMonth() + 1), w / 2, h3 + 5);
g.setFontLECO1976Regular42().setFontAlign(0, -1); g.setFontLECO1976Regular42().setFontAlign(0, -1);
g.setColor(theme.fg); g.setColor(theme.fg);
g.drawString(time, w/2, h2 + 8); g.drawString(time, w/2, h2 + 8);
@ -130,10 +133,8 @@ Bangle.setUI({
Bangle.loadWidgets(); Bangle.loadWidgets();
require("widget_utils").swipeOn(); // hide widgets, make them visible with a swipe require("widget_utils").swipeOn(); // hide widgets, make them visible with a swipe
background.fillRect(Bangle.appRect); // start off with completely clear background background.fillRect(Bangle.appRect); // start off with completely clear background
// contrast bar (top) // background contrast bar
g.setColor(theme.fg).fillRect(0, h2 - 6, w, h2); g.setColor(theme.fg).fillRect(0, h2 - 6, w, h3 + 5);
// contrast bar (bottom)
g.setColor(theme.fg).fillRect(0, h3, w, h3 + 6);
draw(); draw();
} }

View File

@ -2,7 +2,7 @@
"id": "pebblepp", "id": "pebblepp",
"name": "Pebble++ Clock", "name": "Pebble++ Clock",
"shortName": "Pebble++", "shortName": "Pebble++",
"version": "0.08", "version": "0.09",
"description": "A pebble style clock (based on the 'Pebble Clock' app) but with two configurable ClockInfo items at the top", "description": "A pebble style clock (based on the 'Pebble Clock' app) but with two configurable ClockInfo items at the top",
"icon": "app.png", "icon": "app.png",
"screenshots": [{"url":"screenshot.png"}], "screenshots": [{"url":"screenshot.png"}],