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
|
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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in New Issue