Merge pull request #706 from hughbarney/master
Kitchen Combo - multiclock combo of Stepo, Walkersclock, Waypointer and Arrow, can quickly switch betweenpull/707/head^2
18
apps.json
|
@ -3041,5 +3041,23 @@
|
||||||
"evaluate": true
|
"evaluate": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{ "id": "kitchen",
|
||||||
|
"name": "Kitchen Combo",
|
||||||
|
"icon": "kitchen.png",
|
||||||
|
"version":"0.01",
|
||||||
|
"description": "Combination of the stepo, walkersclock, arrow and waypointer apps into a multiclock format. 'Everything but the kitchen sink'. Requires firmware v2.08.167 or later",
|
||||||
|
"tags": "tool,outdoors,gps",
|
||||||
|
"readme": "README.md",
|
||||||
|
"interface":"waypoints.html",
|
||||||
|
"storage": [
|
||||||
|
{"name":"kitchen.app.js","url":"kitchen.app.js"},
|
||||||
|
{"name":"stepo.kit.js","url":"stepo.kit.js"},
|
||||||
|
{"name":"gps.kit.js","url":"gps.kit.js"},
|
||||||
|
{"name":"digi.kit.js","url":"digi.kit.js"},
|
||||||
|
{"name":"compass.kit.js","url":"compass.kit.js"},
|
||||||
|
{"name":"waypoints.json","url":"waypoints.json","evaluate":false},
|
||||||
|
{"name":"kitchen.img","url":"kitchen.icon.js","evaluate":true}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,199 @@
|
||||||
|
# Kitchen Combo - a multiclock format of the waypointer, walksersclock, stopo and arrow apps.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
*...everything but the kitchen sink..*
|
||||||
|
|
||||||
|
NOTE: This app require Bangle firmware 2.08.187 or later.
|
||||||
|
|
||||||
|
The app is aimed at navigation whilst walking. Please note that it
|
||||||
|
would be foolish in the extreme to rely on this as your only
|
||||||
|
navigation aid!
|
||||||
|
|
||||||
|
Please refer to the section on calibration of the compass. This
|
||||||
|
should be done each time the app is going to be used.
|
||||||
|
|
||||||
|
The app has 4 faces that can quickly be switched from one to another.
|
||||||
|
* Stepo - a large font clock that dosplays the current steps in a doughnut guauge
|
||||||
|
* GPS - when the GPS is on displays current grid ref, lat, lon, speed, altitude and course
|
||||||
|
* Digi - a digital clock with day and date, displays battery and memory status (click BTN1)
|
||||||
|
* Waypointer - a compass arrow that points to a selected waypoint when the GPS is on.
|
||||||
|
- enables you to mark waypoints and cycle through a list of waypoints
|
||||||
|
- shows distance and bearing to currently selected waypoint
|
||||||
|
|
||||||
|
|
||||||
|
## Common buttons used to navigate through the app
|
||||||
|
|
||||||
|
* BTN3 - short press, next app/clock face
|
||||||
|
* BTN3 - long press, reset the watch
|
||||||
|
* BTN2 - long press, start the app launcher
|
||||||
|
|
||||||
|
The following buttons depend on which face is currently in use
|
||||||
|
|
||||||
|
* BTN1 - Short press
|
||||||
|
- Digi : Cycle the battery, memory display on the mode line
|
||||||
|
- GPS : Cycle through the GPS data displays (grid ref, lat lon, speed, alt, course)
|
||||||
|
- Waypointer : Select previous waypoint
|
||||||
|
* BTN1 - long press
|
||||||
|
- GPS : switch GPS on or off
|
||||||
|
- Waypointer : set or unset the current waypoint
|
||||||
|
* BTN2 - short press
|
||||||
|
- Waypointer : select next waypoint
|
||||||
|
|
||||||
|
|
||||||
|
## Stepo
|
||||||
|

|
||||||
|
|
||||||
|
- Displays the time in large font
|
||||||
|
- Display current step count in a doughnut guage
|
||||||
|
- Show step count in the middle of the doughnut guage
|
||||||
|
- The guage show percentage of steps out of a goal of 10000 steps
|
||||||
|
- When the battery is less than 25% the doughnut turns red
|
||||||
|
- Use BTN3 to switch to the next app
|
||||||
|
|
||||||
|
## GPS
|
||||||
|

|
||||||
|
- Use BTN1 long press to switch the GPS on or off
|
||||||
|
- Use BTN1 short press to switch between the display of the Os grid refernce, lat lon, speed, alt, course.
|
||||||
|
- Use BTN3 to switch to the next app
|
||||||
|
|
||||||
|
## Digi
|
||||||
|

|
||||||
|
- Displays the time in large font
|
||||||
|
- Display day and date
|
||||||
|
- Use BTN1 to switch between display of battery and memory %.
|
||||||
|
- Use BTN3 to switch to the next app.
|
||||||
|
|
||||||
|
## Waypointer
|
||||||
|
- Use BTN1 to select previous waypoint (when GPS is on)
|
||||||
|
- Use BTN2 to select the next waypoint (when GPS is on)
|
||||||
|
- Use BTN3 to switch to the next app
|
||||||
|
- Use BTN1 long press to clear a waypoint or to record the current position
|
||||||
|
|
||||||
|
When the GPS is off this screen acts as a compass and points
|
||||||
|
North. The white digits below the arrow show your current heading
|
||||||
|
with reference to North.
|
||||||
|
|
||||||
|
When the GPS in on the screen points to the selected waypoint which
|
||||||
|
are loaded from the waypoints.json file. The compass arrow now points
|
||||||
|
in the direction you need to walk in. Once you have selected a
|
||||||
|
waypoint a bearing from your current position (received from a GPS
|
||||||
|
fix) is calculated and the compass is set to point in that direction.
|
||||||
|
If the arrow is pointing to the left, turning left should straighten
|
||||||
|
the arrow up so that it is pointing straight ahead.
|
||||||
|
|
||||||
|
The large digits are the bearing from the current position. On the
|
||||||
|
left is the distance to the waypoint in local units. When the
|
||||||
|
selected waypoint has a lat/lon recorded the text of the distance and
|
||||||
|
waypoint name will be shown in blue. If the waypoint name is shown
|
||||||
|
in white it means it is available to record a waypoint.
|
||||||
|
|
||||||
|
Use BTN1 and BTN2 to select the previous or next waypoint
|
||||||
|
respectively. In the screen shot below a waypoint giving the location
|
||||||
|
of Stone Henge has been selected.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The screenshot above shows that Stone Henge is 259.9 miles from the
|
||||||
|
current location. To travel towards Stone Henge I need to turn
|
||||||
|
slightly right until the arrow is pointing straight ahead. As you
|
||||||
|
continue to walk in the pointed direction you should see the distance
|
||||||
|
to the waypoint reduce. The frequency of updates will depend on
|
||||||
|
which settings you have used in the GPS.
|
||||||
|
|
||||||
|
At the top of the screen you can see two widgets. These are the [GPS
|
||||||
|
Power
|
||||||
|
Widget](https://github.com/espruino/BangleApps/tree/master/apps/widgps)
|
||||||
|
and the [Compass Power Indicator Widget]. These can be installed
|
||||||
|
seperately and provide you a indication of when the GPS and Compass
|
||||||
|
are switched on and drawing power.
|
||||||
|
|
||||||
|
|
||||||
|
### Marking Waypoints
|
||||||
|
|
||||||
|
The app lets you mark your current location as follows. There are
|
||||||
|
vacant slots in the waypoint file which can be allocated a
|
||||||
|
location. In the distributed waypoint file these are labelled WP0 to
|
||||||
|
WP4. Select one of these - WP2 is shown below.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Bearing and distance are both zero as WP2 has currently no GPS
|
||||||
|
location associated with it. To mark the location, long press BTN1.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The app indicates that WP2 is now marked by changing the color to
|
||||||
|
blue. The distance should be small as shown in the screen shot as you
|
||||||
|
have just marked your current location.
|
||||||
|
|
||||||
|
You can free the waypoint by long pressing BTN1 again.
|
||||||
|
|
||||||
|
|
||||||
|
### Waypoint JSON file
|
||||||
|
|
||||||
|
When the app is loaded from the app loader, a file named
|
||||||
|
`waypoints.json` is loaded along with the javascript etc. The file
|
||||||
|
has the following contents:
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name":"NONE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"No10",
|
||||||
|
"lat":51.5032,
|
||||||
|
"lon":-0.1269
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"Stone",
|
||||||
|
"lat":51.1788,
|
||||||
|
"lon":-1.8260
|
||||||
|
},
|
||||||
|
{ "name":"WP0" },
|
||||||
|
{ "name":"WP1" },
|
||||||
|
{ "name":"WP2" },
|
||||||
|
{ "name":"WP3" },
|
||||||
|
{ "name":"WP4" }
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
The file contains the initial NONE waypoint which is useful if you
|
||||||
|
just want to display course and speed. The next two entries are
|
||||||
|
waypoints to No 10 Downing Street and to Stone Henge - obtained from
|
||||||
|
Google Maps. The last five entries are entries which can be *marked*.
|
||||||
|
|
||||||
|
You add and delete entries using the Web IDE to load and then save
|
||||||
|
the file from and to watch storage. The app itself does not limit the
|
||||||
|
number of entries although it does load the entire file into RAM
|
||||||
|
which will obviously limit this.
|
||||||
|
|
||||||
|
|
||||||
|
### Waypoint Editor
|
||||||
|
|
||||||
|
Clicking on the download icon of gpsnav in the app loader invokes the
|
||||||
|
waypoint editor. The editor downloads and displays the current
|
||||||
|
`waypoints.json` file. Clicking the `Edit` button beside an entry
|
||||||
|
causes the entry to be deleted from the list and displayed in the
|
||||||
|
edit boxes. It can be restored - by clicking the `Add waypoint`
|
||||||
|
button. A new markable entry is created by using the `Add name`
|
||||||
|
button. The edited `waypoints.json` file is uploaded to the Bangle by
|
||||||
|
clicking the `Upload` button.
|
||||||
|
|
||||||
|
|
||||||
|
### Calibration of the Compass
|
||||||
|
|
||||||
|
The Compass should be calibrated before using the App to navigate to
|
||||||
|
a waypoint (or a series of waypoints). To do this use either the
|
||||||
|
Arrow Compass or the [Navigation
|
||||||
|
Compass](https://github.com/espruino/BangleApps/tree/master/apps/magnav).
|
||||||
|
Open the compass app and clicking on BTN3. The calibration process
|
||||||
|
takes 30 seconds during which you should move the watch slowly
|
||||||
|
through figures of 8. It is important that during calibration the
|
||||||
|
watch is fully rotated around each of it axes. If the app does give
|
||||||
|
the correct direction heading or is not stable with respect to tilt
|
||||||
|
and roll - redo the calibration by pressing *BTN3*. Calibration data
|
||||||
|
is recorded in a storage file named `magnav.json`.
|
||||||
|
|
|
@ -0,0 +1,306 @@
|
||||||
|
(() => {
|
||||||
|
function getFace(){
|
||||||
|
var intervalRefSec;
|
||||||
|
var pal_by;
|
||||||
|
var pal_bw;
|
||||||
|
var pal_bb;
|
||||||
|
var buf1;
|
||||||
|
var buf2;
|
||||||
|
var bearing;
|
||||||
|
var heading;
|
||||||
|
var oldHeading;
|
||||||
|
var CALIBDATA;
|
||||||
|
var previous;
|
||||||
|
var wp;
|
||||||
|
var wp_distance;
|
||||||
|
var wp_bearing;
|
||||||
|
var loc;
|
||||||
|
var gpsObject;
|
||||||
|
|
||||||
|
function log_debug(o) {
|
||||||
|
//console.log(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
function init(gps) {
|
||||||
|
showMem("compass init() START");
|
||||||
|
gpsObject = gps;
|
||||||
|
pal_by = new Uint16Array([0x0000,0xFFC0],0,1); // black, yellow
|
||||||
|
pal_bw = new Uint16Array([0x0000,0xffff],0,1); // black, white
|
||||||
|
pal_bb = new Uint16Array([0x0000,0x07ff],0,1); // black, blue
|
||||||
|
buf1 = Graphics.createArrayBuffer(128,128,1,{msb:true});
|
||||||
|
buf2 = Graphics.createArrayBuffer(80,40,1,{msb:true});
|
||||||
|
|
||||||
|
intervalRefSec = undefined;
|
||||||
|
bearing = 0; // always point north if GPS is off
|
||||||
|
heading = 0;
|
||||||
|
oldHeading = 0;
|
||||||
|
previous = {bs:"-", dst:"-", wp_name:"-", course:999};
|
||||||
|
loc = require("locale");
|
||||||
|
CALIBDATA = require("Storage").readJSON("magnav.json",1)||null;
|
||||||
|
getWaypoint();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* compass should be powered on before startDraw is called
|
||||||
|
* otherwise compass power widget will not come on
|
||||||
|
*/
|
||||||
|
if (!Bangle.isCompassOn()) Bangle.setCompassPower(1);
|
||||||
|
gps.determineGPSState();
|
||||||
|
|
||||||
|
showMem("compass init() END");
|
||||||
|
}
|
||||||
|
|
||||||
|
function freeResources() {
|
||||||
|
showMem("compass freeResources() START");
|
||||||
|
gpsObject = undefined;
|
||||||
|
pal_by = undefined;
|
||||||
|
pal_bw = undefined;
|
||||||
|
pal_bb = undefined;
|
||||||
|
buf1 = undefined;
|
||||||
|
buf2 = undefined;
|
||||||
|
intervalRefSec = undefined;
|
||||||
|
previous = undefined;
|
||||||
|
|
||||||
|
bearing = 0;
|
||||||
|
heading = 0;
|
||||||
|
oldHeading = 0;
|
||||||
|
loc = undefined;
|
||||||
|
CALIBDATA = undefined;
|
||||||
|
wp = undefined;
|
||||||
|
if (Bangle.isCompassOn()) Bangle.setCompassPower(0);
|
||||||
|
showMem("compass freeResources() END");
|
||||||
|
}
|
||||||
|
|
||||||
|
function flip1(x,y) {
|
||||||
|
g.drawImage({width:128,height:128,bpp:1,buffer:buf1.buffer, palette:pal_by},x ,y);
|
||||||
|
buf1.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
function flip2_bw(x,y) {
|
||||||
|
g.drawImage({width:80,height:40,bpp:1,buffer:buf2.buffer, palette:pal_bw},x ,y);
|
||||||
|
buf2.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
function flip2_bb(x,y) {
|
||||||
|
g.drawImage({width:80,height:40,bpp:1,buffer:buf2.buffer, palette:pal_bb},x ,y);
|
||||||
|
buf2.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
function startTimer() {
|
||||||
|
log_debug("startTimer()");
|
||||||
|
if (!Bangle.isCompassOn()) Bangle.setCompassPower(1);
|
||||||
|
resetPrevious();
|
||||||
|
draw();
|
||||||
|
intervalRefSec = setInterval(draw, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopTimer() {
|
||||||
|
log_debug("stopTimer()");
|
||||||
|
if(intervalRefSec) {intervalRefSec=clearInterval(intervalRefSec);}
|
||||||
|
if (Bangle.isCompassOn()) Bangle.setCompassPower(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showMem(msg) {
|
||||||
|
var val = process.memory();
|
||||||
|
var str = msg + " " + Math.round(val.usage*100/val.total) + "%";
|
||||||
|
log_debug(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onButtonShort(btn) {
|
||||||
|
switch(btn) {
|
||||||
|
case 1:
|
||||||
|
log_debug("prev waypoint");
|
||||||
|
gpsObject.nextWaypoint(-1);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
log_debug("next waypoint");
|
||||||
|
gpsObject.nextWaypoint(1);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
resetPrevious();
|
||||||
|
getWaypoint();
|
||||||
|
drawGPSData();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onButtonLong(btn) {
|
||||||
|
log_debug("markWaypoint()");
|
||||||
|
if (btn !== 1) return;
|
||||||
|
if (gpsObject.getState() !== gpsObject.GPS_RUNNING) return;
|
||||||
|
|
||||||
|
log_debug("markWaypoint()");
|
||||||
|
|
||||||
|
gpsObject.markWaypoint();
|
||||||
|
resetPrevious();
|
||||||
|
getWaypoint();
|
||||||
|
drawGPSData();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWaypoint() {
|
||||||
|
log_debug("getWaypoint()");
|
||||||
|
wp = gpsObject.getCurrentWaypoint();
|
||||||
|
wp_distance = gpsObject.getWPdistance();
|
||||||
|
wp_bearing = gpsObject.getWPbearing();
|
||||||
|
log_debug(wp);
|
||||||
|
log_debug(wp_distance);
|
||||||
|
log_debug(wp_bearing);
|
||||||
|
}
|
||||||
|
|
||||||
|
// takes 32ms
|
||||||
|
function drawCompass(hd) {
|
||||||
|
if (Math.abs(hd - oldHeading) < 2) return 0;
|
||||||
|
hd=hd*Math.PI/180;
|
||||||
|
var p = [0, 1.1071, Math.PI/4, 2.8198, 3.4633, 7*Math.PI/4 , 5.1760];
|
||||||
|
|
||||||
|
var poly = [
|
||||||
|
64+60*Math.sin(hd+p[0]), 64-60*Math.cos(hd+p[0]),
|
||||||
|
64+44.7214*Math.sin(hd+p[1]), 64-44.7214*Math.cos(hd+p[1]),
|
||||||
|
64+28.2843*Math.sin(hd+p[2]), 64-28.2843*Math.cos(hd+p[2]),
|
||||||
|
64+63.2455*Math.sin(hd+p[3]), 64-63.2455*Math.cos(hd+p[3]),
|
||||||
|
64+63.2455*Math.sin(hd+p[4]), 64-63.2455*Math.cos(hd+p[4]),
|
||||||
|
64+28.2843*Math.sin(hd+p[5]), 64-28.2843*Math.cos(hd+p[5]),
|
||||||
|
64+44.7214*Math.sin(hd+p[6]), 64-44.7214*Math.cos(hd+p[6])
|
||||||
|
];
|
||||||
|
|
||||||
|
buf1.fillPoly(poly);
|
||||||
|
flip1(56, 56);
|
||||||
|
}
|
||||||
|
|
||||||
|
// stops violent compass swings and wobbles, takes 3ms
|
||||||
|
function newHeading(m,h){
|
||||||
|
//log_debug("newHeading()");
|
||||||
|
var s = Math.abs(m - h);
|
||||||
|
var delta = (m>h)?1:-1;
|
||||||
|
if (s>=180){s=360-s; delta = -delta;}
|
||||||
|
if (s<2) return h;
|
||||||
|
if (s<3) return h;
|
||||||
|
var hd = h + delta*(1 + Math.round(s/5));
|
||||||
|
if (hd<0) hd+=360;
|
||||||
|
if (hd>360)hd-= 360;
|
||||||
|
return hd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// takes approx 7ms
|
||||||
|
function tiltfixread(O,S){
|
||||||
|
//log_debug("tiltfixread()");
|
||||||
|
var m = Bangle.getCompass();
|
||||||
|
var g = Bangle.getAccel();
|
||||||
|
m.dx =(m.x-O.x)*S.x; m.dy=(m.y-O.y)*S.y; m.dz=(m.z-O.z)*S.z;
|
||||||
|
var d = Math.atan2(-m.dx,m.dy)*180/Math.PI;
|
||||||
|
if (d<0) d+=360;
|
||||||
|
var phi = Math.atan(-g.x/-g.z);
|
||||||
|
var cosphi = Math.cos(phi), sinphi = Math.sin(phi);
|
||||||
|
var theta = Math.atan(-g.y/(-g.x*sinphi-g.z*cosphi));
|
||||||
|
var costheta = Math.cos(theta), sintheta = Math.sin(theta);
|
||||||
|
var xh = m.dy*costheta + m.dx*sinphi*sintheta + m.dz*cosphi*sintheta;
|
||||||
|
var yh = m.dz*sinphi - m.dx*cosphi;
|
||||||
|
var psi = Math.atan2(yh,xh)*180/Math.PI;
|
||||||
|
if (psi<0) psi+=360;
|
||||||
|
return psi;
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
//log_debug("draw()");
|
||||||
|
var d = tiltfixread(CALIBDATA.offset,CALIBDATA.scale);
|
||||||
|
heading = newHeading(d,heading);
|
||||||
|
|
||||||
|
if (gpsObject.getState() === gpsObject.GPS_RUNNING) {
|
||||||
|
wp_dist = gpsObject.getWPdistance();
|
||||||
|
wp_bearing = gpsObject.getWPbearing();
|
||||||
|
bearing = wp_bearing;
|
||||||
|
} else {
|
||||||
|
bearing = 0;
|
||||||
|
wp_distance = 0;
|
||||||
|
wp_bearing = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dir = bearing - heading;
|
||||||
|
if (dir < 0) dir += 360;
|
||||||
|
if (dir > 360) dir -= 360;
|
||||||
|
var t = drawCompass(dir); // we want compass to show us where to go
|
||||||
|
oldHeading = dir;
|
||||||
|
|
||||||
|
if (gpsObject.getState() === gpsObject.GPS_RUNNING) {
|
||||||
|
drawGPSData();
|
||||||
|
} else {
|
||||||
|
drawCompassHeading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawCompassHeading() {
|
||||||
|
//log_debug("drawCompassHeading()");
|
||||||
|
// draw the heading
|
||||||
|
buf2.setColor(1);
|
||||||
|
buf2.setFontAlign(-1,-1);
|
||||||
|
buf2.setFont("Vector",38);
|
||||||
|
var hding = Math.round(heading);
|
||||||
|
var hd = hding.toString();
|
||||||
|
hd = hding < 10 ? "00"+hd : hding < 100 ? "0"+hd : hd;
|
||||||
|
buf2.drawString(hd,0,0);
|
||||||
|
flip2_bw(90, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawGPSData() {
|
||||||
|
log_debug("drawGPSData()");
|
||||||
|
buf2.setFont("Vector",24);
|
||||||
|
var bs = wp_bearing.toString();
|
||||||
|
bs = wp_bearing<10?"00"+bs : wp_bearing<100 ?"0"+bs : bs;
|
||||||
|
var dst = loc.distance(wp_distance);
|
||||||
|
|
||||||
|
log_debug(bs);
|
||||||
|
log_debug(dst);
|
||||||
|
|
||||||
|
// -1=left (default), 0=center, 1=right
|
||||||
|
|
||||||
|
// show distance on the left
|
||||||
|
if (previous.dst !== dst) {
|
||||||
|
previous.dst = dst
|
||||||
|
buf2.setColor(1);
|
||||||
|
buf2.setFontAlign(-1,-1);
|
||||||
|
buf2.setFont("Vector", 20);
|
||||||
|
if (gpsObject.waypointHasLocation()) {
|
||||||
|
buf2.drawString(dst,0,0);
|
||||||
|
flip2_bb(0, 200);
|
||||||
|
} else {
|
||||||
|
buf2.drawString(" ",0,0);
|
||||||
|
flip2_bw(0, 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bearing, place in middle at bottom of compass
|
||||||
|
if (previous.bs !== bs) {
|
||||||
|
previous.bs = bs;
|
||||||
|
buf2.setColor(1);
|
||||||
|
buf2.setFontAlign(0, -1);
|
||||||
|
buf2.setFont("Vector",38);
|
||||||
|
buf2.drawString(bs,40,0);
|
||||||
|
flip2_bw(80, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
// waypoint name on right
|
||||||
|
if (previous.wp_name !== wp.name) {
|
||||||
|
buf2.setColor(1);
|
||||||
|
buf2.setFontAlign(1,-1); // right, bottom
|
||||||
|
buf2.setFont("Vector", 20);
|
||||||
|
buf2.drawString(wp.name, 80, 0);
|
||||||
|
if (gpsObject.waypointHasLocation())
|
||||||
|
flip2_bb(160, 200);
|
||||||
|
else
|
||||||
|
flip2_bw(160, 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear the attributes that control the display refresh
|
||||||
|
function resetPrevious() {
|
||||||
|
log_debug("resetPrevious()");
|
||||||
|
previous = {bs:"-", dst:"-", wp_name:"-", course:999};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {init:init, freeResources:freeResources, startTimer:startTimer, stopTimer:stopTimer,
|
||||||
|
onButtonShort:onButtonShort, onButtonLong:onButtonLong};
|
||||||
|
}
|
||||||
|
|
||||||
|
return getFace;
|
||||||
|
|
||||||
|
})();
|
|
@ -0,0 +1,146 @@
|
||||||
|
(() => {
|
||||||
|
function getFace(){
|
||||||
|
var intervalRefSec;
|
||||||
|
var buf;
|
||||||
|
var days;
|
||||||
|
var prevInfo;
|
||||||
|
var prevDate;
|
||||||
|
var prevTime;
|
||||||
|
var infoMode;
|
||||||
|
|
||||||
|
const INFO_NONE = 0;
|
||||||
|
const INFO_BATT = 1;
|
||||||
|
const INFO_MEM = 2;
|
||||||
|
const Y_TIME = 30;
|
||||||
|
const Y_ACTIVITY = 116;
|
||||||
|
const Y_MODELINE = 200;
|
||||||
|
|
||||||
|
function init(gps) {
|
||||||
|
showMem("digi init 1");
|
||||||
|
days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday","Friday", "Saturday"];
|
||||||
|
prevInfo = "";
|
||||||
|
prevTimeStr = "";
|
||||||
|
prevDateStr = "";
|
||||||
|
infoMode = INFO_NONE;
|
||||||
|
g.clear();
|
||||||
|
showMem("digi init 2");
|
||||||
|
}
|
||||||
|
|
||||||
|
function freeResources() {
|
||||||
|
showMem("digi free 1");
|
||||||
|
days = undefined;
|
||||||
|
prevInfo = undefined;
|
||||||
|
prevTime = undefined;
|
||||||
|
prevDate = undefined;
|
||||||
|
showMem("digi free 2");
|
||||||
|
}
|
||||||
|
|
||||||
|
function showMem(msg) {
|
||||||
|
var val = process.memory();
|
||||||
|
var str = msg + " " + Math.round(val.usage*100/val.total) + "%";
|
||||||
|
//console.log(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
function startTimer() {
|
||||||
|
draw();
|
||||||
|
intervalRefSec = setInterval(draw, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopTimer() {
|
||||||
|
if(intervalRefSec) {intervalRefSec=clearInterval(intervalRefSec);}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onButtonShort(btn) {
|
||||||
|
if (btn === 1) cycleInfoMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onButtonLong(btn) {}
|
||||||
|
function getGPSfix() { return undefined; }
|
||||||
|
function setGPSfix(f) {}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
var d = new Date();
|
||||||
|
var da = d.toString().split(" ");
|
||||||
|
var time = da[4].substr(0,5);
|
||||||
|
|
||||||
|
if (time !== prevTime) {
|
||||||
|
prevTime = time;
|
||||||
|
g.setColor(0);
|
||||||
|
g.fillRect(0, Y_TIME, 239, Y_ACTIVITY -1);
|
||||||
|
g.setColor(1,1,1);
|
||||||
|
g.setFont("Vector",80);
|
||||||
|
g.setFontAlign(0,-1);
|
||||||
|
g.drawString(time, 120, Y_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
var day = days[d.getDay()];
|
||||||
|
var dateStr = da[2] + " " + da[1] + " " + da[3];
|
||||||
|
|
||||||
|
if (dateStr !== prevDate) {
|
||||||
|
prevDate = dateStr;
|
||||||
|
g.setColor(0);
|
||||||
|
g.fillRect(0, Y_ACTIVITY, 239, Y_MODELINE - 3);
|
||||||
|
g.setColor(1,1,1);
|
||||||
|
g.setFont("Vector",26);
|
||||||
|
g.drawString(day, 120, Y_ACTIVITY);
|
||||||
|
g.drawString(dateStr, 120, Y_ACTIVITY + 40);
|
||||||
|
}
|
||||||
|
|
||||||
|
drawInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
function cycleInfoMode() {
|
||||||
|
switch(infoMode) {
|
||||||
|
case INFO_NONE:
|
||||||
|
infoMode = INFO_BATT;
|
||||||
|
break;
|
||||||
|
case INFO_BATT:
|
||||||
|
infoMode = INFO_MEM
|
||||||
|
break;
|
||||||
|
case INFO_MEM:
|
||||||
|
default:
|
||||||
|
infoMode = INFO_NONE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
drawInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawInfo() {
|
||||||
|
let val;
|
||||||
|
let str = "";
|
||||||
|
let col = 0x07FF; // cyan
|
||||||
|
|
||||||
|
switch(infoMode) {
|
||||||
|
case INFO_NONE:
|
||||||
|
col = 0x0000;
|
||||||
|
str = "";
|
||||||
|
break;
|
||||||
|
case INFO_MEM:
|
||||||
|
val = process.memory();
|
||||||
|
str = "Memory: " + Math.round(val.usage*100/val.total) + "%";
|
||||||
|
break;
|
||||||
|
case INFO_BATT:
|
||||||
|
default:
|
||||||
|
str = "Battery: " + E.getBattery() + "%";
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we need to draw, avoid flicker
|
||||||
|
if (str == prevInfo)
|
||||||
|
return;
|
||||||
|
|
||||||
|
prevInfo = str;
|
||||||
|
//g.setFont("6x8", 3);
|
||||||
|
g.setFont("Vector",26);
|
||||||
|
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, 120, Y_MODELINE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {init:init, freeResources:freeResources, startTimer:startTimer, stopTimer:stopTimer,
|
||||||
|
onButtonShort:onButtonShort, onButtonLong:onButtonLong};
|
||||||
|
}
|
||||||
|
|
||||||
|
return getFace;
|
||||||
|
})();
|
|
@ -0,0 +1,182 @@
|
||||||
|
(() => {
|
||||||
|
function getFace(){
|
||||||
|
var intervalRefSec;
|
||||||
|
|
||||||
|
const GDISP_OS = 4;
|
||||||
|
const GDISP_LATLN = 5;
|
||||||
|
const GDISP_SPEED = 6;
|
||||||
|
const GDISP_ALT = 7;
|
||||||
|
const GDISP_COURSE = 8;
|
||||||
|
|
||||||
|
const Y_TIME = 30;
|
||||||
|
const Y_ACTIVITY = 120;
|
||||||
|
const Y_MODELINE = 200;
|
||||||
|
|
||||||
|
let gpsDisplay = GDISP_OS;
|
||||||
|
let clearActivityArea = true;
|
||||||
|
let gpsObject = undefined;
|
||||||
|
|
||||||
|
function log_debug(o) {
|
||||||
|
//console.log(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
function init(gps) {
|
||||||
|
log_debug("gps init");
|
||||||
|
//log_debug(gps);
|
||||||
|
gpsObject = gps;
|
||||||
|
gpsDisplay = GDISP_OS;
|
||||||
|
clearActivityArea = true;
|
||||||
|
gpsObject.determineGPSState();
|
||||||
|
}
|
||||||
|
|
||||||
|
function freeResources() {}
|
||||||
|
|
||||||
|
function startTimer() {
|
||||||
|
draw();
|
||||||
|
intervalRefSec = setInterval(draw, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopTimer() {
|
||||||
|
if(intervalRefSec) {intervalRefSec=clearInterval(intervalRefSec);}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onButtonShort(btn) {
|
||||||
|
if (btn === 1) cycleGPSDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onButtonLong(btn) {
|
||||||
|
if (btn === 1) toggleGPSPower();
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw(){
|
||||||
|
drawGPSTime();
|
||||||
|
drawGPSData();
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawGPSTime() {
|
||||||
|
var time = gpsObject.getGPSTime();
|
||||||
|
|
||||||
|
g.reset();
|
||||||
|
g.clearRect(0,Y_TIME, 239, Y_ACTIVITY - 1);
|
||||||
|
g.setColor(1,1,1);
|
||||||
|
g.setFontAlign(0, -1);
|
||||||
|
|
||||||
|
if (time.length > 5)
|
||||||
|
g.setFont("Vector", 56);
|
||||||
|
else
|
||||||
|
g.setFont("Vector", 80);
|
||||||
|
|
||||||
|
g.drawString(time, 120, Y_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawGPSData() {
|
||||||
|
if (clearActivityArea) {
|
||||||
|
g.clearRect(0, Y_ACTIVITY, 239, Y_MODELINE - 1);
|
||||||
|
clearActivityArea = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
g.setFontVector(26);
|
||||||
|
g.setColor(0xFFC0);
|
||||||
|
g.setFontAlign(0, -1);
|
||||||
|
|
||||||
|
if (gpsObject.getState() === gpsObject.GPS_OFF) {
|
||||||
|
g.drawString("GPS off", 120, Y_ACTIVITY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gpsObject.getState() === gpsObject.GPS_TIME) {
|
||||||
|
g.drawString("Waiting for", 120, Y_ACTIVITY);
|
||||||
|
g.drawString("GPS", 120, Y_ACTIVITY + 36);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let fx = gpsObject.getLastFix();
|
||||||
|
|
||||||
|
log_debug("gpsObject.getState()= " + gpsObject.getState());
|
||||||
|
|
||||||
|
if (gpsObject.getState() === gpsObject.GPS_SATS) {
|
||||||
|
g.drawString("Satellites", 120, Y_ACTIVITY);
|
||||||
|
g.drawString(fx.satellites, 120, Y_ACTIVITY + 36);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gpsObject.getState() === gpsObject.GPS_RUNNING) {
|
||||||
|
let time = gpsObject.formatTime(fx.time);
|
||||||
|
let age = gpsObject.timeSince(time);
|
||||||
|
let os = gpsObject.getOsRef();
|
||||||
|
//let ref = to_map_ref(6, os.easting, os.northing);
|
||||||
|
let speed;
|
||||||
|
let activityStr = "";
|
||||||
|
|
||||||
|
if (age < 0) age = 0;
|
||||||
|
g.setFontVector(40);
|
||||||
|
g.setColor(0xFFC0);
|
||||||
|
|
||||||
|
switch(gpsDisplay) {
|
||||||
|
case GDISP_OS:
|
||||||
|
activityStr = os;
|
||||||
|
break;
|
||||||
|
case GDISP_LATLN:
|
||||||
|
g.setFontVector(26);
|
||||||
|
activityStr = fx.lat.toFixed(4) + ", " + fx.lon.toFixed(4);
|
||||||
|
break;
|
||||||
|
case GDISP_SPEED:
|
||||||
|
speed = fx.speed;
|
||||||
|
speed = speed.toFixed(1);
|
||||||
|
activityStr = speed + "kph";
|
||||||
|
break;
|
||||||
|
case GDISP_ALT:
|
||||||
|
activityStr = fx.alt + "m";
|
||||||
|
break;
|
||||||
|
case GDISP_COURSE:
|
||||||
|
activityStr = fx.course;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
g.clearRect(0, Y_ACTIVITY, 239, Y_MODELINE - 1);
|
||||||
|
g.drawString(activityStr, 120, Y_ACTIVITY);
|
||||||
|
g.setFont("6x8",2);
|
||||||
|
g.setColor(1,1,1);
|
||||||
|
g.drawString(age, 120, Y_ACTIVITY + 46);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleGPSPower() {
|
||||||
|
gpsObject.toggleGPSPower();
|
||||||
|
clearActivityArea = true;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
function cycleGPSDisplay() {
|
||||||
|
if (gpsObject.getState() !== gpsObject.GPS_RUNNING) return;
|
||||||
|
|
||||||
|
switch (gpsDisplay) {
|
||||||
|
case GDISP_OS:
|
||||||
|
gpsDisplay = GDISP_SPEED;
|
||||||
|
break;
|
||||||
|
case GDISP_SPEED:
|
||||||
|
gpsDisplay = GDISP_ALT;
|
||||||
|
break;
|
||||||
|
case GDISP_ALT:
|
||||||
|
gpsDisplay = GDISP_LATLN;
|
||||||
|
break;
|
||||||
|
case GDISP_LATLN:
|
||||||
|
gpsDisplay = GDISP_COURSE;
|
||||||
|
break;
|
||||||
|
case GDISP_COURSE:
|
||||||
|
default:
|
||||||
|
gpsDisplay = GDISP_OS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearActivityArea = true;
|
||||||
|
drawGPSData();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {init:init, freeResources:freeResources, startTimer:startTimer, stopTimer:stopTimer,
|
||||||
|
onButtonShort:onButtonShort, onButtonLong:onButtonLong};
|
||||||
|
}
|
||||||
|
|
||||||
|
return getFace;
|
||||||
|
|
||||||
|
})();
|
|
@ -0,0 +1,482 @@
|
||||||
|
// read in the faces
|
||||||
|
var FACES = [];
|
||||||
|
var STOR = require("Storage");
|
||||||
|
STOR.list(/\.kit\.js$/).forEach(face=>FACES.push(eval(require("Storage").read(face))));
|
||||||
|
var iface = STOR.list(/\.kit\.js$/).indexOf("stepo.kit.js");
|
||||||
|
var face = FACES[iface]();
|
||||||
|
var firstPress
|
||||||
|
var pressTimer;
|
||||||
|
|
||||||
|
function stopdraw() {
|
||||||
|
face.stopTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
function startdraw() {
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
face.startTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextFace(){
|
||||||
|
stopdraw();
|
||||||
|
face.freeResources();
|
||||||
|
|
||||||
|
iface += 1
|
||||||
|
iface = iface % FACES.length;
|
||||||
|
face = FACES[iface]();
|
||||||
|
|
||||||
|
g.clear();
|
||||||
|
g.reset();
|
||||||
|
face.init(gpsObj);
|
||||||
|
startdraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
// when you feel the buzzer you know you have done a long press
|
||||||
|
function longPressCheck() {
|
||||||
|
Bangle.buzz();
|
||||||
|
if (pressTimer) {
|
||||||
|
clearInterval(pressTimer);
|
||||||
|
pressTimer = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// start a timer and buzz when held long enough
|
||||||
|
function buttonPressed(btn) {
|
||||||
|
if (btn === 3) {
|
||||||
|
nextFace();
|
||||||
|
} else {
|
||||||
|
firstPress = getTime();
|
||||||
|
pressTimer = setInterval(longPressCheck, 1500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if you release too soon there is no buzz as timer is cleared
|
||||||
|
function buttonReleased(btn) {
|
||||||
|
var dur = getTime() - firstPress;
|
||||||
|
if (pressTimer) {
|
||||||
|
clearInterval(pressTimer);
|
||||||
|
pressTimer = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( dur >= 1.5 ) {
|
||||||
|
switch(btn) {
|
||||||
|
case 1:
|
||||||
|
face.onButtonLong(btn);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
Bangle.showLauncher();
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
// do nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btn !== 3) face.onButtonShort(btn);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setButtons(){
|
||||||
|
setWatch(buttonPressed.bind(null,1), BTN1, {repeat:true,edge:"rising"});
|
||||||
|
setWatch(buttonPressed.bind(null,2), BTN2, {repeat:true,edge:"rising"});
|
||||||
|
setWatch(nextFace, BTN3, {repeat:true,edge:"rising"});
|
||||||
|
|
||||||
|
setWatch(buttonReleased.bind(null,1), BTN1, {repeat:true,edge:"falling"});
|
||||||
|
setWatch(buttonReleased.bind(null,2), BTN2, {repeat:true,edge:"falling"});
|
||||||
|
// BTN 3 long press should always reset the bangle
|
||||||
|
}
|
||||||
|
|
||||||
|
Bangle.on('kill',()=>{
|
||||||
|
Bangle.setCompassPower(0);
|
||||||
|
Bangle.setGPSPower(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
Bangle.on('lcdPower',function(on) {
|
||||||
|
if (on) {
|
||||||
|
startdraw();
|
||||||
|
} else {
|
||||||
|
stopdraw();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
Start of GPS object code so we can share it between faces
|
||||||
|
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
function log_debug(o) {
|
||||||
|
//console.log(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
function radians(a) {
|
||||||
|
return a*Math.PI/180;
|
||||||
|
}
|
||||||
|
|
||||||
|
function degrees(a) {
|
||||||
|
var d = a*180/Math.PI;
|
||||||
|
return (d+360)%360;
|
||||||
|
}
|
||||||
|
|
||||||
|
function GPS() {
|
||||||
|
this.wp;
|
||||||
|
this.wp_index = 0;
|
||||||
|
this.wp_current = undefined;
|
||||||
|
this.GPS_OFF = 0;
|
||||||
|
this.GPS_TIME = 1;
|
||||||
|
this.GPS_SATS = 2;
|
||||||
|
this.GPS_RUNNING = 3;
|
||||||
|
this.gpsState = this.GPS_OFF;
|
||||||
|
this.gpsPowerState = false;
|
||||||
|
this.last_fix = {
|
||||||
|
fix: 0,
|
||||||
|
alt: 0,
|
||||||
|
lat: 0,
|
||||||
|
lon: 0,
|
||||||
|
speed: 0,
|
||||||
|
time: 0,
|
||||||
|
satellites: 0
|
||||||
|
};
|
||||||
|
this.listenerCount = 0;
|
||||||
|
this.loadFirstWaypoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
GPS.prototype.log_debug = function(o) {
|
||||||
|
//console.log(o);
|
||||||
|
};
|
||||||
|
|
||||||
|
GPS.prototype.getState = function() {
|
||||||
|
return this.gpsState;
|
||||||
|
}
|
||||||
|
|
||||||
|
GPS.prototype.getLastFix = function() {
|
||||||
|
return this.last_fix;
|
||||||
|
}
|
||||||
|
|
||||||
|
GPS.prototype.determineGPSState = function() {
|
||||||
|
this.log_debug("determineGPSState");
|
||||||
|
gpsPowerState = Bangle.isGPSOn();
|
||||||
|
|
||||||
|
//this.log_debug("last_fix.fix " + this.last_fix.fix);
|
||||||
|
//this.log_debug("gpsPowerState " + this.gpsPowerState);
|
||||||
|
//this.log_debug("last_fix.satellites " + this.last_fix.satellites);
|
||||||
|
|
||||||
|
if (!gpsPowerState) {
|
||||||
|
this.gpsState = this.GPS_OFF;
|
||||||
|
this.resetLastFix();
|
||||||
|
} else if (this.last_fix.fix && this.gpsPowerState && this.last_fix.satellites > 0) {
|
||||||
|
this.gpsState = this.GPS_RUNNING;
|
||||||
|
} else {
|
||||||
|
this.gpsState = this.GPS_SATS;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.log_debug("gpsState=" + this.gpsState);
|
||||||
|
|
||||||
|
if (this.gpsState !== this.GPS_OFF) {
|
||||||
|
if (this.listenerCount === 0) {
|
||||||
|
Bangle.on('GPS', processFix);
|
||||||
|
this.listenerCount++;
|
||||||
|
this.log_debug("listener added " + this.listenerCount);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.listenerCount > 0) {
|
||||||
|
Bangle.removeListener("GPS", this.processFix);
|
||||||
|
this.listenerCount--;
|
||||||
|
this.log_debug("listener removed " + this.listenerCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
GPS.prototype.getGPSTime = function() {
|
||||||
|
var time;
|
||||||
|
|
||||||
|
if (this.last_fix !== undefined && this.last_fix.time !== undefined && this.last_fix.time.toUTCString !== undefined &&
|
||||||
|
(this.gpsState == this.GPS_SATS || this.gpsState == this.GPS_RUNNING)) {
|
||||||
|
time = this.last_fix.time.toUTCString().split(" ");
|
||||||
|
return time[4];
|
||||||
|
} else {
|
||||||
|
var d = new Date();
|
||||||
|
var da = d.toString().split(" ");
|
||||||
|
time = da[4].substr(0,5);
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
GPS.prototype.toggleGPSPower = function() {
|
||||||
|
this.log_debug("toggleGPSPower()");
|
||||||
|
this.gpsPowerState = Bangle.isGPSOn();
|
||||||
|
this.gpsPowerState = !this.gpsPowerState;
|
||||||
|
Bangle.setGPSPower(this.gpsPowerState ? 1 : 0);
|
||||||
|
|
||||||
|
this.resetLastFix();
|
||||||
|
this.determineGPSState();
|
||||||
|
|
||||||
|
// poke the gps widget indicator to change
|
||||||
|
if (WIDGETS.gps !== undefined) {
|
||||||
|
WIDGETS.gps.draw();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
GPS.prototype.resetLastFix = function() {
|
||||||
|
this.last_fix = {
|
||||||
|
fix: 0,
|
||||||
|
alt: 0,
|
||||||
|
lat: 0,
|
||||||
|
lon: 0,
|
||||||
|
speed: 0,
|
||||||
|
time: 0,
|
||||||
|
satellites: 0
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
function processFix(fix) {
|
||||||
|
//log_debug("processFix()");
|
||||||
|
gpsObj.processFix(fix);
|
||||||
|
}
|
||||||
|
|
||||||
|
GPS.prototype.processFix = function(fix) {
|
||||||
|
//this.log_debug("GPS:processFix()");
|
||||||
|
//this.log_debug(fix);
|
||||||
|
this.last_fix.time = fix.time;
|
||||||
|
|
||||||
|
if (this.gpsState == this.GPS_TIME) {
|
||||||
|
this.gpsState = this.GPS_SATS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fix.fix) {
|
||||||
|
//this.log_debug("Got fix - setting state to GPS_RUNNING");
|
||||||
|
this.gpsState = this.GPS_RUNNING;
|
||||||
|
if (!this.last_fix.fix) Bangle.buzz(); // buzz on first position
|
||||||
|
this.last_fix = fix;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
GPS.prototype.formatTime = function(now) {
|
||||||
|
var fd = now.toUTCString().split(" ");
|
||||||
|
return fd[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
GPS.prototype.timeSince = function(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);
|
||||||
|
};
|
||||||
|
|
||||||
|
GPS.prototype.getOsRef = function() {
|
||||||
|
let os = OsGridRef.latLongToOsGrid(this.last_fix);
|
||||||
|
let ref = to_map_ref(6, os.easting, os.northing);
|
||||||
|
return ref;
|
||||||
|
};
|
||||||
|
|
||||||
|
GPS.prototype.calcBearing = function(a,b) {
|
||||||
|
var delta = radians(b.lon-a.lon);
|
||||||
|
var alat = radians(a.lat);
|
||||||
|
var blat = radians(b.lat);
|
||||||
|
var y = Math.sin(delta) * Math.cos(blat);
|
||||||
|
var x = Math.cos(alat)*Math.sin(blat) -
|
||||||
|
Math.sin(alat)*Math.cos(blat)*Math.cos(delta);
|
||||||
|
return Math.round(degrees(Math.atan2(y, x)));
|
||||||
|
}
|
||||||
|
|
||||||
|
GPS.prototype.calcDistance = function(a,b) {
|
||||||
|
var x = radians(a.lon-b.lon) * Math.cos(radians((a.lat+b.lat)/2));
|
||||||
|
var y = radians(b.lat-a.lat);
|
||||||
|
return Math.round(Math.sqrt(x*x + y*y) * 6371000);
|
||||||
|
}
|
||||||
|
|
||||||
|
GPS.prototype.getWPdistance = function() {
|
||||||
|
//log_debug(this.last_fix);
|
||||||
|
//log_debug(this.wp_current);
|
||||||
|
|
||||||
|
if (this.wp_current.name === "NONE" || this.wp_current.lat === undefined || this.wp_current.lat === 0)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return this.calcDistance(this.last_fix, this.wp_current);
|
||||||
|
}
|
||||||
|
|
||||||
|
GPS.prototype.getWPbearing = function() {
|
||||||
|
//log_debug(this.last_fix);
|
||||||
|
//log_debug(this.wp_current);
|
||||||
|
|
||||||
|
if (this.wp_current.name === "NONE" || this.wp_current.lat === undefined || this.wp_current.lat === 0)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return this.calcBearing(this.last_fix, this.wp_current);
|
||||||
|
}
|
||||||
|
|
||||||
|
GPS.prototype.loadFirstWaypoint = function() {
|
||||||
|
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"NONE"}];
|
||||||
|
this.wp_index = 0;
|
||||||
|
this.wp_current = waypoints[this.wp_index];
|
||||||
|
log_debug(this.wp_current);
|
||||||
|
return this.wp_current;
|
||||||
|
}
|
||||||
|
|
||||||
|
GPS.prototype.getCurrentWaypoint = function() {
|
||||||
|
return this.wp_current;
|
||||||
|
}
|
||||||
|
|
||||||
|
GPS.prototype.waypointHasLocation = function() {
|
||||||
|
if (this.wp_current.name === "NONE" || this.wp_current.lat === undefined || this.wp_current.lat === 0)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
GPS.prototype.markWaypoint = function() {
|
||||||
|
|
||||||
|
if(this.wp_current.name === "NONE")
|
||||||
|
return;
|
||||||
|
|
||||||
|
log_debug("GPS::markWaypoint()");
|
||||||
|
|
||||||
|
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"NONE"}];
|
||||||
|
this.wp_current = waypoints[this.wp_index];
|
||||||
|
|
||||||
|
if (this.waypointHasLocation()) {
|
||||||
|
waypoints[this.wp_index] = {name:this.wp_current.name, lat:0, lon:0};
|
||||||
|
} else {
|
||||||
|
waypoints[this.wp_index] = {name:this.wp_current.name, lat:this.last_fix.lat, lon:this.last_fix.lon};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.wp_current = waypoints[this.wp_index];
|
||||||
|
require("Storage").writeJSON("waypoints.json", waypoints);
|
||||||
|
log_debug("GPS::markWaypoint() written");
|
||||||
|
}
|
||||||
|
|
||||||
|
GPS.prototype.nextWaypoint = function(inc) {
|
||||||
|
var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"NONE"}];
|
||||||
|
this.wp_index+=inc;
|
||||||
|
if (this.wp_index>=waypoints.length) this.wp_index=0;
|
||||||
|
if (this.wp_index<0) this.wp_index = waypoints.length-1;
|
||||||
|
this.wp_current = waypoints[this.wp_index];
|
||||||
|
log_debug(this.wp_current);
|
||||||
|
return this.wp_current;
|
||||||
|
}
|
||||||
|
|
||||||
|
var gpsObj = new GPS();
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
Start of OS lat lon to grid ref code
|
||||||
|
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
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}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
End of GPS object
|
||||||
|
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
g.clear();
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
face.init(gpsObj);
|
||||||
|
startdraw();
|
||||||
|
setButtons();
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/AH4A/AH4AVgAACF1crnNkF9QuBLoMHF9cyGIYuprovBX1cAsgABrovYJSBeBF4SPBFy8HmQbOLwdkFzAtBmYcNCIIuCYAIvXLgJ/BF5pecAAy9OdqgmCg8rmUznMyAAJkCEI5eEdqQgBU4YAJrsHEYheErwuSFpoxEEoa9WIwgwTgFeAwQvQLqQwFaYa7TLyYuBmQYBdKQuCIoUrlcyABcrKwJcWXgZJBEIQiBAAQlEEwI+BmU5CgINDF6ZgFGQI3BAAkzLw8OLygvBg8VhwaBLY5eEg8OisVjgABhwvVhwaCACgv/F78VF/4v/F8sHd9owBF68HFyhgYRyowDYQMVFqMHFy4xEAAMHg41BAAIrFiq6BAAIuZGhIAIFj4A/AH4A/AH4AWA=="))
|
|
@ -0,0 +1,8 @@
|
||||||
|
require("Storage").write("kitchen.info",{
|
||||||
|
"id":"kitchen",
|
||||||
|
"name":"Kitchen",
|
||||||
|
"src":"kitchen.app.js",
|
||||||
|
"icon":"kitchen.img",
|
||||||
|
"type":"clock",
|
||||||
|
"files":"kitchen.info, kitchen.app.js, kitchen.img, stepo.kit.js, digi.kit.js, compass.kit.js, gps.kit.js"
|
||||||
|
});
|
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 78 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 81 KiB |
After Width: | Height: | Size: 64 KiB |
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 51 KiB |
|
@ -0,0 +1,136 @@
|
||||||
|
(() => {
|
||||||
|
function getFace(){
|
||||||
|
var pal4color;
|
||||||
|
var pal4red;
|
||||||
|
var buf;
|
||||||
|
var intervalRefSec;
|
||||||
|
|
||||||
|
function init(g) {
|
||||||
|
showMem("stepo init 1");
|
||||||
|
pal4color = new Uint16Array([0x0000,0xFFFF,0x7BEF,0xAFE5],0,2); // b,w,grey,greenyellow
|
||||||
|
pal4red = new Uint16Array([0x0000,0xFFFF,0xF800,0xAFE5],0,2); // b,w,red,greenyellow
|
||||||
|
buf = Graphics.createArrayBuffer(120,120,2,{msb:true});
|
||||||
|
showMem("stepo init 2");
|
||||||
|
}
|
||||||
|
|
||||||
|
function freeResources() {
|
||||||
|
showMem("stepo free 1");
|
||||||
|
pal4color = undefined;
|
||||||
|
pal4red = undefined;
|
||||||
|
buf = undefined;
|
||||||
|
showMem("stepo free 2");
|
||||||
|
}
|
||||||
|
|
||||||
|
function showMem(msg) {
|
||||||
|
var val = process.memory();
|
||||||
|
var str = msg + " " + Math.round(val.usage*100/val.total) + "%";
|
||||||
|
//console.log(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
function flip(x,y) {
|
||||||
|
g.drawImage({width:120,height:120,bpp:2,buffer:buf.buffer, palette:pal4color}, x, y);
|
||||||
|
buf.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
function flip_red(x,y) {
|
||||||
|
g.drawImage({width:120,height:120,bpp:2,buffer:buf.buffer, palette:pal4red}, x, y);
|
||||||
|
buf.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onButtonShort(btn) {}
|
||||||
|
function onButtonLong(btn) {}
|
||||||
|
|
||||||
|
function radians(a) {
|
||||||
|
return a*Math.PI/180;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startTimer() {
|
||||||
|
draw();
|
||||||
|
intervalRefSec = setInterval(draw, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopTimer() {
|
||||||
|
if(intervalRefSec) {intervalRefSec=clearInterval(intervalRefSec);}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawSteps() {
|
||||||
|
var i = 0;
|
||||||
|
var cx = 60;
|
||||||
|
var cy = 60;
|
||||||
|
var r = 56;
|
||||||
|
var steps = getSteps();
|
||||||
|
var percent = steps / 10000;
|
||||||
|
|
||||||
|
if (percent > 1) percent = 1;
|
||||||
|
|
||||||
|
var startrot = 0 - 180;
|
||||||
|
var midrot = -180 - (360 * percent);
|
||||||
|
var endrot = -360 - 180;
|
||||||
|
|
||||||
|
buf.setColor(3); // green-yellow
|
||||||
|
|
||||||
|
// draw guauge
|
||||||
|
for (i = startrot; i > midrot; i -= 4) {
|
||||||
|
x = cx + r * Math.sin(radians(i));
|
||||||
|
y = cy + r * Math.cos(radians(i));
|
||||||
|
buf.fillCircle(x,y,4);
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.setColor(2); // grey
|
||||||
|
|
||||||
|
// draw remainder of guage in grey
|
||||||
|
for (i = midrot; i > endrot; i -= 4) {
|
||||||
|
x = cx + r * Math.sin(radians(i));
|
||||||
|
y = cy + r * Math.cos(radians(i));
|
||||||
|
buf.fillCircle(x,y,4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw steps
|
||||||
|
buf.setColor(1); // white
|
||||||
|
buf.setFont("Vector", 24);
|
||||||
|
buf.setFontAlign(0,0);
|
||||||
|
buf.drawString(steps, cx, cy);
|
||||||
|
|
||||||
|
// change the remaining color to RED if battery is below 25%
|
||||||
|
if (E.getBattery() > 25)
|
||||||
|
flip(60,115);
|
||||||
|
else
|
||||||
|
flip_red(60,115);
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
var d = new Date();
|
||||||
|
var da = d.toString().split(" ");
|
||||||
|
var time = da[4].substr(0,5);
|
||||||
|
|
||||||
|
g.clearRect(0, 30, 239, 99);
|
||||||
|
g.setColor(1,1,1);
|
||||||
|
g.setFontAlign(0, -1);
|
||||||
|
g.setFont("Vector", 80);
|
||||||
|
g.drawString(time, 120, 30, true);
|
||||||
|
|
||||||
|
drawSteps();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {init:init, freeResources:freeResources, startTimer:startTimer, stopTimer:stopTimer,
|
||||||
|
onButtonShort:onButtonShort, onButtonLong:onButtonLong};
|
||||||
|
}
|
||||||
|
|
||||||
|
return getFace;
|
||||||
|
|
||||||
|
})();
|
|
@ -0,0 +1,170 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||||
|
<link rel="stylesheet" href="../../css/spectre-icons.min.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h4>List of waypoints</h4>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Lat.</th>
|
||||||
|
<th>Long.</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="waypoints">
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<br>
|
||||||
|
<h4>Add a new waypoint</h4>
|
||||||
|
<form id="add_waypoint_form">
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column col-3 col-xs-8">
|
||||||
|
<input class="form-input input-sm" type="text" id="add_waypoint_name" placeholder="Name">
|
||||||
|
</div>
|
||||||
|
<div class="column col-3 col-xs-8">
|
||||||
|
<input class="form-input input-sm" value="0.0000" type="number" step="any" id="add_latitude" placeholder="Lat">
|
||||||
|
</div>
|
||||||
|
<div class="column col-3 col-xs-8">
|
||||||
|
<input class="form-input input-sm" value="0.0000" type="number" step="any" id="add_longtitude" placeholder="Long">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column col-3 col-xs-8">
|
||||||
|
<button id="add_name_button" class="btn btn-primary btn-sm">Add Name Only</button>
|
||||||
|
</div>
|
||||||
|
<div class="column col-3 col-xs-8">
|
||||||
|
<button id="add_waypoint_button" class="btn btn-primary btn-sm">Add Waypoint</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<br>
|
||||||
|
<button id="Download" class="btn btn-error">Reload</button> <button id="Upload" class="btn btn-primary">Upload</button>
|
||||||
|
|
||||||
|
<script src="../../core/lib/interface.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var waypoints = []
|
||||||
|
|
||||||
|
var $name = document.getElementById('add_waypoint_name')
|
||||||
|
var $form = document.getElementById('add_waypoint_form')
|
||||||
|
var $button = document.getElementById('add_waypoint_button')
|
||||||
|
var $name_button = document.getElementById('add_name_button')
|
||||||
|
var $latitude = document.getElementById('add_latitude')
|
||||||
|
var $longtitude = document.getElementById('add_longtitude')
|
||||||
|
var $list = document.getElementById('waypoints')
|
||||||
|
|
||||||
|
function compare(a, b){
|
||||||
|
var x = a.name.toLowerCase();
|
||||||
|
var y = b.name.toLowerCase();
|
||||||
|
if (x=="none") {return -1};
|
||||||
|
if (y=="none") {return 1};
|
||||||
|
if (x < y) {return -1;}
|
||||||
|
if (x > y) {return 1;}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$button.addEventListener('click', event => {
|
||||||
|
event.preventDefault()
|
||||||
|
var name = $name.value.trim()
|
||||||
|
if(!name) return;
|
||||||
|
var lat = parseFloat($latitude.value).toPrecision(5);
|
||||||
|
var lon = parseFloat($longtitude.value).toPrecision(5);
|
||||||
|
|
||||||
|
waypoints.push({
|
||||||
|
name, lat,lon,
|
||||||
|
});
|
||||||
|
|
||||||
|
waypoints.sort(compare);
|
||||||
|
|
||||||
|
renderWaypoints()
|
||||||
|
$name.value = ''
|
||||||
|
$latitude.value = (0).toPrecision(5);
|
||||||
|
$longtitude.value = (0).toPrecision(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
$name_button.addEventListener('click', event => {
|
||||||
|
event.preventDefault()
|
||||||
|
var name = $name.value.trim()
|
||||||
|
if(!name) return;
|
||||||
|
|
||||||
|
waypoints.push({
|
||||||
|
name
|
||||||
|
});
|
||||||
|
waypoints.sort(compare);
|
||||||
|
|
||||||
|
renderWaypoints()
|
||||||
|
$name.value = ''
|
||||||
|
$latitude.value = 0.0000
|
||||||
|
$longtitude.value = 0.0000
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function removeWaypoint(index){
|
||||||
|
$name.value = waypoints[index].name
|
||||||
|
$latitude.value = waypoints[index].lat
|
||||||
|
$longtitude.value = waypoints[index].lon
|
||||||
|
waypoints = waypoints.filter((p,i) => i!==index)
|
||||||
|
renderWaypoints()
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderWaypoints(){
|
||||||
|
$list.innerHTML = ''
|
||||||
|
waypoints.forEach((waypoint,index) => {
|
||||||
|
var $waypoint = document.createElement('tr')
|
||||||
|
if (index==0){
|
||||||
|
$waypoint.innerHTML = `<td>${waypoint.name}</td>`
|
||||||
|
} else if(waypoint.lat==undefined){
|
||||||
|
$waypoint.innerHTML = `<td>${waypoint.name}</td><td>------</td><td>-----</td><td><button class="btn btn-action btn-primary" onclick="removeWaypoint(${index})"><i class="icon icon-edit"></i></button></td>`
|
||||||
|
} else {
|
||||||
|
$waypoint.innerHTML = `<td>${waypoint.name}</td><td>${waypoint.lat}</td><td>${waypoint.lon}</td><td><button class="btn btn-action btn-primary" onclick="removeWaypoint(${index})"><i class="icon icon-edit"></i></button></td>`
|
||||||
|
}
|
||||||
|
$list.appendChild($waypoint)
|
||||||
|
})
|
||||||
|
$name.focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadJSONfile(fileid, callback) {
|
||||||
|
Puck.write(`\x10(function() {
|
||||||
|
var pts = require("Storage").readJSON("${fileid}")||[{name:"NONE"}];
|
||||||
|
Bluetooth.print(JSON.stringify(pts));
|
||||||
|
})()\n`,contents=>{
|
||||||
|
var storedpts = JSON.parse(contents);
|
||||||
|
callback(storedpts);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function uploadFile(fileid, contents) {
|
||||||
|
Puck.write(`\x10(function() {
|
||||||
|
require("Storage").write("${fileid}",'${contents}');
|
||||||
|
Bluetooth.print("OK");
|
||||||
|
})()\n`,ret=>{
|
||||||
|
console.log("uploadFile",ret);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function gotStored(pts){
|
||||||
|
waypoints = pts;
|
||||||
|
renderWaypoints();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onInit() {
|
||||||
|
downloadJSONfile("waypoints.json", gotStored);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("Download").addEventListener("click", function() {
|
||||||
|
downloadJSONfile("waypoints.json", gotStored);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("Upload").addEventListener("click", function() {
|
||||||
|
var data = JSON.stringify(waypoints);
|
||||||
|
uploadFile("waypoints.json",data);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,20 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name":"NONE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"No10",
|
||||||
|
"lat":51.5032,
|
||||||
|
"lon":-0.1269
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"Stone",
|
||||||
|
"lat":51.1788,
|
||||||
|
"lon":-1.8260
|
||||||
|
},
|
||||||
|
{ "name":"WP0" },
|
||||||
|
{ "name":"WP1" },
|
||||||
|
{ "name":"WP2" },
|
||||||
|
{ "name":"WP3" },
|
||||||
|
{ "name":"WP4" }
|
||||||
|
]
|