From ac652282ca54552f74ffcc0dd060041e3460c7c3 Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Wed, 9 Mar 2022 10:22:23 +0100 Subject: [PATCH 1/3] cscsensor: Test enable on Bangle2 --- apps/cscsensor/ChangeLog | 1 + apps/cscsensor/metadata.json | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/cscsensor/ChangeLog b/apps/cscsensor/ChangeLog index 8f23fa9f3..a98be5c0f 100644 --- a/apps/cscsensor/ChangeLog +++ b/apps/cscsensor/ChangeLog @@ -5,3 +5,4 @@ 0.05: Add cadence sensor support 0.06: Now read wheel rev as well as cadence sensor Improve connection code +0.07: Make Bangle.js 2 compatible diff --git a/apps/cscsensor/metadata.json b/apps/cscsensor/metadata.json index af338c59e..4006789ef 100644 --- a/apps/cscsensor/metadata.json +++ b/apps/cscsensor/metadata.json @@ -2,11 +2,11 @@ "id": "cscsensor", "name": "Cycling speed sensor", "shortName": "CSCSensor", - "version": "0.06", + "version": "0.07", "description": "Read BLE enabled cycling speed and cadence sensor and display readings on watch", "icon": "icons8-cycling-48.png", "tags": "outdoors,exercise,ble,bluetooth", - "supports": ["BANGLEJS"], + "supports": ["BANGLEJS", "BANGLEJS2"], "readme": "README.md", "storage": [ {"name":"cscsensor.app.js","url":"cscsensor.app.js"}, From 10892718cdf3192d3deb7abd5b755f072ec23ae4 Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Wed, 9 Mar 2022 22:53:31 +0100 Subject: [PATCH 2/3] cscsensor Fix button mapping --- apps/cscsensor/README.md | 6 +++--- apps/cscsensor/cscsensor.app.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/cscsensor/README.md b/apps/cscsensor/README.md index 9740fd9cf..3828e8e3e 100644 --- a/apps/cscsensor/README.md +++ b/apps/cscsensor/README.md @@ -11,9 +11,9 @@ Currently the app displays the following data: - total distance traveled - an icon with the battery status of the remote sensor -Button 1 resets all measurements except total distance traveled. The latter gets preserved by being written to storage every 0.1 miles and upon exiting the app. -If the watch app has not received an update from the sensor for at least 10 seconds, pushing button 3 will attempt to reconnect to the sensor. -Button 2 switches between the display for cycling speed and cadence. +Button 1 (swipe up on Bangle.js 2) resets all measurements except total distance traveled. The latter gets preserved by being written to storage every 0.1 miles and upon exiting the app. +If the watch app has not received an update from the sensor for at least 10 seconds, pushing button 3 (swipe down on Bangle.js 2) will attempt to reconnect to the sensor. +Button 2 (tap on Bangle.js 2) switches between the display for cycling speed and cadence. Values displayed are imperial or metric (depending on locale), cadence is in RPM, the wheel circumference can be adjusted in the global settings app. diff --git a/apps/cscsensor/cscsensor.app.js b/apps/cscsensor/cscsensor.app.js index e2af0db16..14403dd8d 100644 --- a/apps/cscsensor/cscsensor.app.js +++ b/apps/cscsensor/cscsensor.app.js @@ -254,8 +254,8 @@ E.on('kill',()=>{ NRF.on('disconnect', connection_setup); // restart if disconnected Bangle.setUI("updown", d=>{ if (d<0) { mySensor.reset(); g.clearRect(0, 48, W, H); mySensor.updateScreen(); } - if (d==0) { if (Date.now()-mySensor.lastBangleTime>10000) connection_setup(); } - if (d>0) { mySensor.toggleDisplayCadence(); g.clearRect(0, 48, W, H); mySensor.updateScreen(); } + else if (d>0) { if (Date.now()-mySensor.lastBangleTime>10000) connection_setup(); } + else { mySensor.toggleDisplayCadence(); g.clearRect(0, 48, W, H); mySensor.updateScreen(); } }); Bangle.loadWidgets(); From 7335289abfc7a333dcc6b6e960812915a94055a9 Mon Sep 17 00:00:00 2001 From: Erik Andresen Date: Sat, 12 Mar 2022 07:10:48 +0100 Subject: [PATCH 3/3] cscsensor Scale layout on BangleJS2 --- apps/cscsensor/cscsensor.app.js | 155 +++++++++++++++++--------------- 1 file changed, 81 insertions(+), 74 deletions(-) diff --git a/apps/cscsensor/cscsensor.app.js b/apps/cscsensor/cscsensor.app.js index 14403dd8d..4ebe7d57e 100644 --- a/apps/cscsensor/cscsensor.app.js +++ b/apps/cscsensor/cscsensor.app.js @@ -7,6 +7,11 @@ const SETTINGS_FILE = 'cscsensor.json'; const storage = require('Storage'); const W = g.getWidth(); const H = g.getHeight(); +const yStart = 48; +const rowHeight = (H-yStart)/6; +const yCol1 = W/2.7586; +const fontSizeLabel = W/12.632; +const fontSizeValue = W/9.2308; class CSCSensor { constructor() { @@ -22,7 +27,6 @@ class CSCSensor { this.speed = 0; this.maxSpeed = 0; this.lastSpeed = 0; - this.qUpdateScreen = true; this.lastRevsStart = -1; this.qMetric = !require("locale").speed(1).toString().endsWith("mph"); this.speedUnit = this.qMetric ? "km/h" : "mph"; @@ -49,6 +53,7 @@ class CSCSensor { toggleDisplayCadence() { this.showCadence = !this.showCadence; this.screenInit = true; + g.setBgColor(0, 0, 0); } setBatteryLevel(level) { @@ -63,14 +68,16 @@ class CSCSensor { } drawBatteryIcon() { - g.setColor(1, 1, 1).drawRect(10, 55, 20, 75).fillRect(14, 53, 16, 55).setColor(0).fillRect(11, 56, 19, 74); + 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); 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); - g.fillRect(11, 74-18*this.batteryLevel/100, 19, 74); + g.fillRect(11*W/240, (yStart+0.10833*H)-18*this.batteryLevel/100, 19*W/240, yStart+0.10833*H); } - else g.setFontVector(14).setFontAlign(0, 0, 0).setColor(0xffff).drawString("?", 16, 66); + else g.setFontVector(W/17.143).setFontAlign(0, 0, 0).setColor(0xffff).drawString("?", 16*W/240, yStart+0.075*H); } updateScreenRevs() { @@ -88,36 +95,36 @@ class CSCSensor { for (var i=0; i<6; ++i) { if ((i&1)==0) g.setColor(0, 0, 0); else g.setColor(0x30cd); - g.fillRect(0, 48+i*32, 86, 48+(i+1)*32); + g.fillRect(0, yStart+i*rowHeight, yCol1-1, yStart+(i+1)*rowHeight); if ((i&1)==1) g.setColor(0); else g.setColor(0x30cd); - g.fillRect(87, 48+i*32, 239, 48+(i+1)*32); - g.setColor(0.5, 0.5, 0.5).drawRect(87, 48+i*32, 239, 48+(i+1)*32).drawLine(0, 239, 239, 239);//.drawRect(0, 48, 87, 239); - g.moveTo(0, 80).lineTo(30, 80).lineTo(30, 48).lineTo(87, 48).lineTo(87, 239).lineTo(0, 239).lineTo(0, 80); + 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); } - g.setFontAlign(1, 0, 0).setFontVector(19).setColor(1, 1, 0); - g.drawString("Time:", 87, 66); - g.drawString("Speed:", 87, 98); - g.drawString("Ave spd:", 87, 130); - g.drawString("Max spd:", 87, 162); - g.drawString("Trip:", 87, 194); - g.drawString("Total:", 87, 226); + 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); this.drawBatteryIcon(); this.screenInit = false; } - g.setFontAlign(-1, 0, 0).setFontVector(26); - g.setColor(0x30cd).fillRect(88, 49, 238, 79); - g.setColor(0xffff).drawString(dmins+":"+dsecs, 92, 66); - g.setColor(0).fillRect(88, 81, 238, 111); - g.setColor(0xffff).drawString(dspeed+" "+this.speedUnit, 92, 98); - g.setColor(0x30cd).fillRect(88, 113, 238, 143); - g.setColor(0xffff).drawString(avespeed + " " + this.speedUnit, 92, 130); - g.setColor(0).fillRect(88, 145, 238, 175); - g.setColor(0xffff).drawString(maxspeed + " " + this.speedUnit, 92, 162); - g.setColor(0x30cd).fillRect(88, 177, 238, 207); - g.setColor(0xffff).drawString(ddist + " " + this.distUnit, 92, 194); - g.setColor(0).fillRect(88, 209, 238, 238); - g.setColor(0xffff).drawString(tdist + " " + this.distUnit, 92, 226); + 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); } updateScreenCadence() { @@ -125,21 +132,21 @@ class CSCSensor { for (var i=0; i<2; ++i) { if ((i&1)==0) g.setColor(0, 0, 0); else g.setColor(0x30cd); - g.fillRect(0, 48+i*32, 86, 48+(i+1)*32); + g.fillRect(0, yStart+i*rowHeight, yCol1-1, yStart+(i+1)*rowHeight); if ((i&1)==1) g.setColor(0); else g.setColor(0x30cd); - g.fillRect(87, 48+i*32, 239, 48+(i+1)*32); - g.setColor(0.5, 0.5, 0.5).drawRect(87, 48+i*32, 239, 48+(i+1)*32).drawLine(0, 239, 239, 239);//.drawRect(0, 48, 87, 239); - g.moveTo(0, 80).lineTo(30, 80).lineTo(30, 48).lineTo(87, 48).lineTo(87, 239).lineTo(0, 239).lineTo(0, 80); + 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); } - g.setFontAlign(1, 0, 0).setFontVector(19).setColor(1, 1, 0); - g.drawString("Cadence:", 87, 98); + g.setFontAlign(1, 0, 0).setFontVector(fontSizeLabel).setColor(1, 1, 0); + g.drawString("Cadence:", yCol1, yStart+rowHeight/2+1*rowHeight); this.drawBatteryIcon(); this.screenInit = false; } - g.setFontAlign(-1, 0, 0).setFontVector(26); - g.setColor(0).fillRect(88, 81, 238, 111); - g.setColor(0xffff).drawString(Math.round(this.cadence), 92, 98); + 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); } updateScreen() { @@ -163,45 +170,45 @@ class CSCSensor { } this.lastCrankRevs = crankRevs; this.lastCrankTime = crankTime; - } - // 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); + } 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); + } } - } - 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 { - this.speedFailed++; - qChanged = false; - if (this.speedFailed>3) { - this.speed = 0; - qChanged = (this.lastSpeed>0); + 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); + } } + this.lastSpeed = this.speed; + if (this.speed>this.maxSpeed && (this.movingTime>3 || this.speed<20) && this.speed<50) this.maxSpeed = this.speed; } - this.lastSpeed = this.speed; - if (this.speed>this.maxSpeed && (this.movingTime>3 || this.speed<20) && this.speed<50) this.maxSpeed = this.speed; } - if (qChanged && this.qUpdateScreen) this.updateScreen(); + if (qChanged) this.updateScreen(); } } @@ -253,9 +260,9 @@ E.on('kill',()=>{ }); NRF.on('disconnect', connection_setup); // restart if disconnected Bangle.setUI("updown", d=>{ - if (d<0) { mySensor.reset(); g.clearRect(0, 48, W, H); mySensor.updateScreen(); } + if (d<0) { mySensor.reset(); g.clearRect(0, yStart, W, H); mySensor.updateScreen(); } else if (d>0) { if (Date.now()-mySensor.lastBangleTime>10000) connection_setup(); } - else { mySensor.toggleDisplayCadence(); g.clearRect(0, 48, W, H); mySensor.updateScreen(); } + else { mySensor.toggleDisplayCadence(); g.clearRect(0, yStart, W, H); mySensor.updateScreen(); } }); Bangle.loadWidgets();