forked from FOSS/BangleApps
commit
1e9bcb1d0b
|
@ -0,0 +1 @@
|
|||
theme: jekyll-theme-slate
|
21
apps.json
21
apps.json
|
@ -171,7 +171,7 @@
|
|||
{ "id": "setting",
|
||||
"name": "Settings",
|
||||
"icon": "settings.png",
|
||||
"version":"0.22",
|
||||
"version":"0.23",
|
||||
"description": "A menu for setting up Bangle.js",
|
||||
"tags": "tool,system",
|
||||
"readme": "README.md",
|
||||
|
@ -2621,6 +2621,25 @@
|
|||
{"name":"lifeclk.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "speedalt",
|
||||
"name": "GPS Speedo and Altimeter",
|
||||
"shortName":"GPS Speed Alt",
|
||||
"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.",
|
||||
"tags": "tool,outdoors",
|
||||
"type":"app",
|
||||
"allow_emulator":true,
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"speedalt.app.js","url":"app.js"},
|
||||
{"name":"speedalt.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"speedalt.settings.js","url":"settings.js"}
|
||||
],
|
||||
"data": [
|
||||
{"name":"speedalt.json"}
|
||||
]
|
||||
},
|
||||
{ "id": "de-stress",
|
||||
"name": "De-Stress",
|
||||
"shortName":"De-Stress",
|
||||
|
|
|
@ -25,3 +25,4 @@
|
|||
0.21: Add passkey pairing option (BETA)
|
||||
Add whitelist option (fix #78)
|
||||
0.22: Move HID to BLE menu
|
||||
0.23: Change max time offset to 13 for NZ summer daylight time (NZDT)
|
||||
|
|
|
@ -327,7 +327,7 @@ function showLocaleMenu() {
|
|||
'Time Zone': {
|
||||
value: settings.timezone,
|
||||
min: -11,
|
||||
max: 12,
|
||||
max: 13,
|
||||
step: 0.5,
|
||||
onchange: v => {
|
||||
settings.timezone = v || 0;
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
0.01 : Initial import.
|
||||
0.02 : Misc development.
|
||||
0.03 : Enable screen off.
|
||||
0.04 : Vibrate once on no fix, twice on fix.
|
||||
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.
|
|
@ -0,0 +1,18 @@
|
|||
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.
|
||||
|
||||
Display Tap : Swaps the displays. You can have either speed or altitude on the large primary display.
|
||||
|
||||
BTN1 : Short press < 2 secs toggles the displays between showing the current speed/alt values or the maximum 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.
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
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. )
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("m02xH+AH4AJlgAMGWQ4lEw1Wq2BAAgHBG8QhFq+C1nXABGswQ4GGjhkBGJIAHOQI2ZDIcyGaQ3EmQ3WNAiaKABusNyoUDNCxuGGySdDNDBuGUoY0e2etAAWzGzoOCGheJxNlAA2J1o2LGprTNGRAAFbZw0LqwZI1pkGAAgJDUxdWGxTULFAmJFJGzURjaET6Q0DGZKYIrqjRT5Q0EGZwpEUZZqPaYaRMGg6LINhBJKNK40JAAI1Iq6fJGjDrIUQoVCwRqJDhA0OXYQRGwRsEAgWsNTCeIDYTwG1g1GqygJNROsq0ymVWq7TI2ZRJUQg1JUARkMAAg0FDhY1GDAxOKQwgAEKI6IKKAQ1KeAQ1IJ4VXAog1gC4Q1ImQVBAwYFBmQ1K1o1fMoQ1ORIQ1Ua5ahD1uzrqnEULo1L1gVBAAusGrDxHC4NlEY4aDAAZQGDhiHCGpZOJNodWmUyqxpIRBY1GQw7wCURAAPKIWtXhI1EwSFJNhIAMNQSgHwQ1EGwVXKBJsWDJSgENgjyKGyg0CNQ/XNQo1DwIRGbIS+HABYWLwI1GGwVWKhZtQChigGNhg2TCRhqIGwcy1g2YCBmsmQ0INgajIYgY1PdRKfCGpCjMG4ShNBxSfKGwyjIADOsGho1DbRI0YagQ1MG0Y0RGwjbLACLTDGh42FqxuY1lWGig2FmRuWwKdDGiZuHG6WBNC42JHAWCVBWswQyEGjQ3IHAJyBAAgHBCAwzbG5QAMGb44SGUgAkA"))
|
|
@ -0,0 +1,408 @@
|
|||
/*
|
||||
Speed and Altitude [speedalt]
|
||||
Ver : 0.07
|
||||
Mike Bennett mike[at]kereru.com
|
||||
*/
|
||||
|
||||
const dbg = 0;
|
||||
|
||||
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 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 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 max = {};
|
||||
max.spd = 0;
|
||||
max.alt = 0;
|
||||
|
||||
var emulator = 0;
|
||||
if (process.env.BOARD=="EMSCRIPTEN") emulator = 1; // 1 = running in emulator. Supplies test values;
|
||||
|
||||
function drawFix(speed,units,sats,alt,alt_units) {
|
||||
if (!canDraw) return;
|
||||
|
||||
buf.clear();
|
||||
|
||||
var val = '';
|
||||
var u='';
|
||||
|
||||
// Primary Display
|
||||
val = speed.toString();
|
||||
if ( !showSpeed ) val = alt.toString();
|
||||
|
||||
// Primary Units
|
||||
u = settings.spd_unit;
|
||||
if ( !showSpeed ) u = alt_units;
|
||||
|
||||
drawPrimary(val,u);
|
||||
|
||||
// Secondary Display
|
||||
val = alt.toString();
|
||||
if ( !showSpeed ) val = speed.toString();
|
||||
|
||||
// Secondary Units
|
||||
u = alt_units;
|
||||
if ( !showSpeed ) u = settings.spd_unit;
|
||||
|
||||
drawSecondary(val,u);
|
||||
|
||||
// Time
|
||||
drawTime();
|
||||
|
||||
//Sats
|
||||
drawSats(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();
|
||||
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
buf.setFontAlign(0,-1); //Centre
|
||||
buf.setColor(1);
|
||||
|
||||
buf.setFontVector(s);
|
||||
buf.drawString(n,110,0);
|
||||
|
||||
// Primary Units
|
||||
buf.setFontAlign(1,-1,3); //right
|
||||
buf.setColor(2);
|
||||
buf.setFontVector(25);
|
||||
buf.drawString(u,210,0);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
buf.setFontAlign(-1,1); //left, bottom
|
||||
buf.setColor(1);
|
||||
buf.setFontVector(45);
|
||||
buf.drawString(n,5,140);
|
||||
|
||||
// Secondary Units
|
||||
buf.setFontAlign(-1,1); //left, bottom
|
||||
buf.setColor(2);
|
||||
buf.setFontVector(25);
|
||||
buf.drawString(u,s,135);
|
||||
}
|
||||
|
||||
|
||||
function drawTime() {
|
||||
var x = 0;
|
||||
var y = 160;
|
||||
|
||||
buf.setFont("7x11Numeric7Seg", 2);
|
||||
buf.setFontAlign(-1,1); //left, bottom
|
||||
|
||||
buf.setColor(0);
|
||||
buf.drawString(time,x,y);
|
||||
time = require("locale").time(new Date(),1);
|
||||
buf.setColor(3);
|
||||
buf.drawString(time,x,y);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
else buf.drawString("Sats:"+sats,240,160);
|
||||
}
|
||||
|
||||
function onGPS(fix) {
|
||||
lastFix = fix;
|
||||
|
||||
var m;
|
||||
|
||||
if (fix.fix || emulator) {
|
||||
doBuzz(1);
|
||||
|
||||
//==== 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);
|
||||
}
|
||||
|
||||
// ==== 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);
|
||||
|
||||
} else {
|
||||
doBuzz(0);
|
||||
drawNoFix(fix.satellites);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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"});
|
||||
|
||||
|
||||
}
|
||||
|
||||
function maxPressed() {
|
||||
maxPress = getTime();
|
||||
}
|
||||
|
||||
function maxReleased() {
|
||||
var dur = getTime()-maxPress;
|
||||
|
||||
if ( dur < 2 ) toggleMax(); // Short press toggle fix/max display
|
||||
else {
|
||||
max.spd = 0; // Long press resets max values.
|
||||
max.alt = 0;
|
||||
onGPS(lastFix); // redraw display
|
||||
}
|
||||
}
|
||||
|
||||
function updateClock() {
|
||||
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++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function startDraw(){
|
||||
canDraw=true;
|
||||
g.clear();
|
||||
Bangle.drawWidgets();
|
||||
onGPS(lastFix); // draw app screen
|
||||
}
|
||||
|
||||
function stopDraw() {
|
||||
canDraw=false;
|
||||
}
|
||||
|
||||
// ===== Main Prog =====
|
||||
|
||||
// Read settings.
|
||||
let settings = 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.
|
||||
|
||||
/*
|
||||
Colour Pallet Idx
|
||||
0 : Background (black)
|
||||
1 : Speed/Alt
|
||||
2 : Units
|
||||
3 : Sats
|
||||
*/
|
||||
var img = {
|
||||
width:buf.getWidth(),
|
||||
height:buf.getHeight(),
|
||||
bpp:2,
|
||||
buffer:buf.buffer,
|
||||
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];
|
||||
}
|
||||
|
||||
var SCREENACCESS = {
|
||||
withApp:true,
|
||||
request:function(){
|
||||
this.withApp=false;
|
||||
stopDraw();
|
||||
clearWatch();
|
||||
},
|
||||
release:function(){
|
||||
this.withApp=true;
|
||||
startDraw();
|
||||
setButtons();
|
||||
}
|
||||
};
|
||||
|
||||
Bangle.on('lcdPower',function(on) {
|
||||
if (!SCREENACCESS.withApp) return;
|
||||
if (on) {
|
||||
startDraw();
|
||||
} else {
|
||||
stopDraw();
|
||||
}
|
||||
});
|
||||
|
||||
// All set up. Lets go.
|
||||
g.clear();
|
||||
Bangle.setLCDBrightness(1);
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
Bangle.setGPSPower(1);
|
||||
|
||||
onGPS(lastFix);
|
||||
Bangle.on('GPS', onGPS);
|
||||
|
||||
setButtons();
|
||||
setInterval(updateClock, 30000);
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.0 KiB |
|
@ -0,0 +1,61 @@
|
|||
(function(back) {
|
||||
|
||||
let settings = require('Storage').readJSON('speedalt.json',1)||{};
|
||||
//settings.buzz = settings.buzz||1;
|
||||
|
||||
function writeSettings() {
|
||||
require('Storage').write('speedalt.json',settings);
|
||||
}
|
||||
|
||||
function setUnits(m,u) {
|
||||
settings.spd = m;
|
||||
settings.spd_unit = u;
|
||||
writeSettings();
|
||||
}
|
||||
|
||||
function setUnitsAlt(m,u) {
|
||||
settings.alt = m;
|
||||
settings.alt_unit = u;
|
||||
writeSettings();
|
||||
}
|
||||
|
||||
function setColour(c) {
|
||||
settings.colour = c;
|
||||
writeSettings();
|
||||
}
|
||||
|
||||
const appMenu = {
|
||||
'': {'title': 'GPS Speed Alt'},
|
||||
'< Back': back,
|
||||
'Units' : function() { E.showMenu(unitsMenu); },
|
||||
'Colours' : function() { E.showMenu(colMenu); },
|
||||
'Vibrate' : {
|
||||
value : settings.buzz,
|
||||
format : v => v?"On":"Off",
|
||||
onchange : () => { settings.buzz = !settings.buzz; writeSettings(); }
|
||||
}};
|
||||
|
||||
const unitsMenu = {
|
||||
'': {'title': 'Units'},
|
||||
'< Back': function() { E.showMenu(appMenu); },
|
||||
'default (spd)' : 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'); }
|
||||
};
|
||||
|
||||
const colMenu = {
|
||||
'': {'title': 'Colours'},
|
||||
'< Back': function() { E.showMenu(appMenu); },
|
||||
'Default' : function() { setColour(0); },
|
||||
'Hi Contrast' : function() { setColour(1); },
|
||||
'Night' : function() { setColour(2); }
|
||||
};
|
||||
|
||||
|
||||
E.showMenu(appMenu);
|
||||
|
||||
})
|
Loading…
Reference in New Issue