mirror of https://github.com/espruino/BangleApps
Merge pull request #726 from hughbarney/master
kicthen combo: improved stopwatch display, rewrite compass arrow to use direct screen write to avoid mem defragpull/729/head
commit
cf007f6919
|
@ -3030,7 +3030,7 @@
|
|||
"name": "Stepometer Clock",
|
||||
"icon": "stepo.png",
|
||||
"version":"0.03",
|
||||
"description": "A large font watch, displays step count in a doughnut guage and warns of low battery",
|
||||
"description": "A large font watch, displays step count in a doughnut guage and warns of low battery, requires one of the steps widgets to be installed",
|
||||
"tags": "clock",
|
||||
"type":"clock",
|
||||
"readme": "README.md",
|
||||
|
@ -3084,7 +3084,7 @@
|
|||
{ "id": "kitchen",
|
||||
"name": "Kitchen Combo",
|
||||
"icon": "kitchen.png",
|
||||
"version":"0.04",
|
||||
"version":"0.06",
|
||||
"description": "Combination of the stepo, walkersclock, arrow and waypointer apps into a multiclock format. 'Everything but the kitchen sink'. Requires firmware v2.08.167 or later",
|
||||
"tags": "tool,outdoors,gps",
|
||||
"type":"clock",
|
||||
|
|
|
@ -2,3 +2,5 @@
|
|||
0.02: compass disable BTN1,BTN2 while waiting for GPS to reach RUNNING status
|
||||
0.03: Don't buzz for GPS fix in Quiet Mode
|
||||
0.04: Added stopwatch face
|
||||
0.05: Stopwatch, hide hours if 0, fixed flicker when stopped, updated README issues
|
||||
0.06: Reduced memory footprint of compass, used direct screen access rather than arrayBuffer
|
||||
|
|
|
@ -202,9 +202,27 @@ the correct direction heading or is not stable with respect to tilt
|
|||
and roll - redo the calibration by pressing *BTN3*. Calibration data
|
||||
is recorded in a storage file named `magnav.json`.
|
||||
|
||||
|
||||
### Technical Notes on Memory Management
|
||||
|
||||
v0.06: The stepo watch face uses an ArrayBuffer to setup the doughnut
|
||||
gauge before it is displayed. This is necessary as the drawing of
|
||||
the doughnut shape is quite slow. However I found that when an
|
||||
ArrayBuffer was also used for the compass arrow part of the App it
|
||||
could result in LOW_MEMORY errors due to the memory fragmentation
|
||||
caused when switching multiple times between the watch faces. It is
|
||||
possible to call Bangle.defrag() when switching to a new watch face
|
||||
but this causes an annoying delay in the time it takes to switch. So
|
||||
I have settled on directly writing to the screen using the Graphics
|
||||
object (g.) for the compass App. This creates a bit of flicker when
|
||||
the arrow moves but is more reliable than using the ArrayBuffer.
|
||||
|
||||
|
||||
### Issues
|
||||
|
||||
* GPS time display shows GMT and not BST, needs localising
|
||||
* Occassional buzzing after 2-3 days of use, seems to disappear after
|
||||
a reset to the launcher menu. Needs investigation
|
||||
* Stopwatch display to hide hours count if elapsed time is less than 1 hour.
|
||||
* Need to gracefully handle incorrect firmware
|
||||
* Need to gracefully handle missing compass calibration data
|
||||
* Need to gracefully handle missing steps widget
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
(() => {
|
||||
function getFace(){
|
||||
var intervalRefSec;
|
||||
var pal_by;
|
||||
var pal_bw;
|
||||
var pal_bb;
|
||||
var buf1;
|
||||
var buf2;
|
||||
var bearing;
|
||||
var heading;
|
||||
var oldHeading;
|
||||
|
@ -24,17 +19,11 @@
|
|||
function init(gps,sw) {
|
||||
showMem("compass init() START");
|
||||
gpsObject = gps;
|
||||
pal_by = new Uint16Array([0x0000,0xFFC0],0,1); // black, yellow
|
||||
pal_bw = new Uint16Array([0x0000,0xffff],0,1); // black, white
|
||||
pal_bb = new Uint16Array([0x0000,0x07ff],0,1); // black, blue
|
||||
buf1 = Graphics.createArrayBuffer(128,128,1,{msb:true});
|
||||
buf2 = Graphics.createArrayBuffer(80,40,1,{msb:true});
|
||||
|
||||
intervalRefSec = undefined;
|
||||
bearing = 0; // always point north if GPS is off
|
||||
heading = 0;
|
||||
oldHeading = 0;
|
||||
previous = {bs:"-", dst:"-", wp_name:"-", course:999};
|
||||
previous = {hding:"-", bs:"-", dst:"-", wp_name:"-", course:999};
|
||||
loc = require("locale");
|
||||
CALIBDATA = require("Storage").readJSON("magnav.json",1)||null;
|
||||
getWaypoint();
|
||||
|
@ -52,14 +41,8 @@
|
|||
function freeResources() {
|
||||
showMem("compass freeResources() START");
|
||||
gpsObject = undefined;
|
||||
pal_by = undefined;
|
||||
pal_bw = undefined;
|
||||
pal_bb = undefined;
|
||||
buf1 = undefined;
|
||||
buf2 = undefined;
|
||||
intervalRefSec = undefined;
|
||||
previous = undefined;
|
||||
|
||||
bearing = 0;
|
||||
heading = 0;
|
||||
oldHeading = 0;
|
||||
|
@ -70,21 +53,6 @@
|
|||
showMem("compass freeResources() END");
|
||||
}
|
||||
|
||||
function flip1(x,y) {
|
||||
g.drawImage({width:128,height:128,bpp:1,buffer:buf1.buffer, palette:pal_by},x ,y);
|
||||
buf1.clear();
|
||||
}
|
||||
|
||||
function flip2_bw(x,y) {
|
||||
g.drawImage({width:80,height:40,bpp:1,buffer:buf2.buffer, palette:pal_bw},x ,y);
|
||||
buf2.clear();
|
||||
}
|
||||
|
||||
function flip2_bb(x,y) {
|
||||
g.drawImage({width:80,height:40,bpp:1,buffer:buf2.buffer, palette:pal_bb},x ,y);
|
||||
buf2.clear();
|
||||
}
|
||||
|
||||
function startTimer() {
|
||||
log_debug("startTimer()");
|
||||
if (!Bangle.isCompassOn()) Bangle.setCompassPower(1);
|
||||
|
@ -143,29 +111,34 @@
|
|||
wp = gpsObject.getCurrentWaypoint();
|
||||
wp_distance = gpsObject.getWPdistance();
|
||||
wp_bearing = gpsObject.getWPbearing();
|
||||
|
||||
if (gpsObject.getState() === gpsObject.GPS_RUNNING)
|
||||
bearing = wp_bearing;
|
||||
else
|
||||
bearing = 0;
|
||||
|
||||
log_debug(wp);
|
||||
log_debug("wp_distance:" + wp_distance);
|
||||
log_debug("wp_bearing:" + wp_bearing);
|
||||
}
|
||||
|
||||
// takes 32ms
|
||||
function drawCompass(hd) {
|
||||
if (Math.abs(hd - oldHeading) < 2) return 0;
|
||||
hd=hd*Math.PI/180;
|
||||
// takes 16-20ms, will be called twice
|
||||
function drawCompass(angle, col) {
|
||||
angle = angle * Math.PI/180;
|
||||
var p = [0, 1.1071, Math.PI/4, 2.8198, 3.4633, 7*Math.PI/4 , 5.1760];
|
||||
|
||||
var poly = [
|
||||
64+60*Math.sin(hd+p[0]), 64-60*Math.cos(hd+p[0]),
|
||||
64+44.7214*Math.sin(hd+p[1]), 64-44.7214*Math.cos(hd+p[1]),
|
||||
64+28.2843*Math.sin(hd+p[2]), 64-28.2843*Math.cos(hd+p[2]),
|
||||
64+63.2455*Math.sin(hd+p[3]), 64-63.2455*Math.cos(hd+p[3]),
|
||||
64+63.2455*Math.sin(hd+p[4]), 64-63.2455*Math.cos(hd+p[4]),
|
||||
64+28.2843*Math.sin(hd+p[5]), 64-28.2843*Math.cos(hd+p[5]),
|
||||
64+44.7214*Math.sin(hd+p[6]), 64-44.7214*Math.cos(hd+p[6])
|
||||
120+60*Math.sin(angle+p[0]), 120-60*Math.cos(angle+p[0]),
|
||||
120+44.7214*Math.sin(angle+p[1]), 120-44.7214*Math.cos(angle+p[1]),
|
||||
120+28.2843*Math.sin(angle+p[2]), 120-28.2843*Math.cos(angle+p[2]),
|
||||
120+63.2455*Math.sin(angle+p[3]), 120-63.2455*Math.cos(angle+p[3]),
|
||||
120+63.2455*Math.sin(angle+p[4]), 120-63.2455*Math.cos(angle+p[4]),
|
||||
120+28.2843*Math.sin(angle+p[5]), 120-28.2843*Math.cos(angle+p[5]),
|
||||
120+44.7214*Math.sin(angle+p[6]), 120-44.7214*Math.cos(angle+p[6])
|
||||
];
|
||||
|
||||
buf1.fillPoly(poly);
|
||||
flip1(56, 56);
|
||||
g.setColor(col);
|
||||
g.fillPoly(poly);
|
||||
}
|
||||
|
||||
// stops violent compass swings and wobbles, takes 3ms
|
||||
|
@ -205,15 +178,20 @@
|
|||
//log_debug("draw()");
|
||||
var d = tiltfixread(CALIBDATA.offset,CALIBDATA.scale);
|
||||
heading = newHeading(d,heading);
|
||||
// sets bearing to waypoint bearing if GPS on else sets to 0 (north)
|
||||
getWaypoint();
|
||||
|
||||
getWaypoint();
|
||||
|
||||
// make the compass point in the direction we need to go
|
||||
var dir = bearing - heading;
|
||||
if (dir < 0) dir += 360;
|
||||
if (dir > 360) dir -= 360;
|
||||
var t = drawCompass(dir); // we want compass to show us where to go
|
||||
oldHeading = dir;
|
||||
|
||||
if (dir !== oldHeading) {
|
||||
drawCompass(oldHeading, 0);
|
||||
drawCompass(dir, 0xFFC0); // yellow
|
||||
oldHeading = dir;
|
||||
}
|
||||
|
||||
if (gpsObject.getState() === gpsObject.GPS_RUNNING) {
|
||||
drawGPSData();
|
||||
} else {
|
||||
|
@ -221,22 +199,25 @@
|
|||
}
|
||||
}
|
||||
|
||||
// only used when acting as compass with GPS off
|
||||
function drawCompassHeading() {
|
||||
//log_debug("drawCompassHeading()");
|
||||
// draw the heading
|
||||
buf2.setColor(1);
|
||||
buf2.setFontAlign(-1,-1);
|
||||
buf2.setFont("Vector",38);
|
||||
var hding = Math.round(heading);
|
||||
var hd = hding.toString();
|
||||
hd = hding < 10 ? "00"+hd : hding < 100 ? "0"+hd : hd;
|
||||
buf2.drawString(hd,0,0);
|
||||
flip2_bw(90, 200);
|
||||
|
||||
if (previous.hding !== hd) {
|
||||
previous.hding = hd;
|
||||
g.setColor(1,1,1);
|
||||
g.setFontAlign(-1,-1);
|
||||
g.setFont("Vector",38);
|
||||
g.clearRect(80, 200, 159, 239);
|
||||
g.drawString(hd, 80, 200);
|
||||
}
|
||||
}
|
||||
|
||||
function drawGPSData() {
|
||||
log_debug("drawGPSData()");
|
||||
buf2.setFont("Vector",24);
|
||||
var bs = wp_bearing.toString();
|
||||
bs = wp_bearing<10?"00"+bs : wp_bearing<100 ?"0"+bs : bs;
|
||||
var dst = loc.distance(wp_distance);
|
||||
|
@ -249,45 +230,45 @@
|
|||
// show distance on the left
|
||||
if (previous.dst !== dst) {
|
||||
previous.dst = dst
|
||||
buf2.setColor(1);
|
||||
buf2.setFontAlign(-1,-1);
|
||||
buf2.setFont("Vector", 20);
|
||||
if (gpsObject.waypointHasLocation()) {
|
||||
buf2.drawString(dst,0,0);
|
||||
flip2_bb(0, 200);
|
||||
} else {
|
||||
buf2.drawString(" ",0,0);
|
||||
flip2_bw(0, 200);
|
||||
}
|
||||
g.setFontAlign(-1,-1); // left, bottom
|
||||
g.setFont("Vector", 20);
|
||||
g.clearRect(0, 200, 79, 239);
|
||||
|
||||
if (gpsObject.waypointHasLocation())
|
||||
g.setColor(0x07ff);
|
||||
else
|
||||
g.setColor(1,1,1);
|
||||
g.drawString(dst, 0, 200);
|
||||
}
|
||||
|
||||
// bearing, place in middle at bottom of compass
|
||||
if (previous.bs !== bs) {
|
||||
previous.bs = bs;
|
||||
buf2.setColor(1);
|
||||
buf2.setFontAlign(0, -1);
|
||||
buf2.setFont("Vector",38);
|
||||
buf2.drawString(bs,40,0);
|
||||
flip2_bw(80, 200);
|
||||
g.setColor(1,1,1);
|
||||
g.setFontAlign(0,-1); // middle, bottom
|
||||
g.setFont("Vector",38);
|
||||
g.clearRect(80, 200, 159, 239);
|
||||
g.drawString(bs, 119, 200);
|
||||
}
|
||||
|
||||
// waypoint name on right
|
||||
if (previous.wp_name !== wp.name) {
|
||||
buf2.setColor(1);
|
||||
buf2.setFontAlign(1,-1); // right, bottom
|
||||
buf2.setFont("Vector", 20);
|
||||
buf2.drawString(wp.name, 80, 0);
|
||||
g.setFontAlign(1,-1); // right, bottom
|
||||
g.setFont("Vector", 20);
|
||||
g.clearRect(160, 200, 239, 239);
|
||||
|
||||
if (gpsObject.waypointHasLocation())
|
||||
flip2_bb(160, 200);
|
||||
g.setColor(0x07ff);
|
||||
else
|
||||
flip2_bw(160, 200);
|
||||
g.setColor(1,1,1);
|
||||
g.drawString(wp.name, 239, 200);
|
||||
}
|
||||
}
|
||||
|
||||
// clear the attributes that control the display refresh
|
||||
function resetPrevious() {
|
||||
log_debug("resetPrevious()");
|
||||
previous = {bs:"-", dst:"-", wp_name:"-", course:999};
|
||||
previous = {hding:"-", bs:"-", dst:"-", wp_name:"-", course:999};
|
||||
}
|
||||
|
||||
return {init:init, freeResources:freeResources, startTimer:startTimer, stopTimer:stopTimer,
|
||||
|
|
|
@ -488,6 +488,7 @@ function STOPWATCH() {
|
|||
this.displayInterval;
|
||||
this.redrawButtons = true;
|
||||
this.redrawLaps = true;
|
||||
this.redrawTime = true;
|
||||
}
|
||||
|
||||
STOPWATCH.prototype.log_debug = function(o) {
|
||||
|
@ -498,8 +499,13 @@ STOPWATCH.prototype.timeToText = function(t) {
|
|||
let hrs = Math.floor(t/3600000);
|
||||
let mins = Math.floor(t/60000)%60;
|
||||
let secs = Math.floor(t/1000)%60;
|
||||
let text;
|
||||
|
||||
if (hrs === 0)
|
||||
text = ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2);
|
||||
else
|
||||
text = (""+hrs) + ":" + ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2);
|
||||
|
||||
let text = ("00"+hrs).substr(-3) + ":" + ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2);
|
||||
this.log_debug(text);
|
||||
return text;
|
||||
}
|
||||
|
@ -520,7 +526,7 @@ STOPWATCH.prototype.stopStart = function() {
|
|||
this.tCurrent = Date.now();
|
||||
this.redrawButtons = true;
|
||||
this.redrawLaps = true;
|
||||
|
||||
this.redrawTime = true;
|
||||
this.draw();
|
||||
}
|
||||
|
||||
|
@ -529,7 +535,7 @@ STOPWATCH.prototype.lap = function() {
|
|||
if (this.running) {
|
||||
this.tCurrent = Date.now();
|
||||
this.lapTimes.unshift(this.tCurrent - this.tStart);
|
||||
console.log(this.tCurrent - this.tStart);
|
||||
log_debug(this.tCurrent - this.tStart);
|
||||
}
|
||||
|
||||
this.tStart = this.tCurrent;
|
||||
|
@ -552,6 +558,7 @@ STOPWATCH.prototype.reset = function() {
|
|||
STOPWATCH.prototype.lapOrReset = function() {
|
||||
this.redrawButtons = true;
|
||||
this.redrawLaptimes = true;
|
||||
this.redrawTime = true;
|
||||
|
||||
this.log_debug("lapReset()");
|
||||
if (this.running)
|
||||
|
@ -566,7 +573,7 @@ STOPWATCH.prototype.draw = function() {
|
|||
|
||||
g.setColor(1,1,1);
|
||||
if (this.redrawButtons) this.drawButtons();
|
||||
this.drawTime();
|
||||
if (this.running || this.redrawTime) this.drawTime();
|
||||
if (this.redrawLaps) this.drawLaptimes();
|
||||
}
|
||||
|
||||
|
@ -597,33 +604,34 @@ STOPWATCH.prototype.drawLaptimes = function() {
|
|||
|
||||
STOPWATCH.prototype.drawTime = function() {
|
||||
this.log_debug("drawTime()");
|
||||
let t = this.tCurrent - this.tStart;
|
||||
let Tt = this.tCurrent - this.tTotal;
|
||||
|
||||
let txt = this.timeToText(t);
|
||||
let Ttxt = this.timeToText(Tt);
|
||||
|
||||
let x = 100;
|
||||
let Tx = 125;
|
||||
let tLap = this.tCurrent - this.tStart;
|
||||
let tTotal = this.tCurrent - this.tTotal;
|
||||
let txtLap = this.timeToText(tLap);
|
||||
let txtTotal = this.timeToText(tTotal);
|
||||
let xTotal = 100;
|
||||
let xLap = 125;
|
||||
|
||||
// total time
|
||||
g.setFont("Vector",38);
|
||||
g.setFontAlign(0,0);
|
||||
g.clearRect(0,this.timeY-21,200,this.timeY+21);
|
||||
g.clearRect(0, this.timeY-21, 200, this.timeY+21);
|
||||
g.setColor(0xFFC0);
|
||||
g.drawString(Ttxt,x,this.timeY);
|
||||
g.drawString(txtTotal, xTotal, this.timeY);
|
||||
|
||||
// current lap time
|
||||
g.setFont("Vector", 20);
|
||||
g.clearRect(0, this.TtimeY-7,200, this.TtimeY+7);
|
||||
g.clearRect(0, this.TtimeY-7, 200, this.TtimeY+7);
|
||||
g.setColor(1,1,1);
|
||||
g.drawString(txt,Tx, this.TtimeY);
|
||||
g.drawString(txtLap, xLap, this.TtimeY);
|
||||
|
||||
this.redrawTime = false;
|
||||
}
|
||||
|
||||
STOPWATCH.prototype.startTimer = function() {
|
||||
this.log_debug("startTimer()");
|
||||
this.redrawButtons = true;
|
||||
this.redrawLaps = true;
|
||||
this.redrawTime = true;
|
||||
this.draw();
|
||||
this.displayInterval = setInterval(stopwatchDraw, 1000);
|
||||
}
|
||||
|
|
|
@ -2,36 +2,18 @@
|
|||
function getFace(){
|
||||
let swObject = undefined;
|
||||
|
||||
function log_debug(o) {
|
||||
//console.log(o);
|
||||
}
|
||||
|
||||
function init(gps, sw) {
|
||||
showMem("swatch init 1");
|
||||
swObject = sw;
|
||||
g.clear();
|
||||
showMem("swatch init 2");
|
||||
}
|
||||
|
||||
function freeResources() {
|
||||
showMem("swatch free 1");
|
||||
swObject = undefined;
|
||||
showMem("swatch free 2");
|
||||
}
|
||||
|
||||
function showMem(msg) {
|
||||
var val = process.memory();
|
||||
var str = msg + " " + Math.round(val.usage*100/val.total) + "%";
|
||||
log_debug(str);
|
||||
}
|
||||
function freeResources() {}
|
||||
|
||||
function startTimer() {
|
||||
log_debug("swObject.startTimer()");
|
||||
swObject.startTimer();
|
||||
}
|
||||
|
||||
function stopTimer() {
|
||||
log_debug("swObject.stopTimer()");
|
||||
swObject.stopTimer();
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,9 @@ A large font watch, displays step count in a doughnut guage and warns of low bat
|
|||
- The guage show percentage of steps out of a goal of 10000 steps
|
||||
|
||||
|
||||
## Dependancies
|
||||
- Requires one of the steps widgets to be installed
|
||||
|
||||
|
||||

|
||||
|
||||
|
|
Loading…
Reference in New Issue