1
0
Fork 0

Merge pull request #1 from nujw/low_power

Low power
master
nujw 2021-02-10 08:31:14 +13:00 committed by GitHub
commit 1475a76a1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 199 additions and 142 deletions

View File

@ -2622,10 +2622,10 @@
]
},
{ "id": "speedalt",
"name": "GPS Speedo and Altimeter",
"shortName":"GPS Speed Alt",
"name": "GPS Adventure Sports",
"shortName":"GPS Adv",
"icon": "app.png",
"version":"1.07",
"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",

View File

@ -5,8 +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 feature. Added waypoints file and distance to selected waypoint display
1.04 : Misc tweaks.
1.05 : Memory optimisation. Stopped loading entire waypoint list into memory.
1.06 : Save display settings and restore when app restarted.
1.07 : Memory optimisation.
1.00 : New features. Added waypoints file and distance to selected waypoint display. Added integration with Low Power GPS service. Save display settings and restore when app restarted.

View File

@ -1,22 +1,44 @@
Displays the GPS speed, altitude and distance to selected waypoint. 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
You can chose between two modes. One showing speed and altitude (A) and one showing speed and distance to waypoint (D).
You can switch between two display modes. One showing speed and altitude (A) and one showing speed and distance to waypoint (D).
The waypoints list is the same as that used with the [GPS Navigation] app so the same set of waypoints can be used across both apps. Refer to that app for waypoint file information.
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.
Left Display Tap : Swaps the displays. You can have either speed or [A]ltitude/[D]istance on the large primary display.
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.
BTN1 : [Speed+Altitude] Short press < 2 secs toggles the displays between showing the current speed/alt values or the maximum speed/alt values recorded.
BTN1 : [Speed+Altitude] Long press > 2 secs resets the recorded maximum values.
BTN1 : [Speed+Distance] Select next waypoint. Last fix distance from selected waypoint is displayed.
## Buttons and Controls
BTN3 : Swaps the modes between Speed+[A]ltitude or Speed+[D]istance.
App Settings : Select the desired display units. Speed can be as per the default locale, kph, knots, mph or m/s. Distance caqn be km, miles or nautical miles. Altitude can be feet or metres. Select one of three colour schemes. Default, high contrast (all white on black) or night ( all red on black ).
### [A]ltitude mode
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 fix or not.
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.
### [D]istance mode
BTN1 : Select next waypoint. Last fix distance from selected waypoint is displayed.
### Both modes
BTN2 : Short press < 2 secs 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. Tap again to restore power saving timeouts.
BTN2 : Long press > 2 secs turns off the low power GPS service and GPS. Exit and restart the app to turn back on. If you are using the low power gps service it will keep the GPS powered on after exiting the app. This is a convenient way to turn it off.
BTN3 : Long press exit and return to watch.
BTN4 : Left Display Tap : Swaps which figure is in the large display. You can have either speed or [A]ltitude/[D]istance on the large primary display.
## App Settings
Select the desired display units. Speed can be as per the default locale, kph, knots, mph or m/s. Distance can be km, miles or nautical miles. Altitude can be feet or metres. Select one of three colour schemes. Default (three colours), high contrast (all white on black) or night ( all red on black ).
## Loss of fix
When the GPS obtains a fix the number of satellites is displayed as 'Sats:nn'. When unable to obtain a fix then the last known fix is used and the age of that fix in seconds is displayed as 'Age:nn'. Seeing 'Sats' or 'Age' indicates whether the GPS has a current fix or not.
## Screens
Speed and Altitude:<br>
![](screen1.png)<p>
@ -29,17 +51,25 @@ MAX Values instead:<br>
Settings:<br>
![](screen4.png)<p>
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!
## Low Power GPS Service
Thanks:
Many thanks to Gordon Williams. Awesome job.
Also to @jeffmer, the developer of the 'GPS Navigation' app.
This app will work quite happily without this service but will use the [Low power GPS Service](https://banglejs.com/apps/#low%20power%20gps%20service) if it is installed. You may choose to use the Low Power GPS Service to gain significantly longer battery life while the GPS is on. Please read the Low Power GPS Service Readme to understand what this does.
Waypoints:
When using the Low Power GPS Service 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. 30 seconds after the display is blanked by the watch this app will switch the GPS to PMOO mode and will only attempt to get a fix every minute or two. 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.
Create a file waypoints.json and write to storage on the Bangle.js using the IDE. The first 6 characters of the name are displyed in Speed+[D]istance mode.
There are a couple of things to consider when using the Low Power GPS Service. This app plus the LP GPS service together use a considerable chunk of the Bangle.JS memory. A large waypoints file will also contribute to this.
Sample waypoints.json
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.
When exiting this app the Low Power GPS Service will keep the GPS powered on, using battery. It can be manually turned off using the gps service settings menu. As a convenience, long press BTN2 for 2 seconds will turn it off while using this GPS Adv app.
## 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>
[
@ -103,3 +133,15 @@ Sample waypoints.json
}
]
</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!
## 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 Service](https://banglejs.com/apps/#low%20power%20gps%20service) work.

View File

@ -1,10 +1,8 @@
/*
Speed and Altitude [speedalt]
Ver : 1.07
Mike Bennett mike[at]kereru.com
process.memory()
*/
var v = '1.14';
var buf = Graphics.createArrayBuffer(240,160,2,{msb:true});
// Load fonts
@ -12,9 +10,10 @@ require("Font7x11Numeric7Seg").add(Graphics);
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 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;
@ -64,22 +63,18 @@ function drawFix(speed,units,sats,alt,alt_units,age,fix) {
var u='';
// Primary Display
v = speed.toString();
if ( !settings.primSpd ) v = alt.toString();
v = (settings.primSpd)?speed.toString():alt.toString();
// Primary Units
u = settings.spd_unit;
if ( !settings.primSpd ) u = alt_units;
u = (settings.primSpd)?settings.spd_unit:alt_units;
drawPrimary(v,u);
// Secondary Display
v = alt.toString();
if ( !settings.primSpd ) v = speed.toString();
v = (settings.primSpd)?alt.toString():speed.toString();
// Secondary Units
u = alt_units;
if ( !settings.primSpd ) u = settings.spd_unit;
u = (settings.primSpd)?alt_units:settings.spd_unit;
drawSecondary(v,u);
@ -90,9 +85,12 @@ function drawFix(speed,units,sats,alt,alt_units,age,fix) {
drawWP();
//Sats
if ( fix ) drawSats('Sats:'+sats);
else drawSats('Age:'+age);
if ( age > 10 ) {
if ( age > 90 ) age = '>90';
drawSats('Age:'+age);
}
else drawSats('Sats:'+sats);
g.reset();
g.drawImage(img,0,40);
@ -161,13 +159,10 @@ function drawTime() {
function drawWP() {
var nm = wp.name;
if ( nm == undefined ) nm = '';
if ( nm == 'NONE' ) nm = '';
if ( settings.modeA ) nm='';
if ( nm == undefined || nm == 'NONE' || settings.modeA ) nm = '';
buf.setFontAlign(-12,1); //left, bottom
buf.setFontAlign(-1,1); //left, bottom
buf.setColor(2);
// buf.setFont("6x8", 1);
buf.setFontVector(20);
buf.drawString(nm.substring(0,6),77,160);
@ -206,8 +201,6 @@ function onGPS(fix) {
fix.satellites = 12;
fix.time = new Date();
}
if (fix.fix) lf = fix;
var m;
@ -215,113 +208,101 @@ function onGPS(fix) {
var al = '---';
var di = '---';
var age = '---';
if (lf.fix == 1 ) {
// Speed
if ( settings.spd == 0 ) {
m = require("locale").speed(lf.speed).match(/([0-9,\.]+)(.*)/); // regex splits numbers from units
sp = parseFloat(m[1]);
settings.spd_unit = m[2];
}
else {
// Calculate for selected units
sp = lf.speed;
sp = parseFloat(sp)/parseFloat(settings.spd);
}
if ( sp < 10 ) sp = sp.toFixed(1);
else sp = Math.round(sp);
if (parseFloat(sp) > parseFloat(max.spd) ) max.spd = parseFloat(sp);
// Altitude
al = lf.alt;
al = Math.round(parseFloat(al)/parseFloat(settings.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 ( age > 90 ) age = '>90';
if (fix.fix) {
lf = fix;
// Speed
if ( settings.spd == 0 ) {
m = require("locale").speed(lf.speed).match(/([0-9,\.]+)(.*)/); // regex splits numbers from units
sp = parseFloat(m[1]);
settings.spd_unit = m[2];
}
else sp = parseFloat(lf.speed)/parseFloat(settings.spd); // Calculate for selected units
if ( sp < 10 ) sp = sp.toFixed(1);
else sp = Math.round(sp);
if (parseFloat(sp) > parseFloat(max.spd) ) max.spd = parseFloat(sp);
// Altitude
al = lf.alt;
al = Math.round(parseFloat(al)/parseFloat(settings.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 ( settings.modeA ) {
if ( showMax ) {
// Speed and alt maximums
drawFix(max.spd,settings.spd_unit,fix.satellites,max.alt,settings.alt_unit,age,fix.fix);
}
else {
// Show speed/altitude
drawFix(sp,settings.spd_unit,fix.satellites,al,settings.alt_unit,age,fix.fix);
}
}
else {
// Show speed/distance
if ( di <= 0 ) {
// No WP selected
drawFix(sp,settings.spd_unit,fix.satellites,'','',age,fix.fix);
}
else {
drawFix(sp,settings.spd_unit,fix.satellites,di,settings.dist_unit,age,fix.fix);
}
}
if ( settings.modeA ) {
if ( showMax ) drawFix(max.spd,settings.spd_unit,lf.satellites,max.alt,settings.alt_unit,age,lf.fix); // Speed and alt maximums
else drawFix(sp,settings.spd_unit,lf.satellites,al,settings.alt_unit,age,lf.fix); // Show speed/altitude
}
else {
// Show speed/distance
if ( di <= 0 ) drawFix(sp,settings.spd_unit,lf.satellites,'','',age,lf.fix); // No WP selected
else drawFix(sp,settings.spd_unit,lf.satellites,di,settings.dist_unit,age,lf.fix);
}
}
function toggleDisplay() {
settings.primSpd = !settings.primSpd;
savSettings();
onGPS(lf); // Update display
}
function toggleAltDist() {
settings.modeA = !settings.modeA;
savSettings();
onGPS(lf);
}
function setButtons(){
// Spd+Dist : Select next waypoint
setWatch(btnPressed, BTN1,{repeat:true,edge:"rising"});
setWatch(btnReleased, BTN1,{repeat:true,edge:"falling"});
setWatch(function(e) {
var dur = e.time - e.lastTime;
if ( settings.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});
// Show launcher when middle button pressed
setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});
// setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});
// Power saving on/off
setWatch(function(e){
var dur = e.time - e.lastTime;
if ( dur < 2 ) { // Short press.
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();
}
}
else gpsOff(); // long press, power off LP GPS
}, BTN2, {repeat:true,edge:"falling"});
// Toggle between alt or dist
setWatch(toggleAltDist, BTN3, {repeat:true,edge:"falling"});
setWatch(function(e){
settings.modeA = !settings.modeA;
savSettings();
onGPS(lf);
}, BTN3, {repeat:true,edge:"falling"});
// Touch left screen to toggle display
setWatch(toggleDisplay, BTN4, {repeat:true,edge:"falling"});
setWatch(function(e){
settings.primSpd = !settings.primSpd;
savSettings();
onGPS(lf); // Update display
}, BTN4, {repeat:true,edge:"falling"});
}
function btnPressed() {
maxPress = getTime();
}
function btnReleased() {
var dur = getTime()-maxPress;
if ( settings.modeA ) {
// Spd+Alt mode - Switch between fix and MAX
if ( dur < 2 ) {
showMax = !showMax; // Short press toggle fix/max display
}
else {
max.spd = 0; // Long press resets max values.
max.alt = 0;
}
}
else {
// Spd+Dist mode - Select next waypoint
nxtWp(1);
}
onGPS(lf);
}
function updateClock() {
if (!canDraw) return;
drawTime();
@ -332,6 +313,7 @@ function updateClock() {
function startDraw(){
canDraw=true;
setLpMode('SuperE'); // off
g.clear();
Bangle.drawWidgets();
onGPS(lf); // draw app screen
@ -339,12 +321,40 @@ function startDraw(){
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.
}
function savSettings() {
require("Storage").write('speedalt.json',settings);
}
// Is low power GPS service available to use?
function isLP() {
if (WIDGETS.gpsservice == undefined) return(0);
return(1);
}
function setLpMode(m) {
if (tmrLP) {clearInterval(tmrLP);tmrLP = false;} // Stop any scheduled drop to low power
if ( !lp ) return;
var s = WIDGETS.gpsservice.gps_get_settings();
if ( m !== s.power_mode || !s.gpsservice ) {
s.gpsservice = true;
s.power_mode = m;
WIDGETS.gpsservice.gps_set_settings(s);
WIDGETS.gpsservice.reload();
}
}
function gpsOff() {
if ( !lp ) return;
var s = WIDGETS.gpsservice.gps_get_settings();
s.gpsservice = false;
s.power_mode = 'SuperE';
WIDGETS.gpsservice.gps_set_settings(s);
WIDGETS.gpsservice.reload();
}
// =Main Prog
// Read settings.
@ -379,8 +389,8 @@ 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]);
if ( settings.colour == 1 ) img.palette = new Uint16Array([0,0xFFFF,0xFFF6,0xDFFF]);
if ( settings.colour == 2 ) img.palette = new Uint16Array([0,0xFF800,0xFAE0,0xF813]);
var SCREENACCESS = {
withApp:true,
@ -398,8 +408,17 @@ Bangle.on('lcdPower',function(on) {
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
Bangle.setGPSPower(1);
onGPS(lf);
Bangle.on('GPS', onGPS);
var lp = isLP(); // Low power GPS widget installed?
if ( lp ) {
setLpMode('SuperE');
setInterval(()=>onGPS(WIDGETS.gpsservice.gps_get_fix()), 1000);
}
else {
Bangle.setGPSPower(1);
Bangle.on('GPS', onGPS);
}
setButtons();
setInterval(updateClock, 30000);