bthrm - Better stability and auto reconnect

pull/1353/head
Martin Boonk 2022-01-25 21:16:43 +01:00
parent e997ad59ed
commit 26226ffc9c
4 changed files with 180 additions and 75 deletions

View File

@ -5,3 +5,5 @@
0.03: Prevent readings from internal sensor mixing into BT values 0.03: Prevent readings from internal sensor mixing into BT values
Mark events with src property Mark events with src property
Show actual source of event in app Show actual source of event in app
0.04: Automatically reconnect BT sensor
App buzzes if no BTHRM events for more than 3 seconds

View File

@ -1,13 +1,28 @@
(function() { (function() {
var log = function() {};//print //var sf = require("Storage").open("bthrm.log","a");
var log = function(text, param){
/*var logline = Date.now().toFixed(3) + " - " + text;
if (param){
logline += " " + JSON.stringify(param);
}
sf.write(logline + "\n");
print(logline);*/
}
log("Start");
var blockInit = false;
var gatt; var gatt;
var status; var currentRetryTimeout;
var initialRetryTime = 40;
var maxRetryTime = 60000;
var retryTime = initialRetryTime;
var origIsHRMOn = Bangle.isHRMOn; var origIsHRMOn = Bangle.isHRMOn;
Bangle.isBTHRMOn = function(){ Bangle.isBTHRMOn = function(){
return (status=="searching" || status=="connecting") || (gatt!==undefined); return (gatt!==undefined && gatt.connected);
} };
Bangle.isHRMOn = function() { Bangle.isHRMOn = function() {
var settings = require('Storage').readJSON("bthrm.json", true) || {}; var settings = require('Storage').readJSON("bthrm.json", true) || {};
@ -18,43 +33,42 @@
return Bangle.isBTHRMOn(); return Bangle.isBTHRMOn();
} }
return origIsHRMOn() || Bangle.isBTHRMOn(); return origIsHRMOn() || Bangle.isBTHRMOn();
};
var serviceFilters = [{
services: [
"180d"
]
}];
function retry(){
log("Retry with time " + retryTime);
if (currentRetryTimeout){
log("Clearing timeout " + currentRetryTimeout);
clearTimeout(currentRetryTimeout);
currentRetryTimeout = undefined;
} }
Bangle.setBTHRMPower = function(isOn, app) { var clampedTime = retryTime < 200 ? 200 : initialRetryTime;
currentRetryTimeout = setTimeout(() => {
log("Set timeout for retry as " + clampedTime);
initBt();
}, clampedTime);
retryTime = Math.pow(retryTime, 1.1);
if (retryTime > maxRetryTime){
retryTime = maxRetryTime;
}
}
function onDisconnect(reason) {
log("Disconnect: " + reason);
log("Gatt: ", gatt);
retry();
}
function onCharacteristic(event) {
var settings = require('Storage').readJSON("bthrm.json", true) || {}; var settings = require('Storage').readJSON("bthrm.json", true) || {};
// Do app power handling
if (!app) app="?";
log("setBTHRMPower ->", isOn, app);
if (Bangle._PWR===undefined) Bangle._PWR={};
if (Bangle._PWR.BTHRM===undefined) Bangle._PWR.BTHRM=[];
if (isOn && !Bangle._PWR.BTHRM.includes(app)) Bangle._PWR.BTHRM.push(app);
if (!isOn && Bangle._PWR.BTHRM.includes(app)) Bangle._PWR.BTHRM = Bangle._PWR.BTHRM.filter(a=>a!=app);
isOn = Bangle._PWR.BTHRM.length;
// so now we know if we're really on
if (isOn) {
log("setBTHRMPower on", app);
if (!Bangle.isBTHRMOn()) {
log("BTHRM not already on");
status = "searching";
NRF.requestDevice({ filters: [{ services: ['180D'] }] }).then(function(device) {
log("Found device "+device.id);
status = "connecting";
device.on('gattserverdisconnected', function(reason) {
gatt = undefined;
});
return device.gatt.connect();
}).then(function(g) {
log("Connected");
gatt = g;
return gatt.getPrimaryService(0x180D);
}).then(function(service) {
return service.getCharacteristic(0x2A37);
}).then(function(characteristic) {
log("Got characteristic");
characteristic.on('characteristicvaluechanged', function(event) {
var dv = event.target.value; var dv = event.target.value;
var flags = dv.getUint8(0); var flags = dv.getUint8(0);
// 0 = 8 or 16 bit // 0 = 8 or 16 bit
@ -70,28 +84,104 @@
Bangle.emit(settings.replace ? "HRM" : "BTHRM", { Bangle.emit(settings.replace ? "HRM" : "BTHRM", {
bpm: bpm, bpm: bpm,
confidence:100, confidence: bpm == 0 ? 0 : 100,
src: settings.replace ? "bthrm" : undefined src: settings.replace ? "bthrm" : undefined
}); });
}); }
return characteristic.startNotifications();
}).then(function() { var reUseCounter=0;
log("Ready");
status = "ok"; function initBt() {
}).catch(function(err) { log("initBt with blockInit: " + blockInit);
log("Error",err); if (blockInit){
retry();
return;
}
blockInit = true;
var connectionPromise;
if (reUseCounter > 3){
log("Reuse counter to high")
if (gatt.connected == true){
try {
log("Force disconnect with gatt: ", gatt);
gatt.disconnect();
} catch(e) {
log("Error during force disconnect", e);
}
}
gatt=undefined; gatt=undefined;
status = "error"; reUseCounter = 0;
}
if (!gatt){
var requestPromise = NRF.requestDevice({ filters: serviceFilters });
connectionPromise = requestPromise.then(function(device) {
gatt = device.gatt;
log("Gatt after request:", gatt);
gatt.device.on('gattserverdisconnected', onDisconnect);
}); });
} else {
reUseCounter++;
log("Reusing gatt:", gatt);
connectionPromise = gatt.connect();
}
var servicePromise = connectionPromise.then(function() {
return gatt.getPrimaryService(0x180d);
});
var characteristicPromise = servicePromise.then(function(service) {
log("Got service:", service);
return service.getCharacteristic(0x2A37);
});
var notificationPromise = characteristicPromise.then(function(c) {
log("Got characteristic:", c);
c.on('characteristicvaluechanged', onCharacteristic);
return c.startNotifications();
});
notificationPromise.then(()=>{
log("Wait for notifications");
retryTime = initialRetryTime;
blockInit=false;
});
notificationPromise.catch((e) => {
log("Error:", e);
blockInit = false;
retry();
});
}
Bangle.setBTHRMPower = function(isOn, app) {
var settings = require('Storage').readJSON("bthrm.json", true) || {};
// Do app power handling
if (!app) app="?";
if (Bangle._PWR===undefined) Bangle._PWR={};
if (Bangle._PWR.BTHRM===undefined) Bangle._PWR.BTHRM=[];
if (isOn && !Bangle._PWR.BTHRM.includes(app)) Bangle._PWR.BTHRM.push(app);
if (!isOn && Bangle._PWR.BTHRM.includes(app)) Bangle._PWR.BTHRM = Bangle._PWR.BTHRM.filter(a=>a!=app);
isOn = Bangle._PWR.BTHRM.length;
// so now we know if we're really on
if (isOn) {
if (!Bangle.isBTHRMOn()) {
initBt();
} }
} else { // not on } else { // not on
log("setBTHRMPower off", app); log("Power off for " + app);
if (gatt) { if (gatt) {
log("BTHRM connected - disconnecting"); try {
status = undefined; log("Disconnect with gatt: ", gatt);
try {gatt.disconnect();}catch(e) { gatt.disconnect();
log("BTHRM disconnect error", e); } catch(e) {
log("Error during disconnect", e);
} }
blockInit = false;
gatt = undefined; gatt = undefined;
} }
} }
@ -100,20 +190,25 @@
var origSetHRMPower = Bangle.setHRMPower; var origSetHRMPower = Bangle.setHRMPower;
Bangle.setHRMPower = function(isOn, app) { Bangle.setHRMPower = function(isOn, app) {
log("setHRMPower for " + app + ":" + (isOn?"on":"off"));
var settings = require('Storage').readJSON("bthrm.json", true) || {}; var settings = require('Storage').readJSON("bthrm.json", true) || {};
if (settings.enabled || !isOn){ if (settings.enabled || !isOn){
log("Enable BTHRM power");
Bangle.setBTHRMPower(isOn, app); Bangle.setBTHRMPower(isOn, app);
} }
if ((settings.enabled && !settings.replace) || !settings.enabled || !isOn){ if ((settings.enabled && !settings.replace) || !settings.enabled || !isOn){
log("Enable HRM power");
origSetHRMPower(isOn, app); origSetHRMPower(isOn, app);
} }
} }
var settings = require('Storage').readJSON("bthrm.json", true) || {}; var settings = require('Storage').readJSON("bthrm.json", true) || {};
if (settings.enabled && settings.replace){ if (settings.enabled && settings.replace){
log("Replace HRM event");
if (!(Bangle._PWR===undefined) && !(Bangle._PWR.HRM===undefined)){ if (!(Bangle._PWR===undefined) && !(Bangle._PWR.HRM===undefined)){
for (var i = 0; i < Bangle._PWR.HRM.length; i++){ for (var i = 0; i < Bangle._PWR.HRM.length; i++){
var app = Bangle._PWR.HRM[i]; var app = Bangle._PWR.HRM[i];
log("Moving app " + app);
origSetHRMPower(0, app); origSetHRMPower(0, app);
Bangle.setBTHRMPower(1, app); Bangle.setBTHRMPower(1, app);
if (Bangle._PWR.HRM===undefined) break; if (Bangle._PWR.HRM===undefined) break;

View File

@ -10,7 +10,9 @@ function draw(y, event, type, counter) {
g.reset(); g.reset();
g.setFontAlign(0,0); g.setFontAlign(0,0);
g.clearRect(0,y,g.getWidth(),y+75); g.clearRect(0,y,g.getWidth(),y+75);
if (type == null || event == null || counter == 0) return; if (type == null || event == null || counter == 0){
return;
}
var str = event.bpm + ""; var str = event.bpm + "";
g.setFontVector(40).drawString(str,px,y+20); g.setFontVector(40).drawString(str,px,y+20);
str = "Confidence: " + event.confidence; str = "Confidence: " + event.confidence;
@ -21,21 +23,27 @@ function draw(y, event, type, counter) {
} }
function onBtHrm(e) { function onBtHrm(e) {
print("Event for BT " + JSON.stringify(e)); //print("Event for BT " + JSON.stringify(e));
counterBt += 5; if (e.bpm == 0){
Bangle.buzz(100,0.2);
}
if (counterBt == 0){
Bangle.buzz(200,0.5);
}
counterBt += 3;
eventBt = e; eventBt = e;
} }
function onHrm(e) { function onHrm(e) {
print("Event for Int " + JSON.stringify(e)); //print("Event for Int " + JSON.stringify(e));
counterInt += 5; counterInt += 3;
eventInt = e; eventInt = e;
} }
Bangle.on('BTHRM', onBtHrm); Bangle.on('BTHRM', onBtHrm);
Bangle.on('HRM', onHrm); Bangle.on('HRM', onHrm);
Bangle.setHRMPower(1,'bthrm') Bangle.setHRMPower(1,'bthrm');
g.clear(); g.clear();
Bangle.loadWidgets(); Bangle.loadWidgets();
@ -47,13 +55,13 @@ g.drawString("Please wait...",g.getWidth()/2,g.getHeight()/2 - 16);
function drawInt(){ function drawInt(){
counterInt--; counterInt--;
if (counterInt < 0) counterInt = 0; if (counterInt < 0) counterInt = 0;
if (counterInt > 5) counterInt = 5; if (counterInt > 3) counterInt = 3;
draw(24, eventInt, "HRM", counterInt); draw(24, eventInt, "HRM", counterInt);
} }
function drawBt(){ function drawBt(){
counterBt--; counterBt--;
if (counterBt < 0) counterBt = 0; if (counterBt < 0) counterBt = 0;
if (counterBt > 5) counterBt = 5; if (counterBt > 3) counterBt = 3;
draw(100, eventBt, "BTHRM", counterBt); draw(100, eventBt, "BTHRM", counterBt);
} }

View File

@ -2,7 +2,7 @@
"id": "bthrm", "id": "bthrm",
"name": "Bluetooth Heart Rate Monitor", "name": "Bluetooth Heart Rate Monitor",
"shortName": "BT HRM", "shortName": "BT HRM",
"version": "0.03", "version": "0.04",
"description": "Overrides Bangle.js's build in heart rate monitor with an external Bluetooth one.", "description": "Overrides Bangle.js's build in heart rate monitor with an external Bluetooth one.",
"icon": "app.png", "icon": "app.png",
"type": "app", "type": "app",