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,16 +1,20 @@
//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;
function accelHandlerTrigger(a) {
"ram"
if (a.mag * 2 > THRESH) { // *2 because 8g mode if (a.mag * 2 > THRESH) { // *2 because 8g mode
tStart = getTime(); timestep_start = getTime();
g.drawString("Recording", g.getWidth() / 2, g.getHeight() / 2, 1); g.drawString("Recording", g.getWidth() / 2, g.getHeight() / 2, 1);
Bangle.removeListener('accel', accelHandlerTrigger); Bangle.removeListener('accel', accelHandlerTrigger);
Bangle.on('accel', accelHandlerRecord); Bangle.on('accel', accelHandlerRecord);
@ -21,14 +25,19 @@ function accelHandlerTrigger(a) {"ram"
lastAccel.push(a); lastAccel.push(a);
} }
} }
function accelHandlerRecord(a) {"ram"
function accelHandlerRecord(a) {
"ram"
var i = accelIdx++; var i = accelIdx++;
accelx[i] = a.x*SCALE*2; accelx[i] = a.x * SCALE * 2; // *2 because of 8g mode
accely[i] = -a.y * SCALE * 2; accely[i] = -a.y * SCALE * 2;
accelz[i] = a.z * SCALE * 2; accelz[i] = a.z * SCALE * 2;
timestep[i] = (getTime() - timestep_start) * 1000;
if (accelIdx >= SAMPLES) recordStop(); if (accelIdx >= SAMPLES) recordStop();
} }
function recordStart() {"ram"
function recordStart() {
"ram"
Bangle.setLCDTimeout(0); // force LCD on Bangle.setLCDTimeout(0); // force LCD on
accelIdx = 0; accelIdx = 0;
lastAccel = []; lastAccel = [];
@ -44,7 +53,8 @@ function recordStart() {"ram"
} }
function recordStop() {"ram" function recordStop() {
"ram"
//console.log("Length:",getTime()-tStart); //console.log("Length:",getTime()-tStart);
Bangle.setPollInterval(80); // default poll interval Bangle.setPollInterval(80); // default poll interval
Bangle.accelWr(0x18, 0b01101100); // off, +-4g Bangle.accelWr(0x18, 0b01101100); // off, +-4g
@ -52,12 +62,15 @@ function recordStop() {"ram"
Bangle.accelWr(0x18, 0b11101100); // +-4g Bangle.accelWr(0x18, 0b11101100); // +-4g
Bangle.removeListener('accel', accelHandlerRecord); Bangle.removeListener('accel', accelHandlerRecord);
E.showMessage("Finished"); E.showMessage("Finished");
showData(); showData(true);
} }
function showData() { function showData(save_file) {
g.clear(1); 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 w = g.getWidth() - 20; // width
var m = g.getHeight() / 2; // middle var m = g.getHeight() / 2; // middle
var s = 12; // how many pixels per G var s = 12; // how many pixels per G
@ -71,7 +84,7 @@ function showData() {
for (var i = 0; i < SAMPLES; i++) for (var i = 0; i < SAMPLES; i++)
g.lineTo(10 + i * w / SAMPLES, m - a[i] * s / SCALE); g.lineTo(10 + i * w / SAMPLES, m - a[i] * s / SCALE);
} }
g.setColor("#0000ff"); g.setColor("#FFFA5F");
plot(accelz); plot(accelz);
g.setColor("#ff0000"); g.setColor("#ff0000");
plot(accelx); plot(accelx);
@ -80,28 +93,31 @@ function showData() {
// work out stats // work out stats
var maxAccel = 0; var maxAccel = 0;
var tStart = SAMPLES, tEnd = 0; var tStart = SAMPLES,
var vel = 0, maxVel = 0; tEnd = 0;
var max_YZ = 0;
for (var i = 0; i < SAMPLES; i++) { for (var i = 0; i < SAMPLES; i++) {
var a = accely[i]/SCALE; 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 (a > 0.1) {
if (i < tStart) tStart = i; if (i < tStart) tStart = i;
if (i > tEnd) tEnd = i; if (i > tEnd) tEnd = i;
} }
if (a > maxAccel) maxAccel = a; if (a > maxAccel) maxAccel = a;
vel += a/HZ; if (a_yz > max_YZ) max_YZ = a_yz;
if (vel>maxVel) maxVel=vel;
} }
g.reset(); g.reset();
g.setFont("6x8").setFontAlign(1, 0); g.setFont("6x8").setFontAlign(1, 0);
g.drawString("Max Y Accel: "+maxAccel.toFixed(2)+" g",g.getWidth()-14,g.getHeight()-50); g.drawString("Max X Accel: " + maxAccel.toFixed(2) + " g", g.getWidth() - 14, g.getHeight() - 50);
g.drawString("Max Y Vel: "+maxVel.toFixed(2)+" m/s",g.getWidth()-14,g.getHeight()-40); 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.drawString("Time moving: " + (tEnd - tStart) / HZ + " s", g.getWidth() - 14, g.getHeight() - 30);
//console.log("End Velocity "+vel); 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.setFont("6x8").setFontAlign(0, 0, 1);
g.drawString("FINISH", g.getWidth() - 4, g.getHeight() / 2); g.drawString("FINISH", g.getWidth() - 4, g.getHeight() / 2);
setWatch(function() { setWatch(function() {
showMenu(); if (save_file) showSaveMenu(); // when select only plot, don't ask for save option
else showMenu();
}, global.BTN2 ? BTN2 : BTN); }, global.BTN2 ? BTN2 : BTN);
} }
@ -133,20 +149,23 @@ function showMenu() {
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() { "Plot": function() {
E.showMenu(); E.showMenu();
if (accelIdx) showData(); if (accelIdx) showData(false);
else E.showAlert("No Data").then(() => { else E.showAlert("No Data").then(() => {
showMenu(); showMenu();
}); });
}, },
"Save" : function() { "Storage": function() {
E.showMenu(); E.showMenu();
if (accelIdx) showSaveMenu(); if (require("Storage").list(/^acc.*\.csv$/).length)
else E.showAlert("No Data").then(()=>{ StorageMenu();
else
E.showAlert("No Data").then(() => {
showMenu(); showMenu();
}); });
}, },
@ -158,22 +177,77 @@ function showMenu() {
} }
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 = "";
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++) for (var i = 0; i < SAMPLES; i++)
csv += `${accelx[i]/SCALE},${accely[i]/SCALE},${accelz[i]/SCALE}\n`; csv += `${timestep[i]},${accelx[i]/SCALE},${accely[i]/SCALE},${accelz[i]/SCALE}\n`;
require("Storage").write(fn, csv); require("Storage").write(fn, csv);
showMenu(); 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();}; menu["< Back"] = function() {
showMenu();
};
E.showMenu(menu); 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(); 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

@ -7,3 +7,4 @@
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"}],