Merge pull request #706 from hughbarney/master

Kitchen Combo - multiclock combo of Stepo, Walkersclock, Waypointer and Arrow, can quickly switch between
pull/707/head^2
Gordon Williams 2021-03-25 16:07:23 +00:00 committed by GitHub
commit 6c481a118b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 1669 additions and 1 deletions

View File

@ -3041,5 +3041,23 @@
"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}
]
}
]

199
apps/kitchen/README.md Normal file
View File

@ -0,0 +1,199 @@
# Kitchen Combo - a multiclock format of the waypointer, walksersclock, stopo and arrow apps.
![](screenshot_kitchen.jpg)
*...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
![](screenshot_stepo.jpg)
- 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
![](screenshot_gps.jpg)
- 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
![](screenshot_digi.jpg)
- 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.
![](screenshot_stone.jpg)
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.
![](screenshot_wp2_cleared.jpg)
Bearing and distance are both zero as WP2 has currently no GPS
location associated with it. To mark the location, long press BTN1.
![](screenshot_wp2_set.jpg)
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`.

306
apps/kitchen/compass.kit.js Normal file
View File

@ -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;
})();

146
apps/kitchen/digi.kit.js Normal file
View File

@ -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;
})();

182
apps/kitchen/gps.kit.js Normal file
View File

@ -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;
})();

482
apps/kitchen/kitchen.app.js Normal file
View File

@ -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();

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/AH4A/AH4AVgAACF1crnNkF9QuBLoMHF9cyGIYuprovBX1cAsgABrovYJSBeBF4SPBFy8HmQbOLwdkFzAtBmYcNCIIuCYAIvXLgJ/BF5pecAAy9OdqgmCg8rmUznMyAAJkCEI5eEdqQgBU4YAJrsHEYheErwuSFpoxEEoa9WIwgwTgFeAwQvQLqQwFaYa7TLyYuBmQYBdKQuCIoUrlcyABcrKwJcWXgZJBEIQiBAAQlEEwI+BmU5CgINDF6ZgFGQI3BAAkzLw8OLygvBg8VhwaBLY5eEg8OisVjgABhwvVhwaCACgv/F78VF/4v/F8sHd9owBF68HFyhgYRyowDYQMVFqMHFy4xEAAMHg41BAAIrFiq6BAAIuZGhIAIFj4A/AH4A/AH4AWA=="))

View File

@ -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"
});

BIN
apps/kitchen/kitchen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

136
apps/kitchen/stepo.kit.js Normal file
View File

@ -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;
})();

170
apps/kitchen/waypoints.html Normal file
View File

@ -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>

View File

@ -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" }
]