2020-09-08 21:35:08 +00:00
|
|
|
var device;
|
|
|
|
var gatt;
|
|
|
|
var service;
|
|
|
|
var characteristic;
|
|
|
|
|
2020-09-09 19:43:59 +00:00
|
|
|
const SETTINGS_FILE = 'cscsensor.json';
|
2020-09-10 20:45:11 +00:00
|
|
|
const storage = require('Storage');
|
2021-11-26 10:04:02 +00:00
|
|
|
const W = g.getWidth();
|
|
|
|
const H = g.getHeight();
|
2022-03-12 06:10:48 +00:00
|
|
|
const yStart = 48;
|
|
|
|
const rowHeight = (H-yStart)/6;
|
|
|
|
const yCol1 = W/2.7586;
|
|
|
|
const fontSizeLabel = W/12.632;
|
|
|
|
const fontSizeValue = W/9.2308;
|
2020-09-09 19:43:59 +00:00
|
|
|
|
2020-09-08 21:35:08 +00:00
|
|
|
class CSCSensor {
|
|
|
|
constructor() {
|
2020-09-09 02:16:02 +00:00
|
|
|
this.movingTime = 0;
|
2020-09-08 21:35:08 +00:00
|
|
|
this.lastTime = 0;
|
|
|
|
this.lastBangleTime = Date.now();
|
|
|
|
this.lastRevs = -1;
|
2020-09-10 20:45:11 +00:00
|
|
|
this.settings = storage.readJSON(SETTINGS_FILE, 1) || {};
|
|
|
|
this.settings.totaldist = this.settings.totaldist || 0;
|
|
|
|
this.totaldist = this.settings.totaldist;
|
2020-09-15 21:48:41 +00:00
|
|
|
this.wheelCirc = (this.settings.wheelcirc || 2230)/25.4;
|
2020-09-08 21:35:08 +00:00
|
|
|
this.speedFailed = 0;
|
|
|
|
this.speed = 0;
|
2020-09-09 02:16:02 +00:00
|
|
|
this.maxSpeed = 0;
|
2020-09-08 21:35:08 +00:00
|
|
|
this.lastSpeed = 0;
|
2020-09-09 14:52:47 +00:00
|
|
|
this.lastRevsStart = -1;
|
2020-09-09 18:20:58 +00:00
|
|
|
this.qMetric = !require("locale").speed(1).toString().endsWith("mph");
|
|
|
|
this.speedUnit = this.qMetric ? "km/h" : "mph";
|
|
|
|
this.distUnit = this.qMetric ? "km" : "mi";
|
|
|
|
this.distFactor = this.qMetric ? 1.609344 : 1;
|
2020-09-16 15:17:37 +00:00
|
|
|
this.screenInit = true;
|
2020-09-15 21:48:41 +00:00
|
|
|
this.batteryLevel = -1;
|
2021-09-23 06:27:19 +00:00
|
|
|
this.lastCrankTime = 0;
|
|
|
|
this.lastCrankRevs = 0;
|
|
|
|
this.showCadence = false;
|
|
|
|
this.cadence = 0;
|
2020-09-09 14:52:47 +00:00
|
|
|
}
|
2020-09-16 15:17:37 +00:00
|
|
|
|
2020-09-09 14:52:47 +00:00
|
|
|
reset() {
|
2020-09-15 19:07:44 +00:00
|
|
|
this.settings.totaldist = this.totaldist;
|
|
|
|
storage.writeJSON(SETTINGS_FILE, this.settings);
|
2020-09-09 14:52:47 +00:00
|
|
|
this.maxSpeed = 0;
|
|
|
|
this.movingTime = 0;
|
|
|
|
this.lastRevsStart = this.lastRevs;
|
|
|
|
this.maxSpeed = 0;
|
2020-09-16 15:17:37 +00:00
|
|
|
this.screenInit = true;
|
2020-09-08 21:35:08 +00:00
|
|
|
}
|
2020-09-16 15:17:37 +00:00
|
|
|
|
2021-09-23 06:27:19 +00:00
|
|
|
toggleDisplayCadence() {
|
|
|
|
this.showCadence = !this.showCadence;
|
|
|
|
this.screenInit = true;
|
2022-03-12 06:10:48 +00:00
|
|
|
g.setBgColor(0, 0, 0);
|
2021-09-23 06:27:19 +00:00
|
|
|
}
|
|
|
|
|
2020-09-15 21:48:41 +00:00
|
|
|
setBatteryLevel(level) {
|
2020-09-16 15:17:37 +00:00
|
|
|
if (level!=this.batteryLevel) {
|
|
|
|
this.batteryLevel = level;
|
|
|
|
this.drawBatteryIcon();
|
|
|
|
}
|
2020-09-15 21:48:41 +00:00
|
|
|
}
|
2020-09-16 15:17:37 +00:00
|
|
|
|
|
|
|
updateBatteryLevel(event) {
|
|
|
|
if (event.target.uuid == "0x2a19") this.setBatteryLevel(event.target.value.getUint8(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
drawBatteryIcon() {
|
2022-03-12 06:10:48 +00:00
|
|
|
g.setColor(1, 1, 1).drawRect(10*W/240, yStart+0.029167*H, 20*W/240, yStart+0.1125*H)
|
|
|
|
.fillRect(14*W/240, yStart+0.020833*H, 16*W/240, yStart+0.029167*H)
|
|
|
|
.setColor(0).fillRect(11*W/240, yStart+0.033333*H, 19*W/240, yStart+0.10833*H);
|
2020-09-16 15:17:37 +00:00
|
|
|
if (this.batteryLevel!=-1) {
|
|
|
|
if (this.batteryLevel<25) g.setColor(1, 0, 0);
|
|
|
|
else if (this.batteryLevel<50) g.setColor(1, 0.5, 0);
|
|
|
|
else g.setColor(0, 1, 0);
|
2022-03-12 06:10:48 +00:00
|
|
|
g.fillRect(11*W/240, (yStart+0.10833*H)-18*this.batteryLevel/100, 19*W/240, yStart+0.10833*H);
|
2020-09-16 15:17:37 +00:00
|
|
|
}
|
2022-03-12 06:10:48 +00:00
|
|
|
else g.setFontVector(W/17.143).setFontAlign(0, 0, 0).setColor(0xffff).drawString("?", 16*W/240, yStart+0.075*H);
|
2020-09-16 15:17:37 +00:00
|
|
|
}
|
|
|
|
|
2021-09-23 06:27:19 +00:00
|
|
|
updateScreenRevs() {
|
2020-09-09 19:43:59 +00:00
|
|
|
var dist = this.distFactor*(this.lastRevs-this.lastRevsStart)*this.wheelCirc/63360.0;
|
2020-09-09 02:16:02 +00:00
|
|
|
var ddist = Math.round(100*dist)/100;
|
2020-09-10 20:45:11 +00:00
|
|
|
var tdist = Math.round(this.distFactor*this.totaldist*10)/10;
|
2021-11-26 10:04:02 +00:00
|
|
|
var dspeed = Math.round(10*this.distFactor*this.speed)/10;
|
2020-09-09 02:16:02 +00:00
|
|
|
var dmins = Math.floor(this.movingTime/60).toString();
|
|
|
|
if (dmins.length<2) dmins = "0"+dmins;
|
|
|
|
var dsecs = (Math.floor(this.movingTime) % 60).toString();
|
|
|
|
if (dsecs.length<2) dsecs = "0"+dsecs;
|
2020-09-23 15:31:31 +00:00
|
|
|
var avespeed = (this.movingTime>3 ? Math.round(10*dist/(this.movingTime/3600))/10 : 0);
|
2020-09-09 18:20:58 +00:00
|
|
|
var maxspeed = Math.round(10*this.distFactor*this.maxSpeed)/10;
|
2020-09-16 15:17:37 +00:00
|
|
|
if (this.screenInit) {
|
|
|
|
for (var i=0; i<6; ++i) {
|
|
|
|
if ((i&1)==0) g.setColor(0, 0, 0);
|
|
|
|
else g.setColor(0x30cd);
|
2022-03-12 06:10:48 +00:00
|
|
|
g.fillRect(0, yStart+i*rowHeight, yCol1-1, yStart+(i+1)*rowHeight);
|
2020-09-16 15:17:37 +00:00
|
|
|
if ((i&1)==1) g.setColor(0);
|
|
|
|
else g.setColor(0x30cd);
|
2022-03-12 06:10:48 +00:00
|
|
|
g.fillRect(yCol1, yStart+i*rowHeight, H-1, yStart+(i+1)*rowHeight);
|
|
|
|
g.setColor(0.5, 0.5, 0.5).drawRect(yCol1, yStart+i*rowHeight, H-1, yStart+(i+1)*rowHeight).drawLine(0, H-1, W-1, H-1);
|
|
|
|
g.moveTo(0, yStart+0.13333*H).lineTo(30*W/240, yStart+0.13333*H).lineTo(30*W/240, yStart).lineTo(yCol1, yStart).lineTo(yCol1, H-1).lineTo(0, H-1).lineTo(0, yStart+0.13333*H);
|
2020-09-16 15:17:37 +00:00
|
|
|
}
|
2022-03-12 06:10:48 +00:00
|
|
|
g.setFontAlign(1, 0, 0).setFontVector(fontSizeLabel).setColor(1, 1, 0);
|
|
|
|
g.drawString("Time:", yCol1, yStart+rowHeight/2+0*rowHeight);
|
|
|
|
g.drawString("Speed:", yCol1, yStart+rowHeight/2+1*rowHeight);
|
|
|
|
g.drawString("Avg spd:", yCol1, yStart+rowHeight/2+2*rowHeight);
|
|
|
|
g.drawString("Max spd:", yCol1, yStart+rowHeight/2+3*rowHeight);
|
|
|
|
g.drawString("Trip:", yCol1, yStart+rowHeight/2+4*rowHeight);
|
|
|
|
g.drawString("Total:", yCol1, yStart+rowHeight/2+5*rowHeight);
|
2020-09-16 15:17:37 +00:00
|
|
|
this.drawBatteryIcon();
|
|
|
|
this.screenInit = false;
|
2020-09-15 21:48:41 +00:00
|
|
|
}
|
2022-03-12 06:10:48 +00:00
|
|
|
g.setFontAlign(-1, 0, 0).setFontVector(fontSizeValue);
|
|
|
|
g.setColor(0x30cd).fillRect(yCol1+1, 49+rowHeight*0, 238, 47+1*rowHeight);
|
|
|
|
g.setColor(0xffff).drawString(dmins+":"+dsecs, yCol1+5, 50+rowHeight/2+0*rowHeight);
|
|
|
|
g.setColor(0).fillRect(yCol1+1, 49+rowHeight*1, 238, 47+2*rowHeight);
|
|
|
|
g.setColor(0xffff).drawString(dspeed+" "+this.speedUnit, yCol1+5, 50+rowHeight/2+1*rowHeight);
|
|
|
|
g.setColor(0x30cd).fillRect(yCol1+1, 49+rowHeight*2, 238, 47+3*rowHeight);
|
|
|
|
g.setColor(0xffff).drawString(avespeed + " " + this.speedUnit, yCol1+5, 50+rowHeight/2+2*rowHeight);
|
|
|
|
g.setColor(0).fillRect(yCol1+1, 49+rowHeight*3, 238, 47+4*rowHeight);
|
|
|
|
g.setColor(0xffff).drawString(maxspeed + " " + this.speedUnit, yCol1+5, 50+rowHeight/2+3*rowHeight);
|
|
|
|
g.setColor(0x30cd).fillRect(yCol1+1, 49+rowHeight*4, 238, 47+5*rowHeight);
|
|
|
|
g.setColor(0xffff).drawString(ddist + " " + this.distUnit, yCol1+5, 50+rowHeight/2+4*rowHeight);
|
|
|
|
g.setColor(0).fillRect(yCol1+1, 49+rowHeight*5, 238, 47+6*rowHeight);
|
|
|
|
g.setColor(0xffff).drawString(tdist + " " + this.distUnit, yCol1+5, 50+rowHeight/2+5*rowHeight);
|
2020-09-08 21:35:08 +00:00
|
|
|
}
|
2021-09-23 06:27:19 +00:00
|
|
|
|
|
|
|
updateScreenCadence() {
|
|
|
|
if (this.screenInit) {
|
|
|
|
for (var i=0; i<2; ++i) {
|
|
|
|
if ((i&1)==0) g.setColor(0, 0, 0);
|
|
|
|
else g.setColor(0x30cd);
|
2022-03-12 06:10:48 +00:00
|
|
|
g.fillRect(0, yStart+i*rowHeight, yCol1-1, yStart+(i+1)*rowHeight);
|
2021-09-23 06:27:19 +00:00
|
|
|
if ((i&1)==1) g.setColor(0);
|
|
|
|
else g.setColor(0x30cd);
|
2022-03-12 06:10:48 +00:00
|
|
|
g.fillRect(yCol1, yStart+i*rowHeight, H-1, yStart+(i+1)*rowHeight);
|
|
|
|
g.setColor(0.5, 0.5, 0.5).drawRect(yCol1, yStart+i*rowHeight, H-1, yStart+(i+1)*rowHeight).drawLine(0, H-1, W-1, H-1);
|
|
|
|
g.moveTo(0, yStart+0.13333*H).lineTo(30*W/240, yStart+0.13333*H).lineTo(30*W/240, yStart).lineTo(yCol1, yStart).lineTo(yCol1, H-1).lineTo(0, H-1).lineTo(0, yStart+0.13333*H);
|
2021-09-23 06:27:19 +00:00
|
|
|
}
|
2022-03-12 06:10:48 +00:00
|
|
|
g.setFontAlign(1, 0, 0).setFontVector(fontSizeLabel).setColor(1, 1, 0);
|
|
|
|
g.drawString("Cadence:", yCol1, yStart+rowHeight/2+1*rowHeight);
|
2021-09-23 06:27:19 +00:00
|
|
|
this.drawBatteryIcon();
|
|
|
|
this.screenInit = false;
|
|
|
|
}
|
2022-03-12 06:10:48 +00:00
|
|
|
g.setFontAlign(-1, 0, 0).setFontVector(fontSizeValue);
|
|
|
|
g.setColor(0).fillRect(yCol1+1, 49+rowHeight*1, 238, 47+2*rowHeight);
|
|
|
|
g.setColor(0xffff).drawString(Math.round(this.cadence), yCol1+5, 50+rowHeight/2+1*rowHeight);
|
2021-09-23 06:27:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
updateScreen() {
|
|
|
|
if (!this.showCadence) {
|
|
|
|
this.updateScreenRevs();
|
|
|
|
} else {
|
|
|
|
this.updateScreenCadence();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-08 21:35:08 +00:00
|
|
|
updateSensor(event) {
|
|
|
|
var qChanged = false;
|
|
|
|
if (event.target.uuid == "0x2a5b") {
|
2021-09-23 06:27:19 +00:00
|
|
|
if (event.target.value.getUint8(0, true) & 0x2) {
|
2021-11-26 10:04:02 +00:00
|
|
|
// crank revolution - if enabled
|
2021-09-23 06:27:19 +00:00
|
|
|
const crankRevs = event.target.value.getUint16(1, true);
|
|
|
|
const crankTime = event.target.value.getUint16(3, true);
|
|
|
|
if (crankTime > this.lastCrankTime) {
|
2021-09-25 10:45:42 +00:00
|
|
|
this.cadence = (crankRevs-this.lastCrankRevs)/(crankTime-this.lastCrankTime)*(60*1024);
|
2021-09-23 06:27:19 +00:00
|
|
|
qChanged = true;
|
2020-09-10 20:45:11 +00:00
|
|
|
}
|
2021-09-23 06:27:19 +00:00
|
|
|
this.lastCrankRevs = crankRevs;
|
|
|
|
this.lastCrankTime = crankTime;
|
2022-03-12 06:10:48 +00:00
|
|
|
} else {
|
|
|
|
// wheel revolution
|
|
|
|
var wheelRevs = event.target.value.getUint32(1, true);
|
|
|
|
var dRevs = (this.lastRevs>0 ? wheelRevs-this.lastRevs : 0);
|
|
|
|
if (dRevs>0) {
|
|
|
|
qChanged = true;
|
|
|
|
this.totaldist += dRevs*this.wheelCirc/63360.0;
|
|
|
|
if ((this.totaldist-this.settings.totaldist)>0.1) {
|
|
|
|
this.settings.totaldist = this.totaldist;
|
|
|
|
storage.writeJSON(SETTINGS_FILE, this.settings);
|
|
|
|
}
|
2021-09-23 06:27:19 +00:00
|
|
|
}
|
2022-03-12 06:10:48 +00:00
|
|
|
this.lastRevs = wheelRevs;
|
|
|
|
if (this.lastRevsStart<0) this.lastRevsStart = wheelRevs;
|
|
|
|
var wheelTime = event.target.value.getUint16(5, true);
|
|
|
|
var dT = (wheelTime-this.lastTime)/1024;
|
|
|
|
var dBT = (Date.now()-this.lastBangleTime)/1000;
|
|
|
|
this.lastBangleTime = Date.now();
|
|
|
|
if (dT<0) dT+=64;
|
|
|
|
if (Math.abs(dT-dBT)>3) dT = dBT;
|
|
|
|
this.lastTime = wheelTime;
|
|
|
|
this.speed = this.lastSpeed;
|
|
|
|
if (dRevs>0 && dT>0) {
|
|
|
|
this.speed = (dRevs*this.wheelCirc/63360.0)*3600/dT;
|
|
|
|
this.speedFailed = 0;
|
|
|
|
this.movingTime += dT;
|
|
|
|
} else if (!this.showCadence) {
|
|
|
|
this.speedFailed++;
|
|
|
|
qChanged = false;
|
|
|
|
if (this.speedFailed>3) {
|
|
|
|
this.speed = 0;
|
|
|
|
qChanged = (this.lastSpeed>0);
|
|
|
|
}
|
2020-09-08 21:35:08 +00:00
|
|
|
}
|
2022-03-12 06:10:48 +00:00
|
|
|
this.lastSpeed = this.speed;
|
|
|
|
if (this.speed>this.maxSpeed && (this.movingTime>3 || this.speed<20) && this.speed<50) this.maxSpeed = this.speed;
|
2020-09-08 21:35:08 +00:00
|
|
|
}
|
|
|
|
}
|
2022-03-12 06:10:48 +00:00
|
|
|
if (qChanged) this.updateScreen();
|
2020-09-08 21:35:08 +00:00
|
|
|
}
|
2020-09-09 15:05:41 +00:00
|
|
|
}
|
2020-09-08 21:35:08 +00:00
|
|
|
|
|
|
|
var mySensor = new CSCSensor();
|
|
|
|
|
2020-09-15 21:48:41 +00:00
|
|
|
function getSensorBatteryLevel(gatt) {
|
|
|
|
gatt.getPrimaryService("180f").then(function(s) {
|
|
|
|
return s.getCharacteristic("2a19");
|
|
|
|
}).then(function(c) {
|
2020-09-16 15:17:37 +00:00
|
|
|
c.on('characteristicvaluechanged', (event)=>mySensor.updateBatteryLevel(event));
|
|
|
|
return c.startNotifications();
|
2020-09-15 21:48:41 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-09-15 19:07:44 +00:00
|
|
|
function connection_setup() {
|
2020-09-28 17:46:53 +00:00
|
|
|
mySensor.screenInit = true;
|
2021-11-26 10:04:02 +00:00
|
|
|
E.showMessage("Scanning for CSC sensor...");
|
|
|
|
NRF.requestDevice({ filters: [{services:["1816"]}]}).then(function(d) {
|
|
|
|
device = d;
|
|
|
|
E.showMessage("Found device");
|
|
|
|
return device.gatt.connect();
|
|
|
|
}).then(function(ga) {
|
|
|
|
gatt = ga;
|
|
|
|
E.showMessage("Connected");
|
|
|
|
return gatt.getPrimaryService("1816");
|
|
|
|
}).then(function(s) {
|
|
|
|
service = s;
|
|
|
|
return service.getCharacteristic("2a5b");
|
|
|
|
}).then(function(c) {
|
|
|
|
characteristic = c;
|
|
|
|
characteristic.on('characteristicvaluechanged', (event)=>mySensor.updateSensor(event));
|
|
|
|
return characteristic.startNotifications();
|
|
|
|
}).then(function() {
|
|
|
|
console.log("Done!");
|
|
|
|
g.reset().clearRect(Bangle.appRect).flip();
|
|
|
|
getSensorBatteryLevel(gatt);
|
|
|
|
mySensor.updateScreen();
|
|
|
|
}).catch(function(e) {
|
|
|
|
E.showMessage(e.toString(), "ERROR");
|
|
|
|
console.log(e);
|
|
|
|
});
|
2020-09-15 19:07:44 +00:00
|
|
|
}
|
2020-09-08 21:35:08 +00:00
|
|
|
|
2020-09-15 19:07:44 +00:00
|
|
|
connection_setup();
|
2021-11-26 10:04:02 +00:00
|
|
|
E.on('kill',()=>{
|
|
|
|
if (gatt!=undefined) gatt.disconnect();
|
|
|
|
mySensor.settings.totaldist = mySensor.totaldist;
|
|
|
|
storage.writeJSON(SETTINGS_FILE, mySensor.settings);
|
|
|
|
});
|
|
|
|
NRF.on('disconnect', connection_setup); // restart if disconnected
|
|
|
|
Bangle.setUI("updown", d=>{
|
2022-03-12 06:10:48 +00:00
|
|
|
if (d<0) { mySensor.reset(); g.clearRect(0, yStart, W, H); mySensor.updateScreen(); }
|
2022-03-09 21:53:31 +00:00
|
|
|
else if (d>0) { if (Date.now()-mySensor.lastBangleTime>10000) connection_setup(); }
|
2022-03-12 06:10:48 +00:00
|
|
|
else { mySensor.toggleDisplayCadence(); g.clearRect(0, yStart, W, H); mySensor.updateScreen(); }
|
2021-11-26 10:04:02 +00:00
|
|
|
});
|
2020-09-09 02:16:02 +00:00
|
|
|
|
|
|
|
Bangle.loadWidgets();
|
2020-09-09 14:52:47 +00:00
|
|
|
Bangle.drawWidgets();
|