mirror of https://github.com/espruino/BangleApps
bthrm - Better stability and auto reconnect
parent
e997ad59ed
commit
26226ffc9c
|
@ -5,3 +5,5 @@
|
|||
0.03: Prevent readings from internal sensor mixing into BT values
|
||||
Mark events with src property
|
||||
Show actual source of event in app
|
||||
0.04: Automatically reconnect BT sensor
|
||||
App buzzes if no BTHRM events for more than 3 seconds
|
||||
|
|
|
@ -1,13 +1,28 @@
|
|||
(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 status;
|
||||
var currentRetryTimeout;
|
||||
var initialRetryTime = 40;
|
||||
var maxRetryTime = 60000;
|
||||
var retryTime = initialRetryTime;
|
||||
|
||||
var origIsHRMOn = Bangle.isHRMOn;
|
||||
|
||||
Bangle.isBTHRMOn = function(){
|
||||
return (status=="searching" || status=="connecting") || (gatt!==undefined);
|
||||
}
|
||||
return (gatt!==undefined && gatt.connected);
|
||||
};
|
||||
|
||||
Bangle.isHRMOn = function() {
|
||||
var settings = require('Storage').readJSON("bthrm.json", true) || {};
|
||||
|
@ -18,16 +33,135 @@
|
|||
return 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;
|
||||
}
|
||||
|
||||
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 dv = event.target.value;
|
||||
var flags = dv.getUint8(0);
|
||||
// 0 = 8 or 16 bit
|
||||
// 1,2 = sensor contact
|
||||
// 3 = energy expended shown
|
||||
// 4 = RR interval
|
||||
var bpm = (flags & 1) ? (dv.getUint16(1) / 100 /* ? */ ) : dv.getUint8(1); // 8 or 16 bit
|
||||
/* var idx = 2 + (flags&1); // index of next field
|
||||
if (flags&8) idx += 2; // energy expended
|
||||
if (flags&16) {
|
||||
var interval = dv.getUint16(idx,1); // in milliseconds
|
||||
}*/
|
||||
|
||||
Bangle.emit(settings.replace ? "HRM" : "BTHRM", {
|
||||
bpm: bpm,
|
||||
confidence: bpm == 0 ? 0 : 100,
|
||||
src: settings.replace ? "bthrm" : undefined
|
||||
});
|
||||
}
|
||||
|
||||
Bangle.setBTHRMPower = function(isOn, app) {
|
||||
|
||||
var reUseCounter=0;
|
||||
|
||||
function initBt() {
|
||||
log("initBt with blockInit: " + blockInit);
|
||||
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;
|
||||
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="?";
|
||||
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);
|
||||
|
@ -35,63 +169,19 @@
|
|||
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 flags = dv.getUint8(0);
|
||||
// 0 = 8 or 16 bit
|
||||
// 1,2 = sensor contact
|
||||
// 3 = energy expended shown
|
||||
// 4 = RR interval
|
||||
var bpm = (flags&1) ? (dv.getUint16(1)/100/* ? */) : dv.getUint8(1); // 8 or 16 bit
|
||||
/* var idx = 2 + (flags&1); // index of next field
|
||||
if (flags&8) idx += 2; // energy expended
|
||||
if (flags&16) {
|
||||
var interval = dv.getUint16(idx,1); // in milliseconds
|
||||
}*/
|
||||
|
||||
Bangle.emit(settings.replace?"HRM":"BTHRM", {
|
||||
bpm:bpm,
|
||||
confidence:100,
|
||||
src:settings.replace?"bthrm":undefined
|
||||
});
|
||||
});
|
||||
return characteristic.startNotifications();
|
||||
}).then(function() {
|
||||
log("Ready");
|
||||
status = "ok";
|
||||
}).catch(function(err) {
|
||||
log("Error",err);
|
||||
gatt = undefined;
|
||||
status = "error";
|
||||
});
|
||||
initBt();
|
||||
}
|
||||
} else { // not on
|
||||
log("setBTHRMPower off", app);
|
||||
log("Power off for " + app);
|
||||
if (gatt) {
|
||||
log("BTHRM connected - disconnecting");
|
||||
status = undefined;
|
||||
try {gatt.disconnect();}catch(e) {
|
||||
log("BTHRM disconnect error", e);
|
||||
try {
|
||||
log("Disconnect with gatt: ", gatt);
|
||||
gatt.disconnect();
|
||||
} catch(e) {
|
||||
log("Error during disconnect", e);
|
||||
}
|
||||
blockInit = false;
|
||||
gatt = undefined;
|
||||
}
|
||||
}
|
||||
|
@ -100,24 +190,29 @@
|
|||
var origSetHRMPower = Bangle.setHRMPower;
|
||||
|
||||
Bangle.setHRMPower = function(isOn, app) {
|
||||
log("setHRMPower for " + app + ":" + (isOn?"on":"off"));
|
||||
var settings = require('Storage').readJSON("bthrm.json", true) || {};
|
||||
if (settings.enabled || !isOn){
|
||||
log("Enable BTHRM power");
|
||||
Bangle.setBTHRMPower(isOn, app);
|
||||
}
|
||||
if ((settings.enabled && !settings.replace) || !settings.enabled || !isOn){
|
||||
log("Enable HRM power");
|
||||
origSetHRMPower(isOn, app);
|
||||
}
|
||||
}
|
||||
|
||||
var settings = require('Storage').readJSON("bthrm.json", true) || {};
|
||||
if (settings.enabled && settings.replace){
|
||||
if (!(Bangle._PWR===undefined) && !(Bangle._PWR.HRM===undefined)){
|
||||
for (var i = 0; i < Bangle._PWR.HRM.length; i++){
|
||||
var app = Bangle._PWR.HRM[i];
|
||||
origSetHRMPower(0, app);
|
||||
Bangle.setBTHRMPower(1, app);
|
||||
if (Bangle._PWR.HRM===undefined) break;
|
||||
log("Replace HRM event");
|
||||
if (!(Bangle._PWR===undefined) && !(Bangle._PWR.HRM===undefined)){
|
||||
for (var i = 0; i < Bangle._PWR.HRM.length; i++){
|
||||
var app = Bangle._PWR.HRM[i];
|
||||
log("Moving app " + app);
|
||||
origSetHRMPower(0, app);
|
||||
Bangle.setBTHRMPower(1, app);
|
||||
if (Bangle._PWR.HRM===undefined) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
|
|
@ -10,7 +10,9 @@ function draw(y, event, type, counter) {
|
|||
g.reset();
|
||||
g.setFontAlign(0,0);
|
||||
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 + "";
|
||||
g.setFontVector(40).drawString(str,px,y+20);
|
||||
str = "Confidence: " + event.confidence;
|
||||
|
@ -21,21 +23,27 @@ function draw(y, event, type, counter) {
|
|||
}
|
||||
|
||||
function onBtHrm(e) {
|
||||
print("Event for BT " + JSON.stringify(e));
|
||||
counterBt += 5;
|
||||
//print("Event for BT " + JSON.stringify(e));
|
||||
if (e.bpm == 0){
|
||||
Bangle.buzz(100,0.2);
|
||||
}
|
||||
if (counterBt == 0){
|
||||
Bangle.buzz(200,0.5);
|
||||
}
|
||||
counterBt += 3;
|
||||
eventBt = e;
|
||||
}
|
||||
|
||||
function onHrm(e) {
|
||||
print("Event for Int " + JSON.stringify(e));
|
||||
counterInt += 5;
|
||||
//print("Event for Int " + JSON.stringify(e));
|
||||
counterInt += 3;
|
||||
eventInt = e;
|
||||
}
|
||||
|
||||
Bangle.on('BTHRM', onBtHrm);
|
||||
Bangle.on('HRM', onHrm);
|
||||
|
||||
Bangle.setHRMPower(1,'bthrm')
|
||||
Bangle.setHRMPower(1,'bthrm');
|
||||
|
||||
g.clear();
|
||||
Bangle.loadWidgets();
|
||||
|
@ -47,13 +55,13 @@ g.drawString("Please wait...",g.getWidth()/2,g.getHeight()/2 - 16);
|
|||
function drawInt(){
|
||||
counterInt--;
|
||||
if (counterInt < 0) counterInt = 0;
|
||||
if (counterInt > 5) counterInt = 5;
|
||||
if (counterInt > 3) counterInt = 3;
|
||||
draw(24, eventInt, "HRM", counterInt);
|
||||
}
|
||||
function drawBt(){
|
||||
counterBt--;
|
||||
if (counterBt < 0) counterBt = 0;
|
||||
if (counterBt > 5) counterBt = 5;
|
||||
if (counterBt > 3) counterBt = 3;
|
||||
draw(100, eventBt, "BTHRM", counterBt);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "bthrm",
|
||||
"name": "Bluetooth Heart Rate Monitor",
|
||||
"shortName": "BT HRM",
|
||||
"version": "0.03",
|
||||
"version": "0.04",
|
||||
"description": "Overrides Bangle.js's build in heart rate monitor with an external Bluetooth one.",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
|
|
Loading…
Reference in New Issue