Merge pull request #2 from espruino/master

update my local
pull/672/head
dapgo 2021-02-17 17:24:25 +01:00 committed by GitHub
commit 071294eca4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 1634 additions and 627 deletions

View File

@ -80,7 +80,7 @@
"name": "Notifications (default)", "name": "Notifications (default)",
"shortName":"Notifications", "shortName":"Notifications",
"icon": "notify.png", "icon": "notify.png",
"version":"0.05", "version":"0.06",
"description": "A handler for displaying notifications that displays them in a bar at the top of the screen", "description": "A handler for displaying notifications that displays them in a bar at the top of the screen",
"tags": "widget", "tags": "widget",
"type": "notify", "type": "notify",
@ -93,7 +93,7 @@
"name": "Fullscreen Notifications", "name": "Fullscreen Notifications",
"shortName":"Notifications", "shortName":"Notifications",
"icon": "notify.png", "icon": "notify.png",
"version":"0.06", "version":"0.07",
"description": "A handler for displaying notifications that displays them fullscreen. This may not fully restore the screen after on some apps. See `Notifications (default)` for more information about the notifications library.", "description": "A handler for displaying notifications that displays them fullscreen. This may not fully restore the screen after on some apps. See `Notifications (default)` for more information about the notifications library.",
"tags": "widget", "tags": "widget",
"type": "notify", "type": "notify",
@ -139,7 +139,7 @@
{ "id": "gbridge", { "id": "gbridge",
"name": "Gadgetbridge", "name": "Gadgetbridge",
"icon": "app.png", "icon": "app.png",
"version":"0.18", "version":"0.20",
"description": "The default notification handler for Gadgetbridge notifications from Android", "description": "The default notification handler for Gadgetbridge notifications from Android",
"tags": "tool,system,android,widget", "tags": "tool,system,android,widget",
"readme": "README.md", "readme": "README.md",
@ -1492,7 +1492,7 @@
"name": "Digital Assistant, not EDITH", "name": "Digital Assistant, not EDITH",
"shortName": "DANE", "shortName": "DANE",
"icon": "app.png", "icon": "app.png",
"version": "0.15", "version": "0.16",
"description": "A Watchface inspired by Tony Stark's EDITH and based on https://arwes.dev/", "description": "A Watchface inspired by Tony Stark's EDITH and based on https://arwes.dev/",
"tags": "clock", "tags": "clock",
"type": "clock", "type": "clock",
@ -2139,7 +2139,7 @@
{ "id": "multiclock", { "id": "multiclock",
"name": "Multi Clock", "name": "Multi Clock",
"icon": "multiclock.png", "icon": "multiclock.png",
"version":"0.11", "version":"0.12",
"description": "Clock with multiple faces - Big, Analogue, Digital, Text, Time-Date.\n Switch between faces with BTN1 & BTN3", "description": "Clock with multiple faces - Big, Analogue, Digital, Text, Time-Date.\n Switch between faces with BTN1 & BTN3",
"readme": "README.md", "readme": "README.md",
"tags": "clock", "tags": "clock",
@ -2153,8 +2153,6 @@
{"name":"txt.face.js","url":"txt.js"}, {"name":"txt.face.js","url":"txt.js"},
{"name":"timdat.face.js","url":"timdat.js"}, {"name":"timdat.face.js","url":"timdat.js"},
{"name":"ped.face.js","url":"ped.js"}, {"name":"ped.face.js","url":"ped.js"},
{"name":"gps.face.js","url":"gps.js"},
{"name":"osref.face.js","url":"osref.js"},
{"name":"multiclock.img","url":"multiclock-icon.js","evaluate":true} {"name":"multiclock.img","url":"multiclock-icon.js","evaluate":true}
] ]
}, },
@ -2639,11 +2637,11 @@
] ]
}, },
{ "id": "speedalt", { "id": "speedalt",
"name": "GPS Speedo and Altimeter", "name": "GPS Adventure Sports",
"shortName":"GPS Speed Alt", "shortName":"GPS Adv Sport",
"icon": "app.png", "icon": "app.png",
"version":"0.07", "version":"1.00",
"description": "GPS speed and altitude display. Designed for easy viewing and use during outdoor activities such as para-gliding, hang-gliding, sailing, cycling etc.", "description": "GPS speed, altitude and distance to waypoint display. Designed for easy viewing and use during outdoor activities such as para-gliding, hang-gliding, sailing, cycling etc.",
"tags": "tool,outdoors", "tags": "tool,outdoors",
"type":"app", "type":"app",
"allow_emulator":true, "allow_emulator":true,
@ -2758,6 +2756,7 @@
"name": "Test User Input", "name": "Test User Input",
"shortName":"Test User Input", "shortName":"Test User Input",
"icon": "app.png", "icon": "app.png",
"readme": "README.md",
"version":"0.02", "version":"0.02",
"description": "Basic app to test the bangle.js input interface. It displays the result in text or a switch on/off image.", "description": "Basic app to test the bangle.js input interface. It displays the result in text or a switch on/off image.",
"tags": "input,interface,buttons,touch", "tags": "input,interface,buttons,touch",
@ -2765,6 +2764,59 @@
{"name":"testuserinput.app.js","url":"app.js"}, {"name":"testuserinput.app.js","url":"app.js"},
{"name":"testuserinput.img","url":"app-icon.js","evaluate":true} {"name":"testuserinput.img","url":"app-icon.js","evaluate":true}
] ]
},
{ "id": "gpssetup",
"name": "GPS Setup",
"shortName":"GPS Setup",
"icon": "gpssetup.png",
"version":"0.02",
"description": "Configure the GPS power options and store them in the GPS nvram",
"tags": "gps, tools, outdoors",
"readme": "README.md",
"storage": [
{"name":"gpssetup","url":"gpssetup.js"},
{"name":"gpssetup.settings.js","url":"settings.js"},
{"name":"gpssetup.settings.json","url":"settings.json"},
{"name":"gpssetup.app.js","url":"app.js"},
{"name":"gpssetup.img","url":"icon.js","evaluate":true}
]
},
{ "id": "walkersclock",
"name": "Walkers Clock",
"shortName":"Walkers Clock",
"icon": "walkersclock48.png",
"version":"0.01",
"description": "A larg font watch, displays steps, can switch GPS on/off, displays grid reference",
"type":"clock",
"tags": "clock, gps, tools, outdoors",
"readme": "README.md",
"storage": [
{"name":"walkersclock.app.js","url":"app.js"},
{"name":"walkersclock.img","url":"icon.js","evaluate":true}
]
},
{ "id": "widgps",
"name": "GPS Widget",
"icon": "widget.png",
"version":"0.01",
"description": "Tiny widget to show the power on/off status of the GPS. Require firmware v2.08.167 or later",
"tags": "widget,gps",
"type":"widget",
"readme": "README.md",
"storage": [
{"name":"widgps.wid.js","url":"widget.js"}
]
},
{ "id": "widhrt",
"name": "HRM Widget",
"icon": "widget.png",
"version":"0.01",
"description": "Tiny widget to show the power on/off status of the Heart Rate Monitor. Requires firmware v2.08.167 or later",
"tags": "widget, hrm",
"type":"widget",
"readme": "README.md",
"storage": [
{"name":"widhrt.wid.js","url":"widget.js"}
]
} }
] ]

View File

@ -11,3 +11,4 @@
0.13: Improve icon 0.13: Improve icon
0.14: Switch Icon back to 8bit web palette to fix it 0.14: Switch Icon back to 8bit web palette to fix it
0.15: Hotfix: Remove var declaration from app image 0.15: Hotfix: Remove var declaration from app image
0.16: Revert: Change Counter back to button control

View File

@ -67,7 +67,7 @@ function levelColor(l) {
function drawBattery() { function drawBattery() {
const l = E.getBattery(), c = levelColor(l); const l = E.getBattery(), c = levelColor(l);
count = l; // count = l;
const xl = 45 + l * (194 - 46) / 100; const xl = 45 + l * (194 - 46) / 100;
g.clearRect(46, 58 + 80 + yOffset + 37, 193, height - 5); g.clearRect(46, 58 + 80 + yOffset + 37, 193, height - 5);
g.setColor(c).fillRect(46, 58 + 80 + yOffset + 37, xl, height - 5); g.setColor(c).fillRect(46, 58 + 80 + yOffset + 37, xl, height - 5);
@ -124,14 +124,14 @@ drawClock();
setWatch(Bangle.showLauncher, BTN2, {repeat: false, edge: "falling"}); setWatch(Bangle.showLauncher, BTN2, {repeat: false, edge: "falling"});
// setWatch(function () { setWatch(function () {
// count++; count++;
// drawCounterText(); drawCounterText();
// }, BTN1, {repeat: true, edge: "falling"}); }, BTN1, {repeat: true, edge: "falling"});
// setWatch(function () { setWatch(function () {
// count--; count--;
// drawCounterText(); drawCounterText();
// }, BTN3, {repeat: true, edge: "falling"}); }, BTN3, {repeat: true, edge: "falling"});
// refesh every 100 milliseconds // refesh every 100 milliseconds
setInterval(updateClock, 500); setInterval(updateClock, 500);

View File

@ -18,3 +18,5 @@
Nicer display of alarm clock notifications Nicer display of alarm clock notifications
0.17: Modified music notification for updated 'notify' library 0.17: Modified music notification for updated 'notify' library
0.18: Added reporting of step count and HRM (new Gadgetbridges can now log this) 0.18: Added reporting of step count and HRM (new Gadgetbridges can now log this)
0.19: Support for call incoming/start/end
0.20: Reduce memory usage

View File

@ -2,13 +2,11 @@
// Music handling // Music handling
const state = { const state = {
music: "stop", music: "stop",
musicInfo: { musicInfo: {
artist: "", artist: "",
album: "", album: "",
track: "" track: ""
}, },
scrollPos: 0 scrollPos: 0
}; };
// activity reporting // activity reporting
@ -43,14 +41,6 @@
return event; return event;
} }
} }
function handleNotificationEvent(event) {
if (event.t === "notify") {
require("notify").show(prettifyNotificationEvent(event));
Bangle.buzz();
} else { // notify-
require("notify").hide(event);
}
}
function updateMusic(options){ function updateMusic(options){
if (state.music === "play") { if (state.music === "play") {
@ -117,37 +107,6 @@
require("notify").hide("music"); require("notify").hide("music");
} }
} }
function handleMusicStateUpdate(event) {
if (state.music !== event.state) {
state.music = event.state
updateMusic({on: true});
}
}
function handleMusicInfoUpdate(event) {
state.musicInfo = event;
updateMusic({on: false});
}
function handleCallEvent(event) {
if (event.cmd === "accept") {
require("notify").show({
size: 55, title: event.name, id: "call",
body: event.number, icon:require("heatshrink").decompress(atob("jEYwIMJj4CCwACJh4CCCIMOAQMGAQMHAQMDAQMBCIMB4PwgHz/EAn4CBj4CBg4CBgACCAAw="))});
Bangle.buzz();
}
}
function handleFindEvent(event) {
if (state.find) {
clearInterval(state.find);
delete state.find;
}
if (event.n)
state.find = setInterval(_=>{
Bangle.buzz();
setTimeout(_=>Bangle.beep(), 1000);
},2000);
}
function handleActivityEvent(event) { function handleActivityEvent(event) {
var s = settings(); var s = settings();
@ -193,19 +152,52 @@
switch (event.t) { switch (event.t) {
case "notify": case "notify":
case "notify-": case "notify-":
handleNotificationEvent(event); if (event.t === "notify") {
require("notify").show(prettifyNotificationEvent(event));
Bangle.buzz();
} else { // notify-
require("notify").hide(event);
}
break; break;
case "musicinfo": case "musicinfo":
handleMusicInfoUpdate(event); state.musicInfo = event;
updateMusic({on: false});
break; break;
case "musicstate": case "musicstate":
handleMusicStateUpdate(event); if (state.music !== event.state) {
state.music = event.state
updateMusic({on: true});
}
break; break;
case "call": case "call":
handleCallEvent(event); var note = { size: 55, title: event.name, id: "call",
body: event.number, icon:require("heatshrink").decompress(atob("jEYwIMJj4CCwACJh4CCCIMOAQMGAQMHAQMDAQMBCIMB4PwgHz/EAn4CBj4CBg4CBgACCAAw="))}
if (event.cmd === "incoming") {
require("notify").show(note);
Bangle.buzz();
} else if (event.cmd === "start") {
require("notify").show(Object.assign(note, {
bgColor : "#008000", titleBgColor : "#00C000",
body: "In progress: "+event.number}));
} else if (event.cmd === "end") {
require("notify").show(Object.assign(note, {
bgColor : "#800000", titleBgColor : "#C00000",
body: "Ended: "+event.number}));
setTimeout(function() {
require("notify").hide({ id: "call" });
}, 2000);
}
break; break;
case "find": case "find":
handleFindEvent(event); if (state.find) {
clearInterval(state.find);
delete state.find;
}
if (event.n)
state.find = setInterval(_=>{
Bangle.buzz();
setTimeout(_=>Bangle.beep(), 1000);
},2000);
break; break;
case "act": case "act":
handleActivityEvent(event); handleActivityEvent(event);

2
apps/gpssetup/ChangeLog Normal file
View File

@ -0,0 +1,2 @@
0.01: First version of GPS Setup app
0.02: Created gppsetup module

109
apps/gpssetup/README.md Normal file
View File

@ -0,0 +1,109 @@
# GPS Setup
An App and module to enable the GPS to be configured into low power mode.
## Goals
To develop an app that configures the GPS to run with the lowest
possible power consumption.
Example power consumption of the GPS while powered on:
* An app that turns on the GPS and constantly displays the screen
will use around 75mA, the battery will last between 3-4 hours.
* Using the GPS with Super-E Power Saving Mode (PSM) with the screen
off most of the time, will consume around 35mA and you might get
10hrs before a recharge.
* Using the GPS in Power Saving Mode On/Off (PSMOO) with suitable
settings can reduce the average consumption to around 15mA. A
simple test using a 120s update period, 6s search period was still
running with 45% battery 20 hours after it started.
## Settings App
The Settings App enables you set the options below. Either start the
App from the launcher or go to Settings, select App/Widgets and then
'GPS Setup'.
When you exit the setup app, the settings will be stored in the
gpssetup.settings.json file, the GPS will be switched on and the
necessary commands sent to the GPS to configure it. The GPS is then
powered off. The GPS configuration is stored in the GPS non-volatile
memory so that next time the GPS is powered, that configuration is
used. These settings will remain for all apps that use the GPS.
- Power Mode:
- **SuperE** - the factory default setup for the GPS. The recommended
power saving mode. If you need frequent (every second) updates on
position, then this is the mode for you.
- **PSMOO** - On/Off power saving mode. Configured by interval and
search time. Choose this mode if you are happy to get a GPS
position update less often (say every 1 or 2 minutes). The longer
the interval the more time the GPS will spend sleeping in low
power mode (7mA) between obtaining fixes (35mA). For walking in
open country an update once every 60 seconds is adequate to put
you within a 6 digit grid refernce sqaure.
- update - the time between two position fix attempts.
- search - the time between two acquisition attempts if the receiver
is unable to get a position fix.
## Module
A module is provided that'll allow you to set GPS configuration from your own
app. To use it:
```
// This will set up the GPS to current saved defaults. It's not normally
// needed unless the watch's battery has run down
require("gpssetup").setPowerMode();
// This sets up the PSMOO mode. update/search are optional in seconds
require("gpssetup").setPowerMode({
power_mode:"PSMOO",
update:optional (default 120),
search:optional (default 5)})
// This sets up SuperE
require("gpssetup").setPowerMode({power_mode:"SuperE"})
```
`setPowerMode` returns a promise, which is completed when the GPS is set up.
So you can for instance do the following to turn the GPS off once it
has been configured:
```
require("gpssetup").setPowerMode().then(function() {
Bangle.setGPSPower(0);
});
```
**Note:** It's not guaranteed that the user will have installed the `gpssetup`
app/module. It might be worth checking for its existence by surrounding the
`require` call with `try...catch` block.
```
var gpssetup;
try {
gpssetup = require("gpssetup")
} catch(e) {
E.showMessage("gpssetup\nnot installed");
}
```
## References
* [UBLOX M8 Receiver Data Sheet](https://www.u-blox.com/sites/default/files/products/documents/u-blox8-M8_ReceiverDescrProtSpec_%28UBX-13003221%29.pdf)
* [UBLOX Power Management App Note](https://www.u-blox.com/sites/default/files/products/documents/PowerManagement_AppNote_%28UBX-13005162%29.pdf)
* Some useful code on Github can be found [here](https://portal.u-blox.com/s/question/0D52p0000925T00CAE/ublox-max-m8q-getting-stuck-when-sleeping-with-extint-pin-control)
and [here](https://github.com/thasti/utrak/blob/master/gps.c)

109
apps/gpssetup/app.js Normal file
View File

@ -0,0 +1,109 @@
/*
* GPS Setup app, hughbarney AT googlemail DOT com
* With thanks to Gordon Williams for support and advice
*
* UBLOX power modes:
* SuperE - will provide updates every second and consume 35mA, 75mA with LCD on
* PSMOO - will sleep for update time and consume around 7mA during that period
* after acquiring satelite fixes the GPS will settle into a cycle of
* obtaining fix, sleeping for update seconds, wake up, get fix and sleep.
*
* See README file for more details
*
*/
Bangle.loadWidgets();
Bangle.drawWidgets();
function log_debug(o) {
//let timestamp = new Date().getTime();
//console.log(timestamp + " : " + o);
}
const SETTINGS_FILE = "gpssetup.settings.json";
let settings = undefined;
let settings_changed = false;
function updateSettings() {
require("Storage").write(SETTINGS_FILE, settings);
settings_changed = true;
}
function loadSettings() {
log_debug("loadSettings()");
settings = require("Storage").readJSON(SETTINGS_FILE,1)||{};
settings.update = settings.update||120;
settings.search = settings.search||5;
settings.power_mode = settings.power_mode||"SuperE";
log_debug(settings);
}
/*********** GPS Power and Setup Functions ******************/
function setupGPS() {
Bangle.setGPSPower(1);
setTimeout(function() {
require("gpssetup").setPowerMode().then(function() {
Bangle.setGPSPower(0);
});
}, 100);
}
/*********** GPS Setup Menu App *****************************/
function showMainMenu() {
var power_options = ["SuperE","PSMOO"];
const mainmenu = {
'': { 'title': 'GPS Setup' },
'< Back': ()=>{exitSetup();},
'Power Mode': {
value: 0 | power_options.indexOf(settings.power_mode),
min: 0, max: 1,
format: v => power_options[v],
onchange: v => {
settings.power_mode = power_options[v];
updateSettings();
},
},
'Update (s)': {
value: settings.update,
min: 10,
max: 1800,
step: 10,
onchange: v => {
settings.update = v;
updateSettings();
}
},
'Search (s)': {
value: settings.search,
min: 1,
max: 65,
step: 1,
onchange: v => {
settings.search = v;
updateSettings();
}
}
};
return E.showMenu(mainmenu);
}
function exitSetup() {
log_debug("exitSetup()");
if (settings_changed) {
log_debug(settings);
E.showMessage("Configuring GPS");
setTimeout(function() {
setupGPS();
setTimeout(function() { load() }, 750);
}, 500);
} else {
load();
}
}
loadSettings();
showMainMenu();

179
apps/gpssetup/gpssetup.js Normal file
View File

@ -0,0 +1,179 @@
const SETTINGS_FILE = "gpssetup.settings.json";
function log_debug(o) {
//let timestamp = new Date().getTime();
//console.log(timestamp + " : " + o);
}
function writeGPScmd(cmd) {
var d = [0xB5,0x62]; // sync chars
d = d.concat(cmd);
var a=0,b=0;
for (var i=2;i<d.length;i++) {
a += d[i];
b += a;
}
d.push(a&255,b&255);
Serial1.write(d);
}
// UBX-CFG-PMS - enable power management - Super-E
function UBX_CFG_PMS() {
log_debug("UBX_CFG_PMS()");
writeGPScmd([0x06,0x86, // msg class + type
8,0,//length
0x00,0x03, 0,0, 0,0, 0,0]);
}
/*
* Extended Power Management
* update and search are in milli seconds
* settings are loaded little endian, lsb first
*
* https://github.com/thasti/utrak/blob/master/gps.c
*/
function UBX_CFG_PM2(update,search) {
log_debug("UBX_CFG_PM2()");
// convert an integer to an array of bytes
function int_2_bytes( x ){
var bytes = [];
var i = 4;
do {
bytes[--i] = x & (255);
x = x>>8;
} while (i);
return bytes;
}
var u = int_2_bytes(update*1000);
var s = int_2_bytes(search*1000);
writeGPScmd([0x06, 0x3B, /* class id */
44, 0, /* length */
0x01, 0x00, 0x00, 0x00, /* v1, reserved 1..3 */
0x00, 0x10, 0x00, 0x00, /* on/off-mode, update ephemeris */
u[3], u[2], u[1], u[0], /* update period, ms, 120s=00 01 D4 C0, 30s= 00 00 75 30 */
s[3], s[2], s[1], s[0], /* search period, ms, 120s, 20s = 00 00 4E 20, 5s = 13 88 */
0x00, 0x00, 0x00, 0x00, /* grid offset */
0x00, 0x00, /* on-time after first fix */
0x01, 0x00, /* minimum acquisition time */
0x00, 0x00, 0x00, 0x00, /* reserved 4,5 */
0x00, 0x00, 0x00, 0x00, /* reserved 6 */
0x00, 0x00, 0x00, 0x00, /* reserved 7 */
0x00, 0x00, 0x00, 0x00, /* reserved 8,9,10 */
0x00, 0x00, 0x00, 0x00]); /* reserved 11 */
}
// enable power saving mode, after configured with PM2
function UBX_CFG_RXM() {
log_debug("UBX_CFG_RXM()");
writeGPScmd([0x06, 0x11, /* UBX-CFG-RXM */
2, 0, /* length */
0x08, 0x01]); /* reserved, enable power save mode */
}
/*
* Save configuration otherwise it will reset when the GPS wakes up
*/
function UBX_CFG_SAVE() {
log_debug("UBX_CFG_SAVE()");
writeGPScmd([0x06, 0x09, // class id
0x0D, 0x00, // length
0x00, 0x00, 0x00, 0x00, // clear mask
0xFF, 0xFF, 0x00, 0x00, // save mask
0x00, 0x00, 0x00, 0x00, // load mask
0x07]); // b2=eeprom b1=flash b0=bat backed ram
}
/*
* Reset to factory settings using clear mask in UBX_CFG_CFG
* https://portal.u-blox.com/s/question/0D52p0000925T00CAE/ublox-max-m8q-getting-stuck-when-sleeping-with-extint-pin-control
*/
function UBX_CFG_RESET() {
log_debug("UBX_CFG_RESET()");
writeGPScmd([0x06, 0x09, // class id
0x0D, 0x00,
0xFF, 0xFB, 0x00, 0x00, // clear mask
0x00, 0x00, 0x00, 0x00, // save mask
0xFF, 0xFF, 0x00, 0x00, // load mask
0x17]);
}
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function setupSuperE() {
log_debug("setupGPS() Super-E");
return Promise.resolve().then(function() {
UBX_CFG_RESET();
return delay(100);
}).then(function() {
UBX_CFG_PMS();
return delay(20);
}).then(function() {
UBX_CFG_SAVE();
return delay(20);
}).then(function() {
log_debug("Powering GPS Off");
/*
* must be part of the promise chain to ensure that
* setup does not return and powerOff before config functions
* have run
*/
return delay(20);
});
}
function setupPSMOO(settings) {
log_debug("setupGPS() PSMOO");
return Promise.resolve().then(function() {
UBX_CFG_RESET();
return delay(100);
}).then(function() {
UBX_CFG_PM2(settings.update, settings.search);
return delay(20);
}).then(function() {
UBX_CFG_RXM();
return delay(20);
}).then(function() {
UBX_CFG_SAVE();
return delay(20);
}).then(function() {
log_debug("Powering GPS Off");
/*
* must be part of the promise chain to ensure that
* setup does not return and powerOff before config functions
* have run
*/
return delay(20);
});
}
/** Set GPS power mode (assumes GPS on), returns a promise.
Either:
require("gpssetup").setPowerMode() // <-- set up GPS to current saved defaults
require("gpssetup").setPowerMode({power_mode:"PSMOO", update:optional, search:optional}) // <-- PSMOO mode
require("gpssetup").setPowerMode({power_mode:"SuperE"}) // <-- Super E mode
See the README for more information
*/
exports.setPowerMode = function(options) {
settings = require("Storage").readJSON(SETTINGS_FILE,1)||{};
if (options) {
if (options.update) settings.update = options.update;
if (options.search) settings.search = options.search;
if (options.power_mode) settings.power_mode = options.power_mode;
}
settings.update = settings.update||120;
settings.search = settings.search||5;
settings.power_mode = settings.power_mode||"SuperE";
if (options) require("Storage").write(SETTINGS_FILE, settings);
if (settings.power_mode === "PSMOO") {
return setupPSMOO(settings);
} else {
return setupSuperE();
}
};

BIN
apps/gpssetup/gpssetup.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

1
apps/gpssetup/icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/ACmsAAIss1mBwIfZqwABFpwuYC4IADGBYuaDQwuDF5ouYLo4vJIYousCYQOIHJIuUCo4uIHJIuUf4wlGEIQaHFywvHGAguiF5GBEIgbHFzAvJDwwuRvV6F6xLJFxmkAAQuKqwtKPQ4RKFwgwICgobHVRJAJFQOr0owIE5AtNDIYRI0ulGBAuNapYABCRGkGBAuGAoIpNGBIIFGBF6FwuBFygAKGBAulGBgujGBWAF0jpBehIvlqwws1jnCGFOs1heBGFQuBFoYwoFxFWwAwjFw+BAAIMBGEIuMGEIuIeQIQFGDouJ1gSHGDYuSGDYuUGDSzBFYP+dAQuNGBN6F6RcBFyAwHwAvQGAYuSGAheCF54wCAAYuRGAQABwGAC6QuWGAV6FyYA/AH4A2A="))

View File

@ -0,0 +1,4 @@
(function(back) {
// just go right to our app
load("gpssetup.app.js");
})();

View File

@ -0,0 +1 @@
{"power_mode":"SuperE", "update":120, "search":6}

View File

@ -8,3 +8,4 @@
Ensure 'on' is always supplied for translations Ensure 'on' is always supplied for translations
0.07: Improve handling of non-ASCII characters (fix #469) 0.07: Improve handling of non-ASCII characters (fix #469)
0.08: Added Mavigation units and en_NAV 0.08: Added Mavigation units and en_NAV
0.09: Added New Zealand en_NZ

View File

@ -245,6 +245,24 @@ var locales = {
day: "söndag,måndag,tisdag,onsdag,torsdag,fredag,lördag", day: "söndag,måndag,tisdag,onsdag,torsdag,fredag,lördag",
trans: { yes: "ja", Yes: "Ja", no: "nej", No: "Nej", ok: "ok", on: "on", off: "off" } trans: { yes: "ja", Yes: "Ja", no: "nej", No: "Nej", ok: "ok", on: "on", off: "off" }
}, },
"en_NZ": {
lang: "en_NZ",
decimal_point: ".",
thousands_sep: ",",
currency_symbol: "$",
int_curr_symbol: "NZD",
speed: "kph",
distance: { 0: "m", 1: "km" },
temperature: "°C",
ampm: { 0: "am", 1: "pm" },
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
datePattern: { 0: "%A, %B %d, %Y", "1": "%d/%m/%y" }, // Sunday, 1 March 2020 // 1/3/20
abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec",
month: "January,February,March,April,May,June,July,August,September,October,November,December",
abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat",
day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday",
// No translation for english...
},
"en_AU": { "en_AU": {
lang: "en_AU", lang: "en_AU",
decimal_point: ".", decimal_point: ".",

View File

@ -9,3 +9,4 @@
0.09: Added Pedometer clock 0.09: Added Pedometer clock
0.10: Added GPS and Grid Ref clock faces 0.10: Added GPS and Grid Ref clock faces
0.11: Updated Pedometer clock to retrieve steps from either wpedom or activepedom 0.11: Updated Pedometer clock to retrieve steps from either wpedom or activepedom
0.12: Removed GPS and Grid Ref clock faces, superceded by GPS setup and Walkers Clock

View File

@ -14,8 +14,6 @@
{"name":"digi.face.js","url":"digi.min.js"}, {"name":"digi.face.js","url":"digi.min.js"},
{"name":"txt.face.js","url":"txt.min.js"}, {"name":"txt.face.js","url":"txt.min.js"},
{"name":"ped.face.js","url":"ped.js"}, {"name":"ped.face.js","url":"ped.js"},
{"name":"osref.face.js","url":"osref.js"},
{"name":"gps.face.js","url":"pgs.js"},
{"name":"multiclock.img","url":"multiclock-icon.js","evaluate":true} {"name":"multiclock.img","url":"multiclock-icon.js","evaluate":true}
] ]
}, },

View File

@ -1,95 +0,0 @@
(() => {
function getFace(){
//var img = require("heatshrink").decompress(atob("mEwghC/AH4AKg9wC6t3u4uVC6wWBI6t3uJeVuMQCqcBLisAi4XLxAABFxAXKgc4DBAuBRhQXEDAq7MmYXEwBHEXZYXFGAOqAAKDMmczC4mIC62CC50PC4JIBkQABiIvRmURAAUSjQXSFwMoxGKC6CRFwUSVYgXLPIgXXwMYegoXLJAYXCGBnzGA0hPQIwMgYwGC6gwCC4ZIMC4gYBC604C4ZISmcRVgapQAAMhC6GIJIwXCMBcIxGDDBAuLC4IwGAARGMAAQWGmAXPJQoWMC4pwCCpoXJAB4XXAH4A/ABQA="));
var nofix = 0;
function formatTime(now) {
var fd = now.toUTCString().split(" ");
return fd[4];
}
function timeSince(t) {
var hms = t.split(":");
var now = new Date();
var sn = 3600*(now.getHours()) + 60*(now.getMinutes()) + 1*(now.getSeconds());
var st = 3600*(hms[0]) + 60*(hms[1]) + 1*(hms[2]);
return (sn - st);
}
function draw() {
var gps_on = false;
var fix = {
fix: 0,
alt: 0,
lat: 0,
lon: 0,
speed: 0,
time: 0,
satellites: 0
};
var y_line = 26;
var y_start = 46;
var x_start = 10;
// only attempt to get gps fix if gpsservuce is loaded
if (WIDGETS.gpsservice !== undefined) {
fix = WIDGETS.gpsservice.gps_get_fix();
gps_on = WIDGETS.gpsservice.gps_get_status();
}
g.reset();
g.clearRect(0,24,239,239);
if (fix.fix) {
var time = formatTime(fix.time);
var age = timeSince(time);
g.setFontAlign(-1, -1);
g.setFont("6x8");
g.setFontVector(22);
g.drawString("Alt: " + fix.alt +" m", x_start, y_start, true);
g.drawString("Lat: "+ fix.lat, x_start, y_start + y_line, true);
g.drawString("Lon: " + fix.lon, x_start, y_start + 2*y_line, true);
g.drawString("Time: " + time, x_start, y_start + 3*y_line, true);
g.drawString("Age(s): " + age, x_start, y_start + 4*y_line, true);
g.drawString("Satellites: " + fix.satellites, x_start, y_start + 5*y_line, true);
} else if (gps_on) {
g.setFontAlign(0, 1);
g.setFont("6x8", 2);
g.drawString("GPS Watch", 120, 60);
g.drawString("Waiting for GPS", 120, 80);
nofix = (nofix+1) % 4;
g.drawString(".".repeat(nofix) + " ".repeat(4-nofix), 120, 120);
g.setFontAlign(0,0);
g.drawString(fix.satellites + " satellites", 120, 100);
} else if (!gps_on) {
g.setFontAlign(0, 0);
g.setFont("6x8", 3);
g.drawString("GPS Watch", 120, 80);
g.drawString("GPS is off",120, 160);
}
}
function onSecond(){
var t = new Date();
if ((t.getSeconds() % 5) === 0) draw();
}
return {init:draw, tick:onSecond};
}
return getFace;
})();

View File

@ -1,202 +0,0 @@
(() => {
function getFace(){
var nofix = 0;
function formatTime(now) {
var fd = now.toUTCString().split(" ");
return fd[4];
}
function timeSince(t) {
var hms = t.split(":");
var now = new Date();
var sn = 3600*(now.getHours()) + 60*(now.getMinutes()) + 1*(now.getSeconds());
var st = 3600*(hms[0]) + 60*(hms[1]) + 1*(hms[2]);
return (sn - st);
}
Number.prototype.toRad = function() { return this*Math.PI/180; };
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Ordnance Survey Grid Reference functions (c) Chris Veness 2005-2014 */
/* - www.movable-type.co.uk/scripts/gridref.js */
/* - www.movable-type.co.uk/scripts/latlon-gridref.html */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function OsGridRef(easting, northing) {
this.easting = 0|easting;
this.northing = 0|northing;
}
OsGridRef.latLongToOsGrid = function(point) {
var lat = point.lat.toRad();
var lon = point.lon.toRad();
var a = 6377563.396, b = 6356256.909; // Airy 1830 major & minor semi-axes
var F0 = 0.9996012717; // NatGrid scale factor on central meridian
var lat0 = (49).toRad(), lon0 = (-2).toRad(); // NatGrid true origin is 49�N,2�W
var N0 = -100000, E0 = 400000; // northing & easting of true origin, metres
var e2 = 1 - (b*b)/(a*a); // eccentricity squared
var n = (a-b)/(a+b), n2 = n*n, n3 = n*n*n;
var cosLat = Math.cos(lat), sinLat = Math.sin(lat);
var nu = a*F0/Math.sqrt(1-e2*sinLat*sinLat); // transverse radius of curvature
var rho = a*F0*(1-e2)/Math.pow(1-e2*sinLat*sinLat, 1.5); // meridional radius of curvature
var eta2 = nu/rho-1;
var Ma = (1 + n + (5/4)*n2 + (5/4)*n3) * (lat-lat0);
var Mb = (3*n + 3*n*n + (21/8)*n3) * Math.sin(lat-lat0) * Math.cos(lat+lat0);
var Mc = ((15/8)*n2 + (15/8)*n3) * Math.sin(2*(lat-lat0)) * Math.cos(2*(lat+lat0));
var Md = (35/24)*n3 * Math.sin(3*(lat-lat0)) * Math.cos(3*(lat+lat0));
var M = b * F0 * (Ma - Mb + Mc - Md); // meridional arc
var cos3lat = cosLat*cosLat*cosLat;
var cos5lat = cos3lat*cosLat*cosLat;
var tan2lat = Math.tan(lat)*Math.tan(lat);
var tan4lat = tan2lat*tan2lat;
var I = M + N0;
var II = (nu/2)*sinLat*cosLat;
var III = (nu/24)*sinLat*cos3lat*(5-tan2lat+9*eta2);
var IIIA = (nu/720)*sinLat*cos5lat*(61-58*tan2lat+tan4lat);
var IV = nu*cosLat;
var V = (nu/6)*cos3lat*(nu/rho-tan2lat);
var VI = (nu/120) * cos5lat * (5 - 18*tan2lat + tan4lat + 14*eta2 - 58*tan2lat*eta2);
var dLon = lon-lon0;
var dLon2 = dLon*dLon, dLon3 = dLon2*dLon, dLon4 = dLon3*dLon, dLon5 = dLon4*dLon, dLon6 = dLon5*dLon;
var N = I + II*dLon2 + III*dLon4 + IIIA*dLon6;
var E = E0 + IV*dLon + V*dLon3 + VI*dLon5;
return new OsGridRef(E, N);
};
/*
* converts northing, easting to standard OS grid reference.
*
* [digits=10] - precision (10 digits = metres)
* to_map_ref(8, 651409, 313177); => 'TG 5140 1317'
* to_map_ref(0, 651409, 313177); => '651409,313177'
*
*/
function to_map_ref(digits, easting, northing) {
if (![ 0,2,4,6,8,10,12,14,16 ].includes(Number(digits))) throw new RangeError(`invalid precision '${digits}'`); // eslint-disable-line comma-spacing
let e = easting;
let n = northing;
// use digits = 0 to return numeric format (in metres) - note northing may be >= 1e7
if (digits == 0) {
const format = { useGrouping: false, minimumIntegerDigits: 6, maximumFractionDigits: 3 };
const ePad = e.toLocaleString('en', format);
const nPad = n.toLocaleString('en', format);
return `${ePad},${nPad}`;
}
// get the 100km-grid indices
const e100km = Math.floor(e / 100000), n100km = Math.floor(n / 100000);
// translate those into numeric equivalents of the grid letters
let l1 = (19 - n100km) - (19 - n100km) % 5 + Math.floor((e100km + 10) / 5);
let l2 = (19 - n100km) * 5 % 25 + e100km % 5;
// compensate for skipped 'I' and build grid letter-pairs
if (l1 > 7) l1++;
if (l2 > 7) l2++;
const letterPair = String.fromCharCode(l1 + 'A'.charCodeAt(0), l2 + 'A'.charCodeAt(0));
// strip 100km-grid indices from easting & northing, and reduce precision
e = Math.floor((e % 100000) / Math.pow(10, 5 - digits / 2));
n = Math.floor((n % 100000) / Math.pow(10, 5 - digits / 2));
// pad eastings & northings with leading zeros
e = e.toString().padStart(digits/2, '0');
n = n.toString().padStart(digits/2, '0');
return `${letterPair} ${e} ${n}`;
}
function draw() {
var gps_on = false;
var fix = {
fix: 0,
alt: 0,
lat: 0,
lon: 0,
speed: 0,
time: 0,
satellites: 0
};
var y_line = 26;
var y_start = 46;
var x_start = 10;
// only attempt to get gps fix if gpsservuce is loaded
if (WIDGETS.gpsservice !== undefined) {
fix = WIDGETS.gpsservice.gps_get_fix();
gps_on = WIDGETS.gpsservice.gps_get_status();
}
g.reset();
g.clearRect(0,24,239,239);
if (fix.fix) {
var time = formatTime(fix.time);
var age = timeSince(time);
var os = OsGridRef.latLongToOsGrid(fix);
var ref = to_map_ref(6, os.easting, os.northing);
age = age >=0 ? age : 0; // avoid -1 etc
g.reset();
g.clearRect(0,24,239,239);
g.setFontAlign(0,0);
g.setFont("Vector");
g.setColor(1,1,1);
g.setFontVector(40);
g.drawString(time, 120, 80);
g.setFontVector(40);
g.setColor(0xFFC0);
g.drawString(ref, 120,140, true);
g.setFont("6x8",2);
g.setColor(1,1,1);
g.drawString(age, 120, 194);
} else if (gps_on) {
g.setFontAlign(0, 1);
g.setFont("6x8", 2);
g.drawString("Gridref Watch", 120, 60);
g.drawString("Waiting for GPS", 120, 80);
nofix = (nofix+1) % 4;
g.drawString(".".repeat(nofix) + " ".repeat(4-nofix), 120, 120);
g.setFontAlign(0,0);
g.drawString(fix.satellites + " satellites", 120, 100);
} else if (!gps_on) {
g.setFontAlign(0, 0);
g.setFont("6x8", 3);
g.drawString("Gridref Watch", 120, 80);
g.drawString("GPS is off", 120, 160);
}
}
function onSecond(){
var t = new Date();
if ((t.getSeconds() % 5) === 0) draw();
}
return {init:draw, tick:onSecond};
}
return getFace;
})();

View File

@ -2,3 +2,4 @@
0.02: Add notification ID option 0.02: Add notification ID option
0.03: Pass `area{x,y,w,h}` to render callback instead of just `y` 0.03: Pass `area{x,y,w,h}` to render callback instead of just `y`
0.05: Adjust position of notification src text 0.05: Adjust position of notification src text
0.06: Support background color

View File

@ -41,7 +41,9 @@ function fitWords(text,rows,width) {
src : string // optional source name src : string // optional source name
body : string // optional body text body : string // optional body text
icon : string // optional icon (image string) icon : string // optional icon (image string)
render function(y) // function callback to render render : function(y) // function callback to render
bgColor : int/string // optional background color (default black)
titleBgColor : int/string // optional background color for title (default black)
} }
*/ */
/* /*
@ -83,13 +85,13 @@ exports.show = function(options) {
b = y+h-1, r = x+w-1; // bottom,right b = y+h-1, r = x+w-1; // bottom,right
g.setClipRect(x,y, r,b); g.setClipRect(x,y, r,b);
// clear area // clear area
g.setColor(0).fillRect(x,y, r,b); g.setColor(options.bgColor||0).fillRect(x,y, r,b);
// bottom border // bottom border
g.setColor(0x39C7).fillRect(0,b-1, r,b); g.setColor(0x39C7).fillRect(0,b-1, r,b);
b -= 2;h -= 2; b -= 2;h -= 2;
// title bar // title bar
if (options.title || options.src) { if (options.title || options.src) {
g.setColor(0x39C7).fillRect(x,y, r,y+20); g.setColor(options.titleBgColor||0x39C7).fillRect(x,y, r,y+20);
const title = options.title||options.src; const title = options.title||options.src;
g.setColor(-1).setFontAlign(-1, -1, 0).setFont("6x8", 2); g.setColor(-1).setFontAlign(-1, -1, 0).setFont("6x8", 2);
g.drawString(title.trim().substring(0, 13), x+25,y+3); g.drawString(title.trim().substring(0, 13), x+25,y+3);

View File

@ -4,3 +4,4 @@
0.04: Pass `area{x,y,w,h}` to render callback instead of just `y` 0.04: Pass `area{x,y,w,h}` to render callback instead of just `y`
0.05: Fix `g` corruption issue if .hide gets called twice 0.05: Fix `g` corruption issue if .hide gets called twice
0.06: Adjust position of notification src text and notifications without title 0.06: Adjust position of notification src text and notifications without title
0.07: Support background color

View File

@ -37,7 +37,9 @@ function fitWords(text,rows,width) {
src : string // optional source name src : string // optional source name
body : string // optional body text body : string // optional body text
icon : string // optional icon (image string) icon : string // optional icon (image string)
render function(y) // function callback to render render : function(y) // function callback to render
bgColor : int/string // optional background color (default black)
titleBgColor : int/string // optional background color for title (default black)
} }
*/ */
exports.show = function(options) { exports.show = function(options) {
@ -53,11 +55,11 @@ exports.show = function(options) {
w = 240, w = 240,
h = size; h = size;
// clear screen // clear screen
g.clear(1); g.setColor(options.bgColor||0).fillRect(0,0,g.getWidth(),g.getHeight());
// top bar // top bar
if (options.title||options.src) { if (options.title||options.src) {
const title = options.title || options.src const title = options.title || options.src
g.setColor(0x39C7).fillRect(x, y, x+w-1, y+30); g.setColor(options.titleBgColor||0x39C7).fillRect(x, y, x+w-1, y+30);
g.setColor(-1).setFontAlign(-1, -1, 0).setFont("6x8", 3); g.setColor(-1).setFontAlign(-1, -1, 0).setFont("6x8", 3);
g.drawString(title.trim().substring(0, 13), x+5, y+3); g.drawString(title.trim().substring(0, 13), x+5, y+3);
if (options.title && options.src) { if (options.title && options.src) {

View File

@ -5,3 +5,4 @@
0.05: Add setting to turn vibrate on/off. 0.05: Add setting to turn vibrate on/off.
0.06: Tweaks to vibration settings. 0.06: Tweaks to vibration settings.
0.07: Switch to BTN1 for Max toggle and reset function. 0.07: Switch to BTN1 for Max toggle and reset function.
1.00: New features. Added waypoints file and distance to selected waypoint display. Added integration with GPS Setup module to switch GPS to low power mode when screen off. Save display settings and restore when app restarted.

View File

@ -1,18 +1,143 @@
Displays the GPS speed and altitude. One is displayed on the watch face using the largest possible characters depending on the number of digits. The other is in a smaller characters below that. Both are always visible. You can display the current or maximum observed speed/altitude values. Current time is always displayed. # GPS Speed, Altimeter and Distance to Waypoint
Display Tap : Swaps the displays. You can have either speed or altitude on the large primary display. You can switch between two display modes. One showing speed and altitude (A) and one showing speed and distance to waypoint (D).
BTN1 : Short press < 2 secs toggles the displays between showing the current speed/alt values or the maximum values recorded. Within each display mode one figure is displayed on the watch face using the largest possible characters depending on the number of digits. The other is in a smaller characters below that. Both are always visible. You can display the current or maximum observed speed/altitude values. Current time is always displayed.
The waypoints list is the same as that used with the [GPS Navigation](https://banglejs.com/apps/#gps%20navigation) app so the same set of waypoints can be used across both apps. Refer to that app for waypoint file information.
## Buttons and Controls
BTN3 : Swaps the modes between Speed+[A]ltitude or Speed+[D]istance.
### [A]ltitude mode
BTN1 : Short press < 2 secs toggles the displays between showing the current speed/alt values or the maximum speed/alt values recorded.
BTN1 : Long press > 2 secs resets the recorded maximum values. BTN1 : Long press > 2 secs resets the recorded maximum values.
App Settings : Select the desired display units. Speed can be as per the default locale, kph, knots, mph or m/s. Altitude can be feet or metres. Select one of three colour schemes. Colours, high contrast (all white on black) or night ( all red on black ). Vibration can be used to indicate when a fix is lost or gained. One buzz for a lost fix and a double buzz when a fix is found. ### [D]istance mode
![](screen1.png) BTN1 : Select next waypoint. Last fix distance from selected waypoint is displayed.
![](screen2.png)
![](screen3.png) ### Both modes
![](screen4.png)
BTN2 : Disables/Restores power saving timeout. Locks the screen on to enable reading for longer periods but uses maximum battery drain. Red LED (dot) at top of screen when screen is locked on. Press again to restore power saving timeouts.
BTN3 : Long press exit and return to watch.
BTN4 : Left Display Tap : Swaps which figure is in the large display. You can have either speed or [A]ltitude/[D]istance on the large primary display.
## App Settings
Select the desired display units. Speed can be as per the default locale, kph, knots, mph or m/s. Distance can be km, miles or nautical miles. Altitude can be feet or metres. Select one of three colour schemes. Default (three colours), high contrast (all white on black) or night ( all red on black ).
## Loss of fix
When the GPS obtains a fix the number of satellites is displayed as 'Sats:nn'. When unable to obtain a fix then the last known fix is used and the age of that fix in seconds is displayed as 'Age:nn'. Seeing 'Sats' or 'Age' indicates whether the GPS has a current fix or not.
## Screens
Speed and Altitude:<br>
![](screen1.png)<p>
Left tap swaps displays:<br>
![](screen2.png)<p>
Distance to waypoint DeltaW:<br>
![](screen5.png)<p>
MAX Values instead:<br>
![](screen3.png)<p>
Settings:<br>
![](screen4.png)<p>
## Power Saving
The The GPS Adv Sport app obeys the watch screen off timeouts as a power saving measure. Restore the screen as per any of the colck/watch apps. Use BTN2 to lock the screen on but doing this will use more battery.
This app will work quite happily on its own but will use the [GPS Setup App](https://banglejs.com/apps/#gps%20setup) if it is installed. You may choose to use the GPS Setup App to gain significantly longer battery life while the GPS is on. Please read the Low Power GPS Setup App Readme to understand what this does.
When using the GPS Setup App this app switches the GPS to SuperE (default) mode while the display is lit and showing fix information. This ensures that that fixes are updated every second or so. 10 seconds after the display is blanked by the watch this app will switch the GPS to PSMOO mode and will only attempt to get a fix every two minutes. This improves power saving while the display is off and the delay gives an opportunity to restore the display before the GPS power mode is switched.
The MAX values continue to be collected with the display off so may appear a little odd after the intermittent fixes of the low power mode.
## Waypoints
Waypoints are used in [D]istance mode. Create a file waypoints.json and write to storage on the Bangle.js using the IDE. The first 6 characters of the name are displayed in Speed+[D]istance mode.
The [GPS Navigation](https://banglejs.com/apps/#gps%20navigation) app in the App Loader has a really nice waypoints file editor. (Must be connected to your Bangle.JS and then click on the Download icon.)
Sample waypoints.json (My sailing waypoints)
<pre>
[
{
"name":"NONE"
},
{
"name":"Omori",
"lat":-38.9058670,
"lon":175.7613350
},
{
"name":"DeltaW",
"lat":-38.9438550,
"lon":175.7676930
},
{
"name":"DeltaE",
"lat":-38.9395240,
"lon":175.7814420
},
{
"name":"BtClub",
"lat":-38.9446020,
"lon":175.8475720
},
{
"name":"Hapua",
"lat":-38.8177750,
"lon":175.8088720
},
{
"name":"Nook",
"lat":-38.7848090,
"lon":175.7839440
},
{
"name":"ChryBy",
"lat":-38.7975050,
"lon":175.7551960
},
{
"name":"Waiha",
"lat":-38.7219630,
"lon":175.7481520
},
{
"name":"KwaKwa",
"lat":-38.6632310,
"lon":175.8670320
},
{
"name":"Hatepe",
"lat":-38.8547420,
"lon":176.0089124
},
{
"name":"Kinloc",
"lat":-38.6614442,
"lon":175.9161607
}
]
</pre>
## Comments and Feedback
Developed for my use in sailing, cycling and motorcycling. If you find this software useful or have feedback drop me a line mike[at]kereru.com. Enjoy! Developed for my use in sailing, cycling and motorcycling. If you find this software useful or have feedback drop me a line mike[at]kereru.com. Enjoy!
( Many thanks to Gordon Williams. Awesome job. ) ## Thanks
Many thanks to Gordon Williams. Awesome job.
Special thanks also to @jeffmer, for the [GPS Navigation](https://banglejs.com/apps/#gps%20navigation) app and @hughbarney for the Low power GPS code development.

View File

@ -1 +1 @@
require("heatshrink").decompress(atob("m02xH+AH4AJlgAMGWQ4lEw1Wq2BAAgHBG8QhFq+C1nXABGswQ4GGjhkBGJIAHOQI2ZDIcyGaQ3EmQ3WNAiaKABusNyoUDNCxuGGySdDNDBuGUoY0e2etAAWzGzoOCGheJxNlAA2J1o2LGprTNGRAAFbZw0LqwZI1pkGAAgJDUxdWGxTULFAmJFJGzURjaET6Q0DGZKYIrqjRT5Q0EGZwpEUZZqPaYaRMGg6LINhBJKNK40JAAI1Iq6fJGjDrIUQoVCwRqJDhA0OXYQRGwRsEAgWsNTCeIDYTwG1g1GqygJNROsq0ymVWq7TI2ZRJUQg1JUARkMAAg0FDhY1GDAxOKQwgAEKI6IKKAQ1KeAQ1IJ4VXAog1gC4Q1ImQVBAwYFBmQ1K1o1fMoQ1ORIQ1Ua5ahD1uzrqnEULo1L1gVBAAusGrDxHC4NlEY4aDAAZQGDhiHCGpZOJNodWmUyqxpIRBY1GQw7wCURAAPKIWtXhI1EwSFJNhIAMNQSgHwQ1EGwVXKBJsWDJSgENgjyKGyg0CNQ/XNQo1DwIRGbIS+HABYWLwI1GGwVWKhZtQChigGNhg2TCRhqIGwcy1g2YCBmsmQ0INgajIYgY1PdRKfCGpCjMG4ShNBxSfKGwyjIADOsGho1DbRI0YagQ1MG0Y0RGwjbLACLTDGh42FqxuY1lWGig2FmRuWwKdDGiZuHG6WBNC42JHAWCVBWswQyEGjQ3IHAJyBAAgHBCAwzbG5QAMGb44SGUgAkA")) require("heatshrink").decompress(atob("mEwxH+AH4A/AE+sFtoABF12swItsF9QuFR4IwmFwwvnFw4vCGEYuIF4JgjFxIvkFxQvCGBfOAAQvqFwYwRFxYvDGBIvUFxgv/F6IuNF4n+0nB4TvXFxwvF4XBAALlPF7ZfBGC4uPF4rABGAYAGTQwvad4YwKFzYvIGBQvfFwgAE3Qvt4IvEFzgvCLxO7Lx7vULzIzTFwIvgGZheFRAiNRGSQvpGYouesYAGmQAKq3CE4PIC4wviq2eFwPCroveCRSGEC6Qv0DAwRLcoouWC4VdVYQXkr1eAgVdAoIABroNEB4gHHC5QvHwQSDAAOCA74vH1uICQIABxGtA74vIAEwv/F/4vXAH4A/AHY"))

View File

@ -1,110 +1,100 @@
/* /*
Speed and Altitude [speedalt] Speed and Altitude [speedalt]
Ver : 0.07
Mike Bennett mike[at]kereru.com Mike Bennett mike[at]kereru.com
1.16 : Use new GPS settings module
*/ */
var v = '1.20';
const dbg = 0;
var buf = Graphics.createArrayBuffer(240,160,2,{msb:true}); var buf = Graphics.createArrayBuffer(240,160,2,{msb:true});
// Load fonts // Load fonts
require("Font7x11Numeric7Seg").add(Graphics); require("Font7x11Numeric7Seg").add(Graphics);
/* var lf = {fix:0,satellites:0};
var mainmenu = {
"" : { "title" : "-- Units --" },
"default" : function() { setUnits(0,''); },
"Kph (spd)" : function() { setUnits(1,'kph'); },
"Knots (spd)" : function() { setUnits(1.852,'knots'); },
"Mph (spd)" : function() { setUnits(1.60934,'mph'); },
"m/s (spd)" : function() { setUnits(3.6,'m/s'); },
"Meters (alt)" : function() { setUnitsAlt(1,'m'); },
"Feet (alt)" : function() { setUnitsAlt(0.3048,'feet'); },
"Exit" : function() { exitMenu(); }, // remove the menu and restore
};
*/
var lastFix = {fix:0,satellites:0};
var showSpeed = 1; // 1 = Speed in primary display. 0 = alt in primary
var showMax = 0; // 1 = display the max values. 0 = display the cur fix var showMax = 0; // 1 = display the max values. 0 = display the cur fix
var maxPress = 0; // Time max button pressed. Used to calculate short or long press. var pwrSav = 1; // 1 = default power saving with watch screen off and GPS to PMOO mode. 0 = screen kept on.
var canDraw = 1; var canDraw = 1;
var lastBuzz = 0; // What sort of buzz was last performed. 0 = no fix, 1 = fix.
var timerBuzz2 = 0; // ID of timer for fix second buzz
var time = ''; // Last time string displayed. Re displayed in background colour to remove before drawing new time. var time = ''; // Last time string displayed. Re displayed in background colour to remove before drawing new time.
var tmrLP; // Timer for delay in switching to low power after screen turns off
var max = {}; var max = {};
max.spd = 0; max.spd = 0;
max.alt = 0; max.alt = 0;
var emulator = 0; var emulator = (process.env.BOARD=="EMSCRIPTEN")?1:0; // 1 = running in emulator. Supplies test values;
if (process.env.BOARD=="EMSCRIPTEN") emulator = 1; // 1 = running in emulator. Supplies test values;
var wp = {}; // Waypoint to use for distance from cur position.
function nxtWp(inc){
if (cfg.modeA) return;
cfg.wp+=inc;
loadWp();
}
function loadWp() {
var w = require("Storage").readJSON('waypoints.json')||[{name:"NONE"}];
if (cfg.wp>=w.length) cfg.wp=0;
if (cfg.wp<0) cfg.wp = w.length-1;
savSettings();
wp = w[cfg.wp];
}
function radians(a) {
return a*Math.PI/180;
}
function distance(a,b){
var x = radians(a.lon-b.lon) * Math.cos(radians((a.lat+b.lat)/2));
var y = radians(b.lat-a.lat);
// Distance in selected units
var d = Math.sqrt(x*x + y*y) * 6371000;
d = (d/parseFloat(cfg.dist)).toFixed(2);
if ( d >= 100 ) d = parseFloat(d).toFixed(1);
if ( d >= 1000 ) d = parseFloat(d).toFixed(0);
return d;
}
function drawFix(dat) {
function drawFix(speed,units,sats,alt,alt_units) {
if (!canDraw) return; if (!canDraw) return;
buf.clear(); buf.clear();
var val = ''; var v = '';
var u=''; var u='';
// Primary Display // Primary Display
val = speed.toString(); v = (cfg.primSpd)?dat.speed.toString():dat.alt.toString();
if ( !showSpeed ) val = alt.toString();
// Primary Units // Primary Units
u = settings.spd_unit; u = (cfg.primSpd)?cfg.spd_unit:dat.alt_units;
if ( !showSpeed ) u = alt_units;
drawPrimary(val,u); drawPrimary(v,u);
// Secondary Display // Secondary Display
val = alt.toString(); v = (cfg.primSpd)?dat.alt.toString():dat.speed.toString();
if ( !showSpeed ) val = speed.toString();
// Secondary Units // Secondary Units
u = alt_units; u = (cfg.primSpd)?dat.alt_units:cfg.spd_unit;
if ( !showSpeed ) u = settings.spd_unit;
drawSecondary(val,u); drawSecondary(v,u);
// Time // Time
drawTime(); drawTime();
// Waypoint name
drawWP();
//Sats //Sats
drawSats(sats); if ( dat.age > 10 ) {
if ( dat.age > 90 ) dat.age = '>90';
drawSats('Age:'+dat.age);
}
else drawSats('Sats:'+dat.sats);
g.reset(); g.reset();
g.drawImage(img,0,40); g.drawImage(img,0,40);
// g.flip();
}
function drawNoFix(sats) {
if (!canDraw) return;
var u;
buf.clear();
buf.setFontAlign(0,0);
buf.setColor(3);
buf.setFontVector(25);
buf.drawString("Waiting for GPS",120,56);
// Time
drawTime();
//Sats
drawSats(sats);
g.reset();
g.drawImage(img,0,40);
// g.flip();
} }
@ -113,15 +103,16 @@ function drawPrimary(n,u) {
// Primary Display // Primary Display
var s=40; // Font size var s=40; // Font size
if ( n.length <= 7 ) s=48; var l=n.length;
if ( n.length <= 6 ) s=55;
if ( n.length <= 5 ) s=68; if ( l <= 7 ) s=48;
if ( n.length <= 4 ) s=85; if ( l <= 6 ) s=55;
if ( n.length <= 3 ) s=110; if ( l <= 5 ) s=68;
if ( l <= 4 ) s=85;
if ( l <= 3 ) s=110;
buf.setFontAlign(0,-1); //Centre buf.setFontAlign(0,-1); //Centre
buf.setColor(1); buf.setColor(1);
buf.setFontVector(s); buf.setFontVector(s);
buf.drawString(n,110,0); buf.drawString(n,110,0);
@ -135,11 +126,12 @@ function drawPrimary(n,u) {
function drawSecondary(n,u) { function drawSecondary(n,u) {
var s=180; // units X position var s=180; // units X position
if ( n.length <= 5 ) s=155; var l=n.length;
if ( n.length <= 4 ) s=125; if ( l <= 5 ) s=155;
if ( n.length <= 3 ) s=100; if ( l <= 4 ) s=125;
if ( n.length <= 2 ) s=65; if ( l <= 3 ) s=100;
if ( n.length <= 1 ) s=35; if ( l <= 2 ) s=65;
if ( l <= 1 ) s=35;
buf.setFontAlign(-1,1); //left, bottom buf.setFontAlign(-1,1); //left, bottom
buf.setColor(1); buf.setColor(1);
@ -153,7 +145,6 @@ function drawSecondary(n,u) {
buf.drawString(u,s,135); buf.drawString(u,s,135);
} }
function drawTime() { function drawTime() {
var x = 0; var x = 0;
var y = 160; var y = 160;
@ -168,181 +159,225 @@ function drawTime() {
buf.drawString(time,x,y); buf.drawString(time,x,y);
} }
function drawWP() {
var nm = wp.name;
if ( nm == undefined || nm == 'NONE' || cfg.modeA ) nm = '';
buf.setFontAlign(-1,1); //left, bottom
buf.setColor(2);
buf.setFontVector(20);
buf.drawString(nm.substring(0,6),77,160);
}
function drawSats(sats) { function drawSats(sats) {
buf.setFontAlign(1,1); //right, bottom
buf.setColor(3); buf.setColor(3);
buf.setFont("6x8", 2); buf.setFont("6x8", 2);
if ( showMax ) { buf.setFontAlign(1,1); //right, bottom
buf.setColor(2); buf.drawString(sats,240,160);
buf.drawString("MAX",240,160);
buf.setFontVector(20);
buf.setColor(2);
if ( cfg.modeA ) buf.drawString("A",240,140);
else buf.drawString("D",240,140);
if ( showMax && cfg.modeA ) {
buf.setFontAlign(0,1); //centre, bottom
buf.drawString("MAX",120,164);
} }
else buf.drawString("Sats:"+sats,240,160);
} }
function onGPS(fix) { function onGPS(fix) {
lastFix = fix;
if ( emulator ) {
fix.fix = 1;
fix.speed = 10;
fix.alt = 354;
fix.lat = -38.92;
fix.lon = 175.7613350;
fix.course = 245;
fix.satellites = 12;
fix.time = new Date();
}
var m; var m;
if (fix.fix || emulator) { var sp = '---';
doBuzz(1); var al = '---';
var di = '---';
var age = '---';
//==== Speed ==== if (fix.fix) lf = fix;
if ( settings.spd == 0 ) { if (lf.fix) {
var strSpeed = require("locale").speed(fix.speed);
m = strSpeed.match(/([0-9,\.]+)(.*)/); // regex splits numbers from units
if ( emulator ) { // Speed
speed = '125'; //testing only if ( cfg.spd == 0 ) {
settings.spd_unit = 'kph'; m = require("locale").speed(lf.speed).match(/([0-9,\.]+)(.*)/); // regex splits numbers from units
} sp = parseFloat(m[1]);
else { cfg.spd_unit = m[2];
speed = m[1];
settings.spd_unit = m[2];
}
}
// Calculate for selected units
else {
speed = fix.speed;
if ( emulator ) speed = '100';
speed = Math.round(parseFloat(speed)/parseFloat(settings.spd),0);
} }
else sp = parseFloat(lf.speed)/parseFloat(cfg.spd); // Calculate for selected units
// ==== Altitude ==== if ( sp < 10 ) sp = sp.toFixed(1);
alt = fix.alt; else sp = Math.round(sp);
if ( emulator ) alt = '360'; if (parseFloat(sp) > parseFloat(max.spd) ) max.spd = parseFloat(sp);
alt = Math.round(parseFloat(alt)/parseFloat(settings.alt),0);
// Record max values // Altitude
if (parseFloat(speed) > parseFloat(max.spd) ) max.spd = parseFloat(speed); al = lf.alt;
if (parseFloat(alt) > parseFloat(max.alt) ) max.alt = parseFloat(alt); al = Math.round(parseFloat(al)/parseFloat(cfg.alt));
if (parseFloat(al) > parseFloat(max.alt) ) max.alt = parseFloat(al);
if ( showMax ) drawFix(max.spd,settings.spd_unit,fix.satellites,max.alt,settings.alt_unit); // Distance to waypoint
else drawFix(speed,settings.spd_unit,fix.satellites,alt,settings.alt_unit); di = distance(lf,wp);
if (isNaN(di)) di = 0;
} else { // Age of last fix (secs)
doBuzz(0); age = Math.max(0,Math.round(getTime())-(lf.time.getTime()/1000));
drawNoFix(fix.satellites);
} }
} if ( cfg.modeA ) {
if ( showMax )
// Vibrate watch when fix lost or gained. drawFix({
function doBuzz(hasFix) { speed:max.spd,
sats:lf.satellites,
// nothing to do alt:max.alt,
if ( lastBuzz === hasFix || !settings.buzz ) { alt_units:cfg.alt_unit,
return; age:age,
fix:lf.fix
}); // Speed and alt maximums
else
drawFix({
speed:sp,
sats:lf.satellites,
alt:al,
alt_units:cfg.alt_unit,
age:age,
fix:lf.fix
}); // Show speed/altitude
}
else {
// Show speed/distance
if ( di <= 0 )
drawFix({
speed:sp,
sats:lf.satellites,
alt:'',
alt_units:'',
age:age,
fix:lf.fix
}); // No WP selected
else
drawFix({
speed:sp,
sats:lf.satellites,
alt:di,
alt_units:cfg.dist_unit,
age:age,
fix:lf.fix
});
} }
// fix gained - double buzz
if ( !lastBuzz && hasFix ) {
if ( dbg ) print('Fix');
lastBuzz = 1;
Bangle.buzz();
timerBuzz2 = setInterval(doBuzz2, 600); // Trigger a second buzz
return;
}
// fix lost - single buzz
if ( lastBuzz && !hasFix ) {
if ( dbg ) print('Fix lost');
lastBuzz = 0;
Bangle.buzz();
return;
}
}
// Second buzz
function doBuzz2() {
if ( dbg ) print('Buzz2');
clearInterval(timerBuzz2);
Bangle.buzz();
}
function toggleDisplay() {
showSpeed = !showSpeed;
onGPS(lastFix); // Back to Speed display
}
function toggleMax() {
// if ( inMenu ) return;
showMax = !showMax;
onGPS(lastFix); // Back to Speed display
} }
function setButtons(){ function setButtons(){
// Show launcher when middle button pressed // Spd+Dist : Select next waypoint
setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"}); setWatch(function(e) {
var dur = e.time - e.lastTime;
if ( cfg.modeA ) {
// Spd+Alt mode - Switch between fix and MAX
if ( dur < 2 ) showMax = !showMax; // Short press toggle fix/max display
else { max.spd = 0; max.alt = 0; } // Long press resets max values.
}
else nxtWp(1); // Spd+Dist mode - Select next waypoint
onGPS(lf);
}, BTN1, { edge:"falling",repeat:true});
// Switch between fix and max display on short press or reset max values on long press // Power saving on/off
setWatch(maxPressed, BTN1,{repeat:true,edge:"rising"}); setWatch(function(e){
setWatch(maxReleased, BTN1,{repeat:true,edge:"falling"}); pwrSav=!pwrSav;
if ( pwrSav ) {
LED1.reset();
var s = require('Storage').readJSON('setting.json',1)||{};
var t = s.timeout||10;
Bangle.setLCDTimeout(t);
}
else {
Bangle.setLCDTimeout(0);
Bangle.setLCDPower(1);
LED1.set();
}
}, BTN2, {repeat:true,edge:"falling"});
// Touch screen to toggle display // Toggle between alt or dist
setWatch(toggleDisplay, BTN4, {repeat:true,edge:"falling"}); setWatch(function(e){
setWatch(toggleDisplay, BTN5, {repeat:true,edge:"falling"}); cfg.modeA = !cfg.modeA;
savSettings();
onGPS(lf);
}, BTN3, {repeat:true,edge:"falling"});
// Touch left screen to toggle display
setWatch(function(e){
cfg.primSpd = !cfg.primSpd;
savSettings();
onGPS(lf); // Update display
}, BTN4, {repeat:true,edge:"falling"});
} }
function maxPressed() {
maxPress = getTime();
}
function maxReleased() {
var dur = getTime()-maxPress;
if ( dur < 2 ) toggleMax(); // Short press toggle fix/max display
else {
max.spd = 0; // Long press resets max values.
max.alt = 0;
onGPS(lastFix); // redraw display
}
}
function updateClock() { function updateClock() {
if ( dbg ) print('Updating clock');
if (!canDraw) return; if (!canDraw) return;
drawTime(); drawTime();
g.reset(); g.reset();
g.drawImage(img,0,40); g.drawImage(img,0,40);
// g.flip(); if ( emulator ) {max.spd++;max.alt++;}
// Something different to display in the emulator
if ( emulator ) {
max.spd++;
max.alt++;
}
} }
function startDraw(){ function startDraw(){
canDraw=true; canDraw=true;
setLpMode('SuperE'); // off
g.clear(); g.clear();
Bangle.drawWidgets(); Bangle.drawWidgets();
onGPS(lastFix); // draw app screen onGPS(lf); // draw app screen
} }
function stopDraw() { function stopDraw() {
canDraw=false; canDraw=false;
if (!tmrLP) tmrLP=setInterval(function () {if (lf.fix) setLpMode('PSMOO');}, 10000); //Drop to low power in 10 secs. Keep lp mode off until we have a first fix.
} }
// ===== Main Prog ===== function savSettings() {
require("Storage").write('speedalt.json',cfg);
}
function setLpMode(m) {
if (tmrLP) {clearInterval(tmrLP);tmrLP = false;} // Stop any scheduled drop to low power
if ( !gpssetup ) return;
gpssetup.setPowerMode({power_mode:m})
}
// =Main Prog
// Read settings. // Read settings.
let settings = require('Storage').readJSON('speedalt.json',1)||{}; let cfg = require('Storage').readJSON('speedalt.json',1)||{};
settings.spd = settings.spd||0; // Multiplier for speed unit conversions. 0 = use the locale values for speed cfg.spd = cfg.spd||0; // Multiplier for speed unit conversions. 0 = use the locale values for speed
settings.spd_unit = settings.spd_unit||''; // Displayed speed unit cfg.spd_unit = cfg.spd_unit||''; // Displayed speed unit
settings.alt = settings.alt||0.3048;// Multiplier for altitude unit conversions. cfg.alt = cfg.alt||0.3048;// Multiplier for altitude unit conversions.
settings.alt_unit = settings.alt_unit||'feet'; // Displayed altitude units cfg.alt_unit = cfg.alt_unit||'feet'; // Displayed altitude units
settings.colour = settings.colour||0; // Colour scheme. cfg.dist = cfg.dist||1000;// Multiplier for distnce unit conversions.
settings.buzz = settings.buzz||0; // Buzz when fix lost or gained. cfg.dist_unit = cfg.dist_unit||'km'; // Displayed altitude units
cfg.colour = cfg.colour||0; // Colour scheme.
cfg.wp = cfg.wp||0; // Last selected waypoint for dist
cfg.modeA = cfg.modeA||0; // 0 = [D], 1 = [A]
cfg.primSpd = cfg.primSpd||0; // 1 = Spd in primary, 0 = Spd in secondary
loadWp();
/* /*
Colour Pallet Idx Colour Pallet Idx
@ -359,50 +394,43 @@ var img = {
palette:new Uint16Array([0,0x4FE0,0xEFE0,0x07DB]) palette:new Uint16Array([0,0x4FE0,0xEFE0,0x07DB])
}; };
if ( settings.colour == 1 ) img.palette = new Uint16Array([0,0xFFFF,0xFFFF,0xFFFF]); if ( cfg.colour == 1 ) img.palette = new Uint16Array([0,0xFFFF,0xFFF6,0xDFFF]);
if ( settings.colour == 2 ) img.palette = new Uint16Array([0,0xFF800,0xF800,0xF800]); if ( cfg.colour == 2 ) img.palette = new Uint16Array([0,0xFF800,0xFAE0,0xF813]);
// Find speed unit if using locale speed
if ( settings.spd == 0 ) {
var strSpeed = require("locale").speed(1);
m = strSpeed.match(/([0-9,\.]+)(.*)/); // regex splits numbers from units
settings.spd_unit = m[2];
}
var SCREENACCESS = { var SCREENACCESS = {
withApp:true, withApp:true,
request:function(){ request:function(){this.withApp=false;stopDraw();},
this.withApp=false; release:function(){this.withApp=true;startDraw();}
stopDraw();
clearWatch();
},
release:function(){
this.withApp=true;
startDraw();
setButtons();
}
}; };
Bangle.on('lcdPower',function(on) { Bangle.on('lcdPower',function(on) {
if (!SCREENACCESS.withApp) return; if (!SCREENACCESS.withApp) return;
if (on) { if (on) startDraw();
startDraw(); else stopDraw();
} else {
stopDraw();
}
}); });
var gpssetup;
try {
gpssetup = require("gpssetup");
} catch(e) {
gpssetup = false;
}
// All set up. Lets go. // All set up. Lets go.
g.clear(); g.clear();
Bangle.setLCDBrightness(1);
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
onGPS(lf);
Bangle.setGPSPower(1); Bangle.setGPSPower(1);
onGPS(lastFix); if ( gpssetup ) {
gpssetup.setPowerMode({power_mode:"SuperE"}).then(function() { Bangle.setGPSPower(1); });
}
else {
Bangle.setGPSPower(1);
}
Bangle.on('GPS', onGPS); Bangle.on('GPS', onGPS);
setButtons(); setButtons();
setInterval(updateClock, 30000); setInterval(updateClock, 30000);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
apps/speedalt/screen5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -19,6 +19,12 @@
writeSettings(); writeSettings();
} }
function setUnitsDist(d,u) {
settings.dist = d;
settings.dist_unit = u;
writeSettings();
}
function setColour(c) { function setColour(c) {
settings.colour = c; settings.colour = c;
writeSettings(); writeSettings();
@ -27,13 +33,15 @@
const appMenu = { const appMenu = {
'': {'title': 'GPS Speed Alt'}, '': {'title': 'GPS Speed Alt'},
'< Back': back, '< Back': back,
'< Load GPS Adv Sport': ()=>{load('speedalt.app.js');},
'Units' : function() { E.showMenu(unitsMenu); }, 'Units' : function() { E.showMenu(unitsMenu); },
'Colours' : function() { E.showMenu(colMenu); }, 'Colours' : function() { E.showMenu(colMenu); }/*,
'Vibrate' : { 'Vibrate' : {
value : settings.buzz, value : settings.buzz,
format : v => v?"On":"Off", format : v => v?"On":"Off",
onchange : () => { settings.buzz = !settings.buzz; writeSettings(); } onchange : () => { settings.buzz = !settings.buzz; writeSettings(); }
}}; }*/
};
const unitsMenu = { const unitsMenu = {
'': {'title': 'Units'}, '': {'title': 'Units'},
@ -43,6 +51,9 @@
'Knots (spd)' : function() { setUnits(1.852,'knots'); }, 'Knots (spd)' : function() { setUnits(1.852,'knots'); },
'Mph (spd)' : function() { setUnits(1.60934,'mph'); }, 'Mph (spd)' : function() { setUnits(1.60934,'mph'); },
'm/s (spd)' : function() { setUnits(3.6,'m/s'); }, 'm/s (spd)' : function() { setUnits(3.6,'m/s'); },
'Km (dist)' : function() { setUnitsDist(1000,'km'); },
'Miles (dist)' : function() { setUnitsDist(1609.344,'miles'); },
'Nm (dist)' : function() { setUnitsDist(1852.001,'nm'); },
'Meters (alt)' : function() { setUnitsAlt(1,'m'); }, 'Meters (alt)' : function() { setUnitsAlt(1,'m'); },
'Feet (alt)' : function() { setUnitsAlt(0.3048,'feet'); } 'Feet (alt)' : function() { setUnitsAlt(0.3048,'feet'); }
}; };

View File

@ -1 +0,0 @@
00堽<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>]]]]]<5D><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>⺌]]]]]]]]<5D><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>WW亪<57>瑘乄W<E4B984><57><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>WW伂匙鬃超乄W<E4B984><57><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>⺈]<5D><EFBFBD>鬃琞W<E7909E><57><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>VV埑赚瓮维壮圴V<E59CB4><56><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>㑇V<E39187><56>现衔<E78EB0>琕V<E79095><56><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>㑇V<E39187><56>种滞<E7A78D>琕V<E79095><56><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>㑇V<E39187><56>现瓮<E78EB0>琕V<E79095><56><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>㑇V偝<56>屯屯<E5B1AF>俈V<E4BF88><56><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>VV<56>屯屯同琕V<E79095><56><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>㑇V]<5D>屯屯琞VV<56><56><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>VVV屯屯蚔VV<56><56><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>V2屯屯<E5B1AF>2V<32><56><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯<E5B1AF><E5B1AF><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯<E5B1AF><E5B1AF><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯<E5B1AF><E5B1AF><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯<E5B1AF><E5B1AF><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯⑼屯<E291BC><E5B1AF><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯峭屯屯屯屯<E5B1AF><E5B1AF><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯屯屯屯屯屯屯屯<E5B1AF><E5B1AF><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯屯屯屯屯屯屯屯屯<E5B1AF><E5B1AF><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯屯屯屯屯屯屯屯屯<E5B1AF><E5B1AF><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯威<E5B1AF><E5A881><EFBFBD><EFBFBD>屯屯屯屯屯屯屯屯屯屯威<E5B1AF><E5A881><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯<E5B1AF><E5B1AF><EFBFBD><EFBFBD>屯屯屯屯屯屯屯屯屯屯威<E5B1AF><E5A881><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯威<E5B1AF><E5A881><EFBFBD>屯屯屯屯屯屯屯屯屯屯威<E5B1AF><E5A881><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯屯威<E5B1AF>⑼屯屯屯屯屯屯屯屯屯屯威<E5B1AF><E5A881><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯屯屯牵屯屯屯屯屯屯屯屯屯屯威<E5B1AF><E5A881><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯屯颓屯屯屯屯屯屯屯屯屯屯威<E5B1AF><E5A881><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯屯屯峭屯屯屯屯屯屯屯屯屯屯威<E5B1AF><E5A881><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯屯屯屯屯屯屯屯屯屯屯屯屯威<E5B1AF><E5A881><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯屯屯屯屯屯屯屯屯屯屯屯屯威<E5B1AF><E5A881><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯屯屯屯屯屯屯屯屯屯屯屯威<E5B1AF><E5A881><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯屯屯屯屯屯屯屯屯屯屯屯威<E5B1AF><E5A881><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯屯屯屯屯屯屯屯屯屯屯屯威<E5B1AF><E5A881><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯屯屯屯屯屯屯屯屯屯屯威<E5B1AF><E5A881><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯屯屯屯屯屯屯屯屯屯屯威<E5B1AF><E5A881><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯屯屯屯屯屯屯屯屯屯威<E5B1AF><E5A881><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯屯屯屯屯屯屯屯屯屯<E5B1AF><E5B1AF><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯屯屯屯屯屯屯屯屯<E5B1AF><E5B1AF><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯屯屯屯屯屯屯屯威<E5B1AF><E5A881><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯屯屯屯屯屯威<E5B1AF><E5A881><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>

View File

@ -0,0 +1,63 @@
# Walkers Clock
A larg font watch, displays steps, can switch GPS on/off, displays grid reference
The watch works well with GPSsetup, the Activepedom or Widpedom
wdigets. A tiny GPS power widget is waiting in the wings for when
the v2.09 firware is released.
## Features
- Displays the time in large font
- Uses BTN1 to select modeline display (eg battery status or switch between setting when in a function mode
- Uses BTN3 to select the function mode (eg turn on/off GPS, or change GPS display)
- two function menus at present
GPS Power = On/Off
GPS Display = Grid | Speed Alt
when the modeline in CYAN use button BTN1 to switch between options
- Display the current steps if one of the steps widgets is installed
- Ensures that BTN2 requires a 1.5 second press in order to switch to the launcher
this is so you dont accidently switch out of the GPS/watch display with you coat sleeve
- Displays the timestamp of the last GPS fix when the GPS is on
- Buzzes when the GPS aquires a positional satellite fix
- Displays the current OS map grid reference in a large font
- Displays the age of the current GPS position fix in seconds
- Works in tandem with the GPS setup app so that you can reduce the power consumption of the GPS
## BTN1 cycles the information line
- By default the modeline is switched off
- Click BTN1 once and display your steps (if one of the step widgets is installed)
- Click BTN1 again and it will show battery % charge
- Click BTN1 again and it will switch the modeline off
## BTN2 Long press to start the launcher
BTN2 is confiured to respond to a 1.5 second press in order to switch
to the launcher App. Simply press and hold until you hear a buzz and
release. This avoids accidently switching out of the watch app when
clothing catches it.
## BTN3 cycles the function mode
- Click BTN3 once and the GPS ON / OFF menu is displayed
- If the GPS is OFF then pressing BTN1 will turn it ON
- If the GPS is ON then Clicking BTN1 will turn it OFF
When the GPS is ON a second function menu can be displayed by
pressing BTN3 again. This will show options to change the GPS display
on the second line of the watch.
- Grid - will display the GPS position converted to an OS Grid Ref
- Speed - will display the GPS speed inforation supplied in the last GPS fix
- Alt - will display the altitude information
![](gps_osref.jpg)
![](gps_speed.jpg)
![](gps_alt.jpg)
## Future Enhancements
* Ability to turn on the Heart Rate monitor
* Maybe a simple stopwatch capability
* Fix the screen flicker

522
apps/walkersclock/app.js Normal file
View File

@ -0,0 +1,522 @@
/*
* Walkers clock, hugh barney AT googlemail DOT com
*
* A clock that has the following features
* - displays the time in large font
* - uses BTN1 to select modeline display (eg battery status or switch between setting when in a function mode
* - uses BTN3 to select the function mode (eg turn on/off GPS, or change GPS display)
* - two function menus at present
* GPS Power = On/Off
* GPS Display = Grid | Speed Alt
* when the modeline in CYAN use button BTN1 to switch between options
* - display the current steps if one of the steps widgets is installed
* - ensures that BTN2 requires a 1.5 second press in order to switch to the launcher
* this is so you dont accidently switch out of the GPS/watch display with you coat sleeve
* - displays the timestamp of the last GPS fix when the GPS is on
* - buzzes when the GPS aquires a positional satellite fix
* - displays the current OS map grid reference in a large font
* - displays the age of the current GPS position fix in seconds
* - works in tandem with the GPS setup app so that you can reduce the power consumption of the GPS
*
*/
const INFO_NONE = "none";
const INFO_BATT = "batt";
const INFO_STEPS = "step";
const FN_MODE_OFF = "fn_mode_off";
const FN_MODE_GPS = "fn_mode_gps";
const FN_MODE_GDISP = "fn_mode_gdisp";
const GPS_OFF = "gps_off";
const GPS_TIME = "gps_time";
const GPS_SATS = "gps_sats";
const GPS_RUNNING = "gps_running";
const GDISP_OS = "g_osref";
const GDISP_SPEED = "g_speed";
const GDISP_ALT = "g_alt";
const Y_TIME = 40;
const Y_ACTIVITY = 120;
const Y_MODELINE = 200;
let gpsState = GPS_OFF;
let gpsPowerState = false;
let infoMode = INFO_NONE;
let functionMode = FN_MODE_OFF;
let gpsDisplay = GDISP_OS;
let last_steps = undefined;
let firstPress = 0;
let last_fix = {
fix: 0,
alt: 0,
lat: 0,
lon: 0,
speed: 0,
time: 0,
satellites: 0
};
function drawTime() {
var d = new Date();
var da = d.toString().split(" ");
var time = da[4].substr(0,5);
g.reset();
g.clearRect(0,24,239,239);
g.setColor(1,1,1); // white
g.setFontAlign(0, -1);
if (gpsState == GPS_SATS || gpsState == GPS_RUNNING) {
time = last_fix.time.toUTCString().split(" ");
time = time[4];
g.setFont("Vector", 56);
} else {
g.setFont("Vector", 80);
}
g.drawString(time, g.getWidth()/2, Y_TIME);
}
function drawSteps() {
g.setColor(0,255,0); // green
g.setFont("Vector", 60);
g.drawString(getSteps(), g.getWidth()/2, Y_ACTIVITY);
}
function drawActivity() {
if (!gpsPowerState) {
drawSteps();
return;
}
g.setFont("6x8", 3);
g.setColor(1,1,1);
g.setFontAlign(0, -1);
if (gpsState == GPS_TIME) {
g.drawString("Waiting for", g.getWidth()/2, Y_ACTIVITY);
g.drawString("GPS", g.getWidth()/2, Y_ACTIVITY + 30);
return;
}
if (gpsState == GPS_SATS) {
g.drawString("Satellites", g.getWidth()/2, Y_ACTIVITY);
g.drawString(last_fix.satellites, g.getWidth()/2, Y_ACTIVITY + 30);
return;
}
if (gpsState == GPS_RUNNING) {
//console.log("Draw GPS Running");
let time = formatTime(last_fix.time);
let age = timeSince(time);
let os = OsGridRef.latLongToOsGrid(last_fix);
let ref = to_map_ref(6, os.easting, os.northing);
let speed;
if (age < 0) age = 0;
g.setFontVector(40);
g.setColor(0xFFC0);
switch(gpsDisplay) {
case GDISP_OS:
g.drawString(ref, 120, Y_ACTIVITY, true);
break;
case GDISP_SPEED:
speed = last_fix.speed;
speed = speed.toFixed(1);
g.drawString(speed + "kph", 120, Y_ACTIVITY, true);
break;
case GDISP_ALT:
g.drawString(last_fix.alt + "m" , 120, Y_ACTIVITY, true);
break;
}
g.setFont("6x8",2);
g.setColor(1,1,1);
g.drawString(age, 120, Y_ACTIVITY + 46);
}
}
function onTick() {
if (!Bangle.isLCDOn())
return;
if (gpsPowerState) {
drawAll();
return;
}
if (last_steps != getSteps()) {
last_steps = getSteps();
drawAll();
return;
}
var t = new Date();
if (t.getSeconds() === 0 && !gpsPowerState) {
drawAll();
}
}
function drawAll(){
drawTime();
drawActivity(); // steps, hrt or gps
drawInfo();
}
function drawInfo() {
let val;
let str = "";
let col = 0x07E0; // green
//console.log("drawInfo(), infoMode=" + infoMode + " funcMode=" + functionMode);
switch(functionMode) {
case FN_MODE_OFF:
break;
case FN_MODE_GPS:
col = 0x07FF; // cyan
str = "GPS: " + (gpsPowerState ? "ON" : "OFF");
drawModeLine(str,col);
return;
case FN_MODE_GDISP:
col = 0x07FF; // cyan
switch(gpsDisplay) {
case GDISP_OS:
str = "GPS: Grid";
break;
case GDISP_SPEED:
str = "GPS: Speed";
break;
case GDISP_ALT:
str = "GPS: Alt";
break;
}
drawModeLine(str,col);
return;
}
switch(infoMode) {
case INFO_NONE:
col = 0x0000;
str = "";
break;
case INFO_STEPS:
str = "Steps: " + getSteps();
break;
case INFO_BATT:
default:
str = "Battery: " + E.getBattery() + "%";
}
drawModeLine(str,col);
}
function drawModeLine(str, col) {
g.setFont("6x8", 3);
g.setColor(col);
g.fillRect(0, Y_MODELINE - 3, 239, Y_MODELINE + 25);
g.setColor(0,0,0);
g.setFontAlign(0, -1);
g.drawString(str, g.getWidth()/2, Y_MODELINE);
}
function changeInfoMode() {
switch(functionMode) {
case FN_MODE_OFF:
break;
case FN_MODE_GPS:
gpsPowerState = !gpsPowerState;
Bangle.buzz();
Bangle.setGPSPower(gpsPowerState ? 1 : 0);
if (gpsPowerState) {
gpsState = GPS_TIME; // waiting first response so we can display time
Bangle.on('GPS', processFix);
} else {
Bangle.removeListener("GPS", processFix);
gpsState = GPS_OFF;
}
resetLastFix();
// poke the gps widget indicator to change
if (WIDGETS.gps !== undefined) {
WIDGETS.gps.draw();
}
functionMode = FN_MODE_OFF;
infoMode = INFO_NONE;
//drawInfo();
return;
case FN_MODE_GDISP:
switch (gpsDisplay) {
case GDISP_OS:
gpsDisplay = GDISP_SPEED;
break;
case GDISP_SPEED:
gpsDisplay = GDISP_ALT;
break;
case GDISP_ALT:
default:
gpsDisplay = GDISP_OS;
break;
}
}
switch(infoMode) {
case INFO_NONE:
if (stepsWidget() !== undefined)
infoMode = INFO_STEPS;
else
infoMode = INFO_BATT;
break;
case INFO_STEPS:
infoMode = INFO_BATT;
break;
case INFO_BATT:
default:
infoMode = INFO_NONE;
}
//drawInfo();
}
function changeFunctionMode() {
//console.log("changeFunctionMode()");
if (gpsState != GPS_RUNNING) {
switch(functionMode) {
case FN_MODE_OFF:
functionMode = FN_MODE_GPS;
break;
case FN_MODE_GPS:
default:
functionMode = FN_MODE_OFF;
break;
}
} else {
// if GPS is RUNNING then we want the GPS display options first
switch(functionMode) {
case FN_MODE_OFF:
functionMode = FN_MODE_GDISP;
break;
case FN_MODE_GDISP:
functionMode = FN_MODE_GPS;
break;
case FN_MODE_GPS:
default:
functionMode = FN_MODE_OFF;
break;
}
}
infoMode = INFO_NONE; // function mode overrides info mode
}
function resetLastFix() {
last_fix = {
fix: 0,
alt: 0,
lat: 0,
lon: 0,
speed: 0,
time: 0,
satellites: 0
};
}
function processFix(fix) {
last_fix.time = fix.time;
if (gpsState == GPS_TIME)
gpsState = GPS_SATS;
if (fix.fix) {
if (!last_fix.fix) Bangle.buzz(); // buzz on first position
gpsState = GPS_RUNNING;
last_fix = fix;
}
}
function getSteps() {
if (stepsWidget() !== undefined)
return stepsWidget().getSteps();
return "-";
}
function stepsWidget() {
if (WIDGETS.activepedom !== undefined) {
return WIDGETS.activepedom;
} else if (WIDGETS.wpedom !== undefined) {
return WIDGETS.wpedom;
}
return undefined;
}
/************* GPS / OSREF Code **************************/
function formatTime(now) {
var fd = now.toUTCString().split(" ");
return fd[4];
}
function timeSince(t) {
var hms = t.split(":");
var now = new Date();
var sn = 3600*(now.getHours()) + 60*(now.getMinutes()) + 1*(now.getSeconds());
var st = 3600*(hms[0]) + 60*(hms[1]) + 1*(hms[2]);
return (sn - st);
}
Number.prototype.toRad = function() { return this*Math.PI/180; };
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Ordnance Survey Grid Reference functions (c) Chris Veness 2005-2014 */
/* - www.movable-type.co.uk/scripts/gridref.js */
/* - www.movable-type.co.uk/scripts/latlon-gridref.html */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
function OsGridRef(easting, northing) {
this.easting = 0|easting;
this.northing = 0|northing;
}
OsGridRef.latLongToOsGrid = function(point) {
var lat = point.lat.toRad();
var lon = point.lon.toRad();
var a = 6377563.396, b = 6356256.909; // Airy 1830 major & minor semi-axes
var F0 = 0.9996012717; // NatGrid scale factor on central meridian
var lat0 = (49).toRad(), lon0 = (-2).toRad(); // NatGrid true origin is 49�N,2�W
var N0 = -100000, E0 = 400000; // northing & easting of true origin, metres
var e2 = 1 - (b*b)/(a*a); // eccentricity squared
var n = (a-b)/(a+b), n2 = n*n, n3 = n*n*n;
var cosLat = Math.cos(lat), sinLat = Math.sin(lat);
var nu = a*F0/Math.sqrt(1-e2*sinLat*sinLat); // transverse radius of curvature
var rho = a*F0*(1-e2)/Math.pow(1-e2*sinLat*sinLat, 1.5); // meridional radius of curvature
var eta2 = nu/rho-1;
var Ma = (1 + n + (5/4)*n2 + (5/4)*n3) * (lat-lat0);
var Mb = (3*n + 3*n*n + (21/8)*n3) * Math.sin(lat-lat0) * Math.cos(lat+lat0);
var Mc = ((15/8)*n2 + (15/8)*n3) * Math.sin(2*(lat-lat0)) * Math.cos(2*(lat+lat0));
var Md = (35/24)*n3 * Math.sin(3*(lat-lat0)) * Math.cos(3*(lat+lat0));
var M = b * F0 * (Ma - Mb + Mc - Md); // meridional arc
var cos3lat = cosLat*cosLat*cosLat;
var cos5lat = cos3lat*cosLat*cosLat;
var tan2lat = Math.tan(lat)*Math.tan(lat);
var tan4lat = tan2lat*tan2lat;
var I = M + N0;
var II = (nu/2)*sinLat*cosLat;
var III = (nu/24)*sinLat*cos3lat*(5-tan2lat+9*eta2);
var IIIA = (nu/720)*sinLat*cos5lat*(61-58*tan2lat+tan4lat);
var IV = nu*cosLat;
var V = (nu/6)*cos3lat*(nu/rho-tan2lat);
var VI = (nu/120) * cos5lat * (5 - 18*tan2lat + tan4lat + 14*eta2 - 58*tan2lat*eta2);
var dLon = lon-lon0;
var dLon2 = dLon*dLon, dLon3 = dLon2*dLon, dLon4 = dLon3*dLon, dLon5 = dLon4*dLon, dLon6 = dLon5*dLon;
var N = I + II*dLon2 + III*dLon4 + IIIA*dLon6;
var E = E0 + IV*dLon + V*dLon3 + VI*dLon5;
return new OsGridRef(E, N);
};
/*
* converts northing, easting to standard OS grid reference.
*
* [digits=10] - precision (10 digits = metres)
* to_map_ref(8, 651409, 313177); => 'TG 5140 1317'
* to_map_ref(0, 651409, 313177); => '651409,313177'
*
*/
function to_map_ref(digits, easting, northing) {
if (![ 0,2,4,6,8,10,12,14,16 ].includes(Number(digits))) throw new RangeError(`invalid precision '${digits}'`); // eslint-disable-line comma-spacing
let e = easting;
let n = northing;
// use digits = 0 to return numeric format (in metres) - note northing may be >= 1e7
if (digits == 0) {
const format = { useGrouping: false, minimumIntegerDigits: 6, maximumFractionDigits: 3 };
const ePad = e.toLocaleString('en', format);
const nPad = n.toLocaleString('en', format);
return `${ePad},${nPad}`;
}
// get the 100km-grid indices
const e100km = Math.floor(e / 100000), n100km = Math.floor(n / 100000);
// translate those into numeric equivalents of the grid letters
let l1 = (19 - n100km) - (19 - n100km) % 5 + Math.floor((e100km + 10) / 5);
let l2 = (19 - n100km) * 5 % 25 + e100km % 5;
// compensate for skipped 'I' and build grid letter-pairs
if (l1 > 7) l1++;
if (l2 > 7) l2++;
const letterPair = String.fromCharCode(l1 + 'A'.charCodeAt(0), l2 + 'A'.charCodeAt(0));
// strip 100km-grid indices from easting & northing, and reduce precision
e = Math.floor((e % 100000) / Math.pow(10, 5 - digits / 2));
n = Math.floor((n % 100000) / Math.pow(10, 5 - digits / 2));
// pad eastings & northings with leading zeros
e = e.toString().padStart(digits/2, '0');
n = n.toString().padStart(digits/2, '0');
return `${letterPair} ${e} ${n}`;
}
// start a timer and buzz whenn held long enough
function firstPressed() {
firstPress = getTime();
pressTimer = setInterval(longPressCheck, 1500);
}
// if you release too soon there is no buzz as timer is cleared
function thenReleased() {
var dur = getTime() - firstPress;
if (pressTimer) {
clearInterval(pressTimer);
pressTimer = undefined;
}
if ( dur >= 1.5 ) Bangle.showLauncher();
}
// when you feel the buzzer you know you have done a long press
function longPressCheck() {
Bangle.buzz();
if (pressTimer) {
clearInterval(pressTimer);
pressTimer = undefined;
}
}
var pressTimer;
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
drawAll();
Bangle.on('lcdPower',function(on) {
functionMode = FN_MODE_OFF;
infoMode = INFO_NONE;
if (on) drawAll();
});
var click = setInterval(onTick, 5000);
setWatch(() => { changeInfoMode(); drawAll(); }, BTN1, {repeat: true});
setWatch(() => { changeFunctionMode(); drawAll(); }, BTN3, {repeat: true});
// make BTN require a long press (1.5 seconds) to switch to launcher
setWatch(firstPressed, BTN2,{repeat:true,edge:"rising"});
setWatch(thenReleased, BTN2,{repeat:true,edge:"falling"});

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwxH+AH4A/AEcsAAYuuGFIqEF9gFJF/62SACAvvGCgYEwIASqwxVF4mmACQvEGCJcCF65iDF/4vbqwvVd4YeBJgIECAAulq4KDmS/XAAVXJwgvHOAIKCOoQuTGQ1WvYvCvYAGBQdWLwYtUMQwAQGAQuYXgJPCmQEBAAwKDCQKNZlmsAAKECAoQAEX4QQESKh4EDwVXegIvPSKgbBDwMs64ABAoOBAoQAE1gvGTAQwQCINXF4olBFw4KCAAYvCDIQvZABgv/F/4vqCoYvpGAMyF7ICBF6QADEIeBABQuCF4QACFyAwFDwRlCABNWHoUyFyoxEwOIxGBPoIAJMYYvCFyrzEGAIALFwS7TYRYACmQAGSxAtcACaNWwGArizBAwIAININWrgTCF6+GAAYvCA4gACwAKGCQIv/F+qqBAAQvCA4gACXAIKFF67cBAAQvCAwYKFmQGFF6wAZF6YxaFyo1HIBYpcAH4A/AAoA=="))

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

2
apps/widgps/ChangeLog Normal file
View File

@ -0,0 +1,2 @@
0.01: First version

10
apps/widgps/README.md Normal file
View File

@ -0,0 +1,10 @@
# GPS Power Status Widget
A simple widget that shows the on/off status of the GPS.
The GPS can quickly run the battery down if it is on all the time so
it is useful to know if it has been switched on or not.
- Uses Bangle.isGPSOn(), requires firmware v2.08.167 or later
- Shows in grey when the GPS is off
- Shows in amber when the GPS is on

28
apps/widgps/widget.js Normal file
View File

@ -0,0 +1,28 @@
(function(){
var img = E.toArrayBuffer(atob("GBiBAAAAAAAAAAAAAA//8B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+A//8AAAAAAAAAAAAA=="));
function draw() {
g.reset();
if (Bangle.isGPSOn()) {
g.setColor(1,0.8,0); // on = amber
} else {
g.setColor(0.3,0.3,0.3); // off = grey
}
g.drawImage(img, 10+this.x, 2+this.y);
}
var timerInterval;
Bangle.on('lcdPower', function(on) {
if (on) {
WIDGETS.gps.draw();
if (!timerInterval) timerInterval = setInterval(()=>WIDGETS["gps"].draw(), 2000);
} else {
if (timerInterval) {
clearInterval(timerInterval);
timerInterval = undefined;
}
}
});
WIDGETS.gps={area:"tr",width:24,draw:draw};
})();

BIN
apps/widgps/widget.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

2
apps/widhrt/ChangeLog Normal file
View File

@ -0,0 +1,2 @@
0.01: First version

8
apps/widhrt/README.md Normal file
View File

@ -0,0 +1,8 @@
# Heart Rate Power Monitor Widget
A simple widget that shows the on/off status of the Heart Rate
Monitor.
- Uses Bangle.isHRTOn(). Requires firmware v2.08.167 or later.
- Shows in grey when the HRT is off
- Shows in red when the HRT is on

28
apps/widhrt/widget.js Normal file
View File

@ -0,0 +1,28 @@
(function(){
var img = E.toArrayBuffer(atob("FhaBAAAAAAAAAAAAAcDgD8/AYeGDAwMMDAwwADDAAMOABwYAGAwAwBgGADAwAGGAAMwAAeAAAwAAAAAAAAAAAAA="));
function draw() {
g.reset();
if (Bangle.isHRMOn()) {
g.setColor(1,0,0); // on = red
} else {
g.setColor(0.3,0.3,0.3); // off = grey
}
g.drawImage(img, 10+this.x, 2+this.y);
}
var timerInterval;
Bangle.on('lcdPower', function(on) {
if (on) {
WIDGETS.widhrt.draw();
if (!timerInterval) timerInterval = setInterval(()=>WIDGETS["widhrt"].draw(), 2000);
} else {
if (timerInterval) {
clearInterval(timerInterval);
timerInterval = undefined;
}
}
});
WIDGETS.widhrt={area:"tr",width:24,draw:draw};
})();

BIN
apps/widhrt/widget.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 848 B