76
apps.json
|
@ -80,7 +80,7 @@
|
|||
"name": "Notifications (default)",
|
||||
"shortName":"Notifications",
|
||||
"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",
|
||||
"tags": "widget",
|
||||
"type": "notify",
|
||||
|
@ -93,7 +93,7 @@
|
|||
"name": "Fullscreen Notifications",
|
||||
"shortName":"Notifications",
|
||||
"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.",
|
||||
"tags": "widget",
|
||||
"type": "notify",
|
||||
|
@ -139,7 +139,7 @@
|
|||
{ "id": "gbridge",
|
||||
"name": "Gadgetbridge",
|
||||
"icon": "app.png",
|
||||
"version":"0.18",
|
||||
"version":"0.20",
|
||||
"description": "The default notification handler for Gadgetbridge notifications from Android",
|
||||
"tags": "tool,system,android,widget",
|
||||
"readme": "README.md",
|
||||
|
@ -1492,7 +1492,7 @@
|
|||
"name": "Digital Assistant, not EDITH",
|
||||
"shortName": "DANE",
|
||||
"icon": "app.png",
|
||||
"version": "0.15",
|
||||
"version": "0.16",
|
||||
"description": "A Watchface inspired by Tony Stark's EDITH and based on https://arwes.dev/",
|
||||
"tags": "clock",
|
||||
"type": "clock",
|
||||
|
@ -2139,7 +2139,7 @@
|
|||
{ "id": "multiclock",
|
||||
"name": "Multi Clock",
|
||||
"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",
|
||||
"readme": "README.md",
|
||||
"tags": "clock",
|
||||
|
@ -2153,8 +2153,6 @@
|
|||
{"name":"txt.face.js","url":"txt.js"},
|
||||
{"name":"timdat.face.js","url":"timdat.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}
|
||||
]
|
||||
},
|
||||
|
@ -2639,11 +2637,11 @@
|
|||
]
|
||||
},
|
||||
{ "id": "speedalt",
|
||||
"name": "GPS Speedo and Altimeter",
|
||||
"shortName":"GPS Speed Alt",
|
||||
"name": "GPS Adventure Sports",
|
||||
"shortName":"GPS Adv Sport",
|
||||
"icon": "app.png",
|
||||
"version":"0.07",
|
||||
"description": "GPS speed and altitude display. Designed for easy viewing and use during outdoor activities such as para-gliding, hang-gliding, sailing, cycling etc.",
|
||||
"version":"1.00",
|
||||
"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",
|
||||
"type":"app",
|
||||
"allow_emulator":true,
|
||||
|
@ -2758,6 +2756,7 @@
|
|||
"name": "Test User Input",
|
||||
"shortName":"Test User Input",
|
||||
"icon": "app.png",
|
||||
"readme": "README.md",
|
||||
"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.",
|
||||
"tags": "input,interface,buttons,touch",
|
||||
|
@ -2765,6 +2764,59 @@
|
|||
{"name":"testuserinput.app.js","url":"app.js"},
|
||||
{"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"}
|
||||
]
|
||||
}
|
||||
|
||||
]
|
||||
|
|
|
@ -10,4 +10,5 @@
|
|||
0.12: Move code to Arwes Module
|
||||
0.13: Improve icon
|
||||
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
|
|
@ -67,7 +67,7 @@ function levelColor(l) {
|
|||
|
||||
function drawBattery() {
|
||||
const l = E.getBattery(), c = levelColor(l);
|
||||
count = l;
|
||||
// count = l;
|
||||
const xl = 45 + l * (194 - 46) / 100;
|
||||
g.clearRect(46, 58 + 80 + yOffset + 37, 193, 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(function () {
|
||||
// count++;
|
||||
// drawCounterText();
|
||||
// }, BTN1, {repeat: true, edge: "falling"});
|
||||
// setWatch(function () {
|
||||
// count--;
|
||||
// drawCounterText();
|
||||
// }, BTN3, {repeat: true, edge: "falling"});
|
||||
setWatch(function () {
|
||||
count++;
|
||||
drawCounterText();
|
||||
}, BTN1, {repeat: true, edge: "falling"});
|
||||
setWatch(function () {
|
||||
count--;
|
||||
drawCounterText();
|
||||
}, BTN3, {repeat: true, edge: "falling"});
|
||||
|
||||
// refesh every 100 milliseconds
|
||||
setInterval(updateClock, 500);
|
||||
|
|
|
@ -18,3 +18,5 @@
|
|||
Nicer display of alarm clock notifications
|
||||
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.19: Support for call incoming/start/end
|
||||
0.20: Reduce memory usage
|
||||
|
|
|
@ -2,20 +2,18 @@
|
|||
// Music handling
|
||||
const state = {
|
||||
music: "stop",
|
||||
|
||||
musicInfo: {
|
||||
artist: "",
|
||||
album: "",
|
||||
track: ""
|
||||
},
|
||||
|
||||
scrollPos: 0
|
||||
};
|
||||
// activity reporting
|
||||
var currentSteps = 0, lastSentSteps=0;
|
||||
var activityInterval;
|
||||
var hrmTimeout;
|
||||
|
||||
|
||||
function settings() {
|
||||
let settings = require('Storage').readJSON("gbridge.json", true) || {};
|
||||
if (!("showIcon" in settings)) {
|
||||
|
@ -43,14 +41,6 @@
|
|||
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){
|
||||
if (state.music === "play") {
|
||||
|
@ -117,38 +107,7 @@
|
|||
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) {
|
||||
var s = settings();
|
||||
// handle setting activity interval
|
||||
|
@ -193,19 +152,52 @@
|
|||
switch (event.t) {
|
||||
case "notify":
|
||||
case "notify-":
|
||||
handleNotificationEvent(event);
|
||||
if (event.t === "notify") {
|
||||
require("notify").show(prettifyNotificationEvent(event));
|
||||
Bangle.buzz();
|
||||
} else { // notify-
|
||||
require("notify").hide(event);
|
||||
}
|
||||
break;
|
||||
case "musicinfo":
|
||||
handleMusicInfoUpdate(event);
|
||||
state.musicInfo = event;
|
||||
updateMusic({on: false});
|
||||
break;
|
||||
case "musicstate":
|
||||
handleMusicStateUpdate(event);
|
||||
if (state.music !== event.state) {
|
||||
state.music = event.state
|
||||
updateMusic({on: true});
|
||||
}
|
||||
break;
|
||||
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;
|
||||
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;
|
||||
case "act":
|
||||
handleActivityEvent(event);
|
||||
|
@ -251,7 +243,7 @@
|
|||
function sendBattery() {
|
||||
gbSend({ t: "status", bat: E.getBattery() });
|
||||
}
|
||||
|
||||
|
||||
// Send a summary of activity to Gadgetbridge
|
||||
function sendActivity(hrm) {
|
||||
var steps = currentSteps - lastSentSteps;
|
||||
|
@ -279,7 +271,7 @@
|
|||
}
|
||||
});
|
||||
handleActivityEvent({}); // kicks off activity reporting
|
||||
|
||||
|
||||
// Finally add widget
|
||||
WIDGETS["gbridgew"] = {area: "tl", width: 24, draw: draw, reload: reload};
|
||||
reload();
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
0.01: First version of GPS Setup app
|
||||
0.02: Created gppsetup module
|
|
@ -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)
|
|
@ -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();
|
|
@ -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();
|
||||
}
|
||||
};
|
After Width: | Height: | Size: 2.6 KiB |
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/ACmsAAIss1mBwIfZqwABFpwuYC4IADGBYuaDQwuDF5ouYLo4vJIYousCYQOIHJIuUCo4uIHJIuUf4wlGEIQaHFywvHGAguiF5GBEIgbHFzAvJDwwuRvV6F6xLJFxmkAAQuKqwtKPQ4RKFwgwICgobHVRJAJFQOr0owIE5AtNDIYRI0ulGBAuNapYABCRGkGBAuGAoIpNGBIIFGBF6FwuBFygAKGBAulGBgujGBWAF0jpBehIvlqwws1jnCGFOs1heBGFQuBFoYwoFxFWwAwjFw+BAAIMBGEIuMGEIuIeQIQFGDouJ1gSHGDYuSGDYuUGDSzBFYP+dAQuNGBN6F6RcBFyAwHwAvQGAYuSGAheCF54wCAAYuRGAQABwGAC6QuWGAV6FyYA/AH4A2A="))
|
|
@ -0,0 +1,4 @@
|
|||
(function(back) {
|
||||
// just go right to our app
|
||||
load("gpssetup.app.js");
|
||||
})();
|
|
@ -0,0 +1 @@
|
|||
{"power_mode":"SuperE", "update":120, "search":6}
|
|
@ -8,3 +8,4 @@
|
|||
Ensure 'on' is always supplied for translations
|
||||
0.07: Improve handling of non-ASCII characters (fix #469)
|
||||
0.08: Added Mavigation units and en_NAV
|
||||
0.09: Added New Zealand en_NZ
|
||||
|
|
|
@ -245,6 +245,24 @@ var locales = {
|
|||
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" }
|
||||
},
|
||||
"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": {
|
||||
lang: "en_AU",
|
||||
decimal_point: ".",
|
||||
|
|
|
@ -9,3 +9,4 @@
|
|||
0.09: Added Pedometer clock
|
||||
0.10: Added GPS and Grid Ref clock faces
|
||||
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
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
{"name":"digi.face.js","url":"digi.min.js"},
|
||||
{"name":"txt.face.js","url":"txt.min.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}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
|
||||
})();
|
|
@ -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;
|
||||
|
||||
})();
|
|
@ -2,3 +2,4 @@
|
|||
0.02: Add notification ID option
|
||||
0.03: Pass `area{x,y,w,h}` to render callback instead of just `y`
|
||||
0.05: Adjust position of notification src text
|
||||
0.06: Support background color
|
||||
|
|
|
@ -41,7 +41,9 @@ function fitWords(text,rows,width) {
|
|||
src : string // optional source name
|
||||
body : string // optional body text
|
||||
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
|
||||
g.setClipRect(x,y, r,b);
|
||||
// clear area
|
||||
g.setColor(0).fillRect(x,y, r,b);
|
||||
g.setColor(options.bgColor||0).fillRect(x,y, r,b);
|
||||
// bottom border
|
||||
g.setColor(0x39C7).fillRect(0,b-1, r,b);
|
||||
b -= 2;h -= 2;
|
||||
// title bar
|
||||
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;
|
||||
g.setColor(-1).setFontAlign(-1, -1, 0).setFont("6x8", 2);
|
||||
g.drawString(title.trim().substring(0, 13), x+25,y+3);
|
||||
|
|
|
@ -4,3 +4,4 @@
|
|||
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.06: Adjust position of notification src text and notifications without title
|
||||
0.07: Support background color
|
||||
|
|
|
@ -37,7 +37,9 @@ function fitWords(text,rows,width) {
|
|||
src : string // optional source name
|
||||
body : string // optional body text
|
||||
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) {
|
||||
|
@ -53,11 +55,11 @@ exports.show = function(options) {
|
|||
w = 240,
|
||||
h = size;
|
||||
// clear screen
|
||||
g.clear(1);
|
||||
g.setColor(options.bgColor||0).fillRect(0,0,g.getWidth(),g.getHeight());
|
||||
// top bar
|
||||
if (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.drawString(title.trim().substring(0, 13), x+5, y+3);
|
||||
if (options.title && options.src) {
|
||||
|
|
|
@ -5,3 +5,4 @@
|
|||
0.05: Add setting to turn vibrate on/off.
|
||||
0.06: Tweaks to vibration settings.
|
||||
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.
|
|
@ -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.
|
||||
|
||||
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
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
BTN1 : Select next waypoint. Last fix distance from selected waypoint is displayed.
|
||||
|
||||
### Both modes
|
||||
|
||||
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>
|
||||
<p>
|
||||
Left tap swaps displays:<br>
|
||||
<p>
|
||||
Distance to waypoint DeltaW:<br>
|
||||
<p>
|
||||
MAX Values instead:<br>
|
||||
<p>
|
||||
Settings:<br>
|
||||
<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!
|
||||
|
||||
( 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.
|
||||
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
require("heatshrink").decompress(atob("m02xH+AH4AJlgAMGWQ4lEw1Wq2BAAgHBG8QhFq+C1nXABGswQ4GGjhkBGJIAHOQI2ZDIcyGaQ3EmQ3WNAiaKABusNyoUDNCxuGGySdDNDBuGUoY0e2etAAWzGzoOCGheJxNlAA2J1o2LGprTNGRAAFbZw0LqwZI1pkGAAgJDUxdWGxTULFAmJFJGzURjaET6Q0DGZKYIrqjRT5Q0EGZwpEUZZqPaYaRMGg6LINhBJKNK40JAAI1Iq6fJGjDrIUQoVCwRqJDhA0OXYQRGwRsEAgWsNTCeIDYTwG1g1GqygJNROsq0ymVWq7TI2ZRJUQg1JUARkMAAg0FDhY1GDAxOKQwgAEKI6IKKAQ1KeAQ1IJ4VXAog1gC4Q1ImQVBAwYFBmQ1K1o1fMoQ1ORIQ1Ua5ahD1uzrqnEULo1L1gVBAAusGrDxHC4NlEY4aDAAZQGDhiHCGpZOJNodWmUyqxpIRBY1GQw7wCURAAPKIWtXhI1EwSFJNhIAMNQSgHwQ1EGwVXKBJsWDJSgENgjyKGyg0CNQ/XNQo1DwIRGbIS+HABYWLwI1GGwVWKhZtQChigGNhg2TCRhqIGwcy1g2YCBmsmQ0INgajIYgY1PdRKfCGpCjMG4ShNBxSfKGwyjIADOsGho1DbRI0YagQ1MG0Y0RGwjbLACLTDGh42FqxuY1lWGig2FmRuWwKdDGiZuHG6WBNC42JHAWCVBWswQyEGjQ3IHAJyBAAgHBCAwzbG5QAMGb44SGUgAkA"))
|
||||
require("heatshrink").decompress(atob("mEwxH+AH4A/AE+sFtoABF12swItsF9QuFR4IwmFwwvnFw4vCGEYuIF4JgjFxIvkFxQvCGBfOAAQvqFwYwRFxYvDGBIvUFxgv/F6IuNF4n+0nB4TvXFxwvF4XBAALlPF7ZfBGC4uPF4rABGAYAGTQwvad4YwKFzYvIGBQvfFwgAE3Qvt4IvEFzgvCLxO7Lx7vULzIzTFwIvgGZheFRAiNRGSQvpGYouesYAGmQAKq3CE4PIC4wviq2eFwPCroveCRSGEC6Qv0DAwRLcoouWC4VdVYQXkr1eAgVdAoIABroNEB4gHHC5QvHwQSDAAOCA74vH1uICQIABxGtA74vIAEwv/F/4vXAH4A/AHY"))
|
||||
|
|
|
@ -1,110 +1,100 @@
|
|||
/*
|
||||
Speed and Altitude [speedalt]
|
||||
Ver : 0.07
|
||||
Mike Bennett mike[at]kereru.com
|
||||
1.16 : Use new GPS settings module
|
||||
*/
|
||||
|
||||
const dbg = 0;
|
||||
|
||||
var v = '1.20';
|
||||
var buf = Graphics.createArrayBuffer(240,160,2,{msb:true});
|
||||
|
||||
// Load fonts
|
||||
require("Font7x11Numeric7Seg").add(Graphics);
|
||||
|
||||
/*
|
||||
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 lf = {fix:0,satellites:0};
|
||||
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 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 tmrLP; // Timer for delay in switching to low power after screen turns off
|
||||
|
||||
var max = {};
|
||||
max.spd = 0;
|
||||
max.alt = 0;
|
||||
|
||||
var emulator = 0;
|
||||
if (process.env.BOARD=="EMSCRIPTEN") emulator = 1; // 1 = running in emulator. Supplies test values;
|
||||
var emulator = (process.env.BOARD=="EMSCRIPTEN")?1:0; // 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;
|
||||
|
||||
buf.clear();
|
||||
|
||||
var val = '';
|
||||
|
||||
var v = '';
|
||||
var u='';
|
||||
|
||||
// Primary Display
|
||||
val = speed.toString();
|
||||
if ( !showSpeed ) val = alt.toString();
|
||||
v = (cfg.primSpd)?dat.speed.toString():dat.alt.toString();
|
||||
|
||||
// Primary Units
|
||||
u = settings.spd_unit;
|
||||
if ( !showSpeed ) u = alt_units;
|
||||
u = (cfg.primSpd)?cfg.spd_unit:dat.alt_units;
|
||||
|
||||
drawPrimary(val,u);
|
||||
drawPrimary(v,u);
|
||||
|
||||
// Secondary Display
|
||||
val = alt.toString();
|
||||
if ( !showSpeed ) val = speed.toString();
|
||||
v = (cfg.primSpd)?dat.alt.toString():dat.speed.toString();
|
||||
|
||||
// Secondary Units
|
||||
u = alt_units;
|
||||
if ( !showSpeed ) u = settings.spd_unit;
|
||||
u = (cfg.primSpd)?dat.alt_units:cfg.spd_unit;
|
||||
|
||||
drawSecondary(val,u);
|
||||
drawSecondary(v,u);
|
||||
|
||||
// Time
|
||||
drawTime();
|
||||
|
||||
// Waypoint name
|
||||
drawWP();
|
||||
|
||||
//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.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
|
||||
|
||||
var s=40; // Font size
|
||||
if ( n.length <= 7 ) s=48;
|
||||
if ( n.length <= 6 ) s=55;
|
||||
if ( n.length <= 5 ) s=68;
|
||||
if ( n.length <= 4 ) s=85;
|
||||
if ( n.length <= 3 ) s=110;
|
||||
var l=n.length;
|
||||
|
||||
if ( l <= 7 ) s=48;
|
||||
if ( l <= 6 ) s=55;
|
||||
if ( l <= 5 ) s=68;
|
||||
if ( l <= 4 ) s=85;
|
||||
if ( l <= 3 ) s=110;
|
||||
|
||||
buf.setFontAlign(0,-1); //Centre
|
||||
buf.setColor(1);
|
||||
|
||||
buf.setFontVector(s);
|
||||
buf.drawString(n,110,0);
|
||||
|
||||
|
@ -135,11 +126,12 @@ function drawPrimary(n,u) {
|
|||
function drawSecondary(n,u) {
|
||||
|
||||
var s=180; // units X position
|
||||
if ( n.length <= 5 ) s=155;
|
||||
if ( n.length <= 4 ) s=125;
|
||||
if ( n.length <= 3 ) s=100;
|
||||
if ( n.length <= 2 ) s=65;
|
||||
if ( n.length <= 1 ) s=35;
|
||||
var l=n.length;
|
||||
if ( l <= 5 ) s=155;
|
||||
if ( l <= 4 ) s=125;
|
||||
if ( l <= 3 ) s=100;
|
||||
if ( l <= 2 ) s=65;
|
||||
if ( l <= 1 ) s=35;
|
||||
|
||||
buf.setFontAlign(-1,1); //left, bottom
|
||||
buf.setColor(1);
|
||||
|
@ -153,7 +145,6 @@ function drawSecondary(n,u) {
|
|||
buf.drawString(u,s,135);
|
||||
}
|
||||
|
||||
|
||||
function drawTime() {
|
||||
var x = 0;
|
||||
var y = 160;
|
||||
|
@ -168,181 +159,225 @@ function drawTime() {
|
|||
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) {
|
||||
buf.setFontAlign(1,1); //right, bottom
|
||||
|
||||
buf.setColor(3);
|
||||
buf.setFont("6x8", 2);
|
||||
if ( showMax ) {
|
||||
buf.setColor(2);
|
||||
buf.drawString("MAX",240,160);
|
||||
buf.setFontAlign(1,1); //right, bottom
|
||||
buf.drawString(sats,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) {
|
||||
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;
|
||||
|
||||
if (fix.fix || emulator) {
|
||||
doBuzz(1);
|
||||
var sp = '---';
|
||||
var al = '---';
|
||||
var di = '---';
|
||||
var age = '---';
|
||||
|
||||
//==== Speed ====
|
||||
if ( settings.spd == 0 ) {
|
||||
var strSpeed = require("locale").speed(fix.speed);
|
||||
m = strSpeed.match(/([0-9,\.]+)(.*)/); // regex splits numbers from units
|
||||
|
||||
if ( emulator ) {
|
||||
speed = '125'; //testing only
|
||||
settings.spd_unit = 'kph';
|
||||
}
|
||||
else {
|
||||
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);
|
||||
if (fix.fix) lf = fix;
|
||||
if (lf.fix) {
|
||||
|
||||
// Speed
|
||||
if ( cfg.spd == 0 ) {
|
||||
m = require("locale").speed(lf.speed).match(/([0-9,\.]+)(.*)/); // regex splits numbers from units
|
||||
sp = parseFloat(m[1]);
|
||||
cfg.spd_unit = m[2];
|
||||
}
|
||||
else sp = parseFloat(lf.speed)/parseFloat(cfg.spd); // Calculate for selected units
|
||||
|
||||
// ==== Altitude ====
|
||||
alt = fix.alt;
|
||||
if ( emulator ) alt = '360';
|
||||
alt = Math.round(parseFloat(alt)/parseFloat(settings.alt),0);
|
||||
|
||||
// Record max values
|
||||
if (parseFloat(speed) > parseFloat(max.spd) ) max.spd = parseFloat(speed);
|
||||
if (parseFloat(alt) > parseFloat(max.alt) ) max.alt = parseFloat(alt);
|
||||
|
||||
if ( showMax ) drawFix(max.spd,settings.spd_unit,fix.satellites,max.alt,settings.alt_unit);
|
||||
else drawFix(speed,settings.spd_unit,fix.satellites,alt,settings.alt_unit);
|
||||
if ( sp < 10 ) sp = sp.toFixed(1);
|
||||
else sp = Math.round(sp);
|
||||
if (parseFloat(sp) > parseFloat(max.spd) ) max.spd = parseFloat(sp);
|
||||
|
||||
} else {
|
||||
doBuzz(0);
|
||||
drawNoFix(fix.satellites);
|
||||
// Altitude
|
||||
al = lf.alt;
|
||||
al = Math.round(parseFloat(al)/parseFloat(cfg.alt));
|
||||
if (parseFloat(al) > parseFloat(max.alt) ) max.alt = parseFloat(al);
|
||||
|
||||
// Distance to waypoint
|
||||
di = distance(lf,wp);
|
||||
if (isNaN(di)) di = 0;
|
||||
|
||||
// Age of last fix (secs)
|
||||
age = Math.max(0,Math.round(getTime())-(lf.time.getTime()/1000));
|
||||
}
|
||||
|
||||
if ( cfg.modeA ) {
|
||||
if ( showMax )
|
||||
drawFix({
|
||||
speed:max.spd,
|
||||
sats:lf.satellites,
|
||||
alt:max.alt,
|
||||
alt_units:cfg.alt_unit,
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Vibrate watch when fix lost or gained.
|
||||
function doBuzz(hasFix) {
|
||||
|
||||
// nothing to do
|
||||
if ( lastBuzz === hasFix || !settings.buzz ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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(){
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});
|
||||
|
||||
// Switch between fix and max display on short press or reset max values on long press
|
||||
setWatch(maxPressed, BTN1,{repeat:true,edge:"rising"});
|
||||
setWatch(maxReleased, BTN1,{repeat:true,edge:"falling"});
|
||||
|
||||
// Touch screen to toggle display
|
||||
setWatch(toggleDisplay, BTN4, {repeat:true,edge:"falling"});
|
||||
setWatch(toggleDisplay, BTN5, {repeat:true,edge:"falling"});
|
||||
// Spd+Dist : Select next waypoint
|
||||
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});
|
||||
|
||||
// Power saving on/off
|
||||
setWatch(function(e){
|
||||
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"});
|
||||
|
||||
}
|
||||
|
||||
function maxPressed() {
|
||||
maxPress = getTime();
|
||||
}
|
||||
|
||||
function maxReleased() {
|
||||
var dur = getTime()-maxPress;
|
||||
// Toggle between alt or dist
|
||||
setWatch(function(e){
|
||||
cfg.modeA = !cfg.modeA;
|
||||
savSettings();
|
||||
onGPS(lf);
|
||||
}, BTN3, {repeat:true,edge:"falling"});
|
||||
|
||||
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
|
||||
}
|
||||
// Touch left screen to toggle display
|
||||
setWatch(function(e){
|
||||
cfg.primSpd = !cfg.primSpd;
|
||||
savSettings();
|
||||
onGPS(lf); // Update display
|
||||
}, BTN4, {repeat:true,edge:"falling"});
|
||||
|
||||
}
|
||||
|
||||
function updateClock() {
|
||||
if ( dbg ) print('Updating clock');
|
||||
if (!canDraw) return;
|
||||
|
||||
drawTime();
|
||||
g.reset();
|
||||
g.drawImage(img,0,40);
|
||||
// g.flip();
|
||||
|
||||
// Something different to display in the emulator
|
||||
if ( emulator ) {
|
||||
max.spd++;
|
||||
max.alt++;
|
||||
}
|
||||
|
||||
if ( emulator ) {max.spd++;max.alt++;}
|
||||
}
|
||||
|
||||
function startDraw(){
|
||||
canDraw=true;
|
||||
setLpMode('SuperE'); // off
|
||||
g.clear();
|
||||
Bangle.drawWidgets();
|
||||
onGPS(lastFix); // draw app screen
|
||||
onGPS(lf); // draw app screen
|
||||
}
|
||||
|
||||
function stopDraw() {
|
||||
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.
|
||||
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
|
||||
settings.spd_unit = settings.spd_unit||''; // Displayed speed unit
|
||||
settings.alt = settings.alt||0.3048;// Multiplier for altitude unit conversions.
|
||||
settings.alt_unit = settings.alt_unit||'feet'; // Displayed altitude units
|
||||
settings.colour = settings.colour||0; // Colour scheme.
|
||||
settings.buzz = settings.buzz||0; // Buzz when fix lost or gained.
|
||||
cfg.spd = cfg.spd||0; // Multiplier for speed unit conversions. 0 = use the locale values for speed
|
||||
cfg.spd_unit = cfg.spd_unit||''; // Displayed speed unit
|
||||
cfg.alt = cfg.alt||0.3048;// Multiplier for altitude unit conversions.
|
||||
cfg.alt_unit = cfg.alt_unit||'feet'; // Displayed altitude units
|
||||
cfg.dist = cfg.dist||1000;// Multiplier for distnce unit conversions.
|
||||
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
|
||||
|
@ -359,50 +394,43 @@ var img = {
|
|||
palette:new Uint16Array([0,0x4FE0,0xEFE0,0x07DB])
|
||||
};
|
||||
|
||||
if ( settings.colour == 1 ) img.palette = new Uint16Array([0,0xFFFF,0xFFFF,0xFFFF]);
|
||||
if ( settings.colour == 2 ) img.palette = new Uint16Array([0,0xFF800,0xF800,0xF800]);
|
||||
|
||||
|
||||
// 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];
|
||||
}
|
||||
if ( cfg.colour == 1 ) img.palette = new Uint16Array([0,0xFFFF,0xFFF6,0xDFFF]);
|
||||
if ( cfg.colour == 2 ) img.palette = new Uint16Array([0,0xFF800,0xFAE0,0xF813]);
|
||||
|
||||
var SCREENACCESS = {
|
||||
withApp:true,
|
||||
request:function(){
|
||||
this.withApp=false;
|
||||
stopDraw();
|
||||
clearWatch();
|
||||
},
|
||||
release:function(){
|
||||
this.withApp=true;
|
||||
startDraw();
|
||||
setButtons();
|
||||
}
|
||||
request:function(){this.withApp=false;stopDraw();},
|
||||
release:function(){this.withApp=true;startDraw();}
|
||||
};
|
||||
|
||||
Bangle.on('lcdPower',function(on) {
|
||||
if (!SCREENACCESS.withApp) return;
|
||||
if (on) {
|
||||
startDraw();
|
||||
} else {
|
||||
stopDraw();
|
||||
}
|
||||
if (on) startDraw();
|
||||
else stopDraw();
|
||||
});
|
||||
|
||||
var gpssetup;
|
||||
try {
|
||||
gpssetup = require("gpssetup");
|
||||
} catch(e) {
|
||||
gpssetup = false;
|
||||
}
|
||||
|
||||
// All set up. Lets go.
|
||||
g.clear();
|
||||
Bangle.setLCDBrightness(1);
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
onGPS(lf);
|
||||
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);
|
||||
|
||||
setButtons();
|
||||
setInterval(updateClock, 30000);
|
||||
|
||||
|
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 4.3 KiB |
|
@ -19,6 +19,12 @@
|
|||
writeSettings();
|
||||
}
|
||||
|
||||
function setUnitsDist(d,u) {
|
||||
settings.dist = d;
|
||||
settings.dist_unit = u;
|
||||
writeSettings();
|
||||
}
|
||||
|
||||
function setColour(c) {
|
||||
settings.colour = c;
|
||||
writeSettings();
|
||||
|
@ -27,13 +33,15 @@
|
|||
const appMenu = {
|
||||
'': {'title': 'GPS Speed Alt'},
|
||||
'< Back': back,
|
||||
'< Load GPS Adv Sport': ()=>{load('speedalt.app.js');},
|
||||
'Units' : function() { E.showMenu(unitsMenu); },
|
||||
'Colours' : function() { E.showMenu(colMenu); },
|
||||
'Colours' : function() { E.showMenu(colMenu); }/*,
|
||||
'Vibrate' : {
|
||||
value : settings.buzz,
|
||||
format : v => v?"On":"Off",
|
||||
onchange : () => { settings.buzz = !settings.buzz; writeSettings(); }
|
||||
}};
|
||||
}*/
|
||||
};
|
||||
|
||||
const unitsMenu = {
|
||||
'': {'title': 'Units'},
|
||||
|
@ -43,6 +51,9 @@
|
|||
'Knots (spd)' : function() { setUnits(1.852,'knots'); },
|
||||
'Mph (spd)' : function() { setUnits(1.60934,'mph'); },
|
||||
'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'); },
|
||||
'Feet (alt)' : function() { setUnitsAlt(0.3048,'feet'); }
|
||||
};
|
||||
|
|
|
@ -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>屯屯屯屯牵M屯屯屯屯屯屯屯屯屯屯威<E5B1AF><E5A881><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>屯屯屯颓M屯屯屯屯屯屯屯屯屯屯威<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>
|
|
@ -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
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
## Future Enhancements
|
||||
* Ability to turn on the Heart Rate monitor
|
||||
* Maybe a simple stopwatch capability
|
||||
* Fix the screen flicker
|
||||
|
|
@ -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"});
|
||||
|
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 44 KiB |
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwxH+AH4A/AEcsAAYuuGFIqEF9gFJF/62SACAvvGCgYEwIASqwxVF4mmACQvEGCJcCF65iDF/4vbqwvVd4YeBJgIECAAulq4KDmS/XAAVXJwgvHOAIKCOoQuTGQ1WvYvCvYAGBQdWLwYtUMQwAQGAQuYXgJPCmQEBAAwKDCQKNZlmsAAKECAoQAEX4QQESKh4EDwVXegIvPSKgbBDwMs64ABAoOBAoQAE1gvGTAQwQCINXF4olBFw4KCAAYvCDIQvZABgv/F/4vqCoYvpGAMyF7ICBF6QADEIeBABQuCF4QACFyAwFDwRlCABNWHoUyFyoxEwOIxGBPoIAJMYYvCFyrzEGAIALFwS7TYRYACmQAGSxAtcACaNWwGArizBAwIAININWrgTCF6+GAAYvCA4gACwAKGCQIv/F+qqBAAQvCA4gACXAIKFF67cBAAQvCAwYKFmQGFF6wAZF6YxaFyo1HIBYpcAH4A/AAoA=="))
|
After Width: | Height: | Size: 1.8 KiB |
|
@ -0,0 +1,2 @@
|
|||
0.01: First version
|
||||
|
|
@ -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
|
|
@ -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};
|
||||
})();
|
After Width: | Height: | Size: 328 B |
|
@ -0,0 +1,2 @@
|
|||
0.01: First version
|
||||
|
|
@ -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
|
|
@ -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};
|
||||
})();
|
After Width: | Height: | Size: 848 B |