diff --git a/apps/openwind/ChangeLog b/apps/openwind/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/openwind/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/openwind/README.md b/apps/openwind/README.md new file mode 100644 index 000000000..dc139bc9a --- /dev/null +++ b/apps/openwind/README.md @@ -0,0 +1,25 @@ +# App Name + +Describe the app... + +Add screen shots (if possible) to the app folder and link then into this file with ![](.png) + +## Usage + +Describe how to use it + +## Features + +Name the function + +## Controls + +Name the buttons and what they are used for + +## Requests + +Name who should be contacted for support/update requests + +## Creator + +Your name diff --git a/apps/openwind/app-icon.js b/apps/openwind/app-icon.js new file mode 100644 index 000000000..49232b838 --- /dev/null +++ b/apps/openwind/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwJC/AH4A/AH4AgA==")) diff --git a/apps/openwind/app.js b/apps/openwind/app.js new file mode 100644 index 000000000..c1c3408d0 --- /dev/null +++ b/apps/openwind/app.js @@ -0,0 +1,108 @@ +OW_CHAR_UUID = '0000cc91-0000-1000-8000-00805f9b34fb'; +require("Font7x11Numeric7Seg").add(Graphics); +gatt = {}; +cx = g.getWidth()/2; +cy = 24+(g.getHeight()-24)/2; +w = (g.getWidth()-24)/2; + +gps_course = {}; + +var settings = require("Storage").readJSON('openwind.json', 1) || {}; + +i = 0; +hullpoly = []; +for (y=-1; y<=1; y+=0.1) { + hullpoly[i++] = cx - (y<0 ? 1+y*0.15 : (Math.sqrt(1-0.7*y*y)-Math.sqrt(0.3))/(1-Math.sqrt(0.3)))*w*0.3; + hullpoly[i++] = cy - y*w*0.7; +} +for (y=1; y>=-1; y-=0.1) { + hullpoly[i++] = cx + (y<0 ? 1+y*0.15 : (Math.sqrt(1-0.7*y*y)-Math.sqrt(0.3))/(1-Math.sqrt(0.3)))*w*0.3; + hullpoly[i++] = cy - y*w*0.7; +} + +function wind_updated(ev) { + if (ev.target.uuid == "0xcc91") { + awa = 180-ev.target.value.getInt16(1, true)*0.1; + aws = ev.target.value.getInt16(3, true)*0.01; +// console.log(awa, aws); + if (gps_course.spd > 0) { + wv = { // wind vector + lon: Math.sin(Math.PI*awa/180)*aws, + lat: Math.cos(Math.PI*awa/180)*aws + }; + twv = { lon: wv.lon+gps_course.lon, lat: wv.lat+gps_course.lat }; + tws = Math.sqrt(Math.pow(twv.lon,2)+Math.pow(twv.lat, 2)); + twa = Math.atan2(twv.lat, twv.lon)*180/Math.PI; + if (twa<0) twa += 360; + } + else { + tws = -1; + twa = 0; + } + draw_compass(awa,aws,twa,tws); + } +} + +function draw_compass(awa, aws, twa, tws) { + g.clearRect(0, 24, g.getWidth()-1, g.getHeight()-1); + fh = w*0.15; + g.setColor(0, 0, 1).fillPoly(hullpoly); + g.setFontVector(fh).setColor(1, 1, 1); + g.setFontAlign(0, 0, 0).drawString("0", cx, 24+fh/2); + g.setFontAlign(0, 0, 1).drawString("90", g.getWidth()-12-fh, cy); + g.setFontAlign(0, 0, 2).drawString("180", cx, g.getHeight()-fh/2); + g.setFontAlign(0, 0, 3).drawString("270", 12+fh/2, cy); + for (i=0; i<4; ++i) { + a = i*Math.PI/2+Math.PI/4; + g.drawLineAA(cx+Math.cos(a)*w*0.85, cy+Math.sin(a)*w*0.85, cx+Math.cos(a)*w*0.99, cy+Math.sin(a)*w*0.99); + } + g.setColor(0, 1, 0).fillCircle(cx+Math.sin(Math.PI*awa/180)*w*0.9, cy+Math.cos(Math.PI*awa/180)*w*0.9, w*0.1); + if (tws>0) g.setColor(1, 0, 0).fillCircle(cx+Math.sin(Math.PI*twa/180)*w*0.9, cy+Math.cos(Math.PI*twa/180)*w*0.9, w*0.1); + g.setColor(0, 1, 0).setFont("7x11Numeric7Seg",w*0.06); + g.setFontAlign(0, 0, 0).drawString(aws.toFixed(1), cx, cy-0.32*w); + if (tws>0) g.setColor(1, 0, 0).drawString(tws.toFixed(1), cx, cy+0.32*w); +} + +function parseDevice(d) { + device = d; + console.log("Found device"); + device.gatt.connect().then(function(ga) { + console.log("Connected"); + gatt = ga; + return ga.getPrimaryService("cc90"); +}).then(function(s) { + return s.getCharacteristic("cc91"); +}).then(function(c) { + c.on('characteristicvaluechanged', (event)=>wind_updated(event)); + return c.startNotifications(); +}).then(function() { + console.log("Done!"); +}).catch(function(e) { + console.log("ERROR"+e); +});} + +function connection_setup() { + NRF.setScan(); + NRF.setScan(parseDevice, { filters: [{services:["cc90"]}], timeout: 2000}); + console.log("Scanning for OW sensor"); +} + +if (settings.truewind) { + Bangle.on('GPS',function(fix) { + if (fix.fix && fix.satellites>3 && fix.speed>2) { // only uses fixes w/ more than 3 sats and speed > 2kph + gps_course = + { lon: Math.sin(Math.PI*fix.course/180)*fix.speed/1.852, + lat: Math.cos(Math.PI*fix.course/180)*fix.speed/1.852, + spd: fix.speed, + crs: fix.course + }; + } + else gps_course.spd = -1; + }); + Bangle.setGPSPower(1, "app"); +} + +Bangle.loadWidgets(); +Bangle.drawWidgets(); +draw_compass(0, 0, 0, 0); +connection_setup(); diff --git a/apps/openwind/app.png b/apps/openwind/app.png new file mode 100644 index 000000000..9fd64efba Binary files /dev/null and b/apps/openwind/app.png differ diff --git a/apps/openwind/icons8-wind-speed-48-52-48.png b/apps/openwind/icons8-wind-speed-48-52-48.png new file mode 100644 index 000000000..9fd64efba Binary files /dev/null and b/apps/openwind/icons8-wind-speed-48-52-48.png differ diff --git a/apps/openwind/metadata.json b/apps/openwind/metadata.json new file mode 100644 index 000000000..ec59998a0 --- /dev/null +++ b/apps/openwind/metadata.json @@ -0,0 +1,14 @@ +{ "id": "openwind", + "name": "OpenWind display", + "shortName":"Display OpenWind data", + "version":"0.01", + "description": "OpenWind wind speed and direction sensor display", + "icon": "app.png", + "tags": "", + "supports" : ["BANGLEJS2"], + "storage": [ + {"name":openwind.app.js","url":"app.js"}, + {"name":"openwind.img","url":"app-icon.js","evaluate":true}, + {"name":"openseachart.settings.js", "url":"settings.js"} + ] +} diff --git a/apps/openwind/settings.js b/apps/openwind/settings.js new file mode 100644 index 000000000..5ab847a92 --- /dev/null +++ b/apps/openwind/settings.js @@ -0,0 +1,35 @@ +// This file should contain exactly one function, which shows the app's settings +/** + * @param {function} back Use back() to return to settings menu + */ +(function(back) { + const SETTINGS_FILE = 'openwindsettings.json' + // initialize with default settings... + let s = { + 'truewind': false, + } + // ...and overwrite them with any saved values + // This way saved values are preserved if a new version adds more settings + const storage = require('Storage') + const saved = storage.readJSON(SETTINGS_FILE, 1) || {} + for (const key in saved) { + s[key] = saved[key]; + } + // creates a function to safe a specific setting, e.g. save('color')(1) + function save(key) { + return function (value) { + s[key] = value; + storage.write(SETTINGS_FILE, s); + } + } + const menu = { + '': { 'title': 'OpenWind' }, + '< Back': back, + 'Draw course': { + value: s.truewind, + format: boolFormat, + onchange: save('truewind'), + }, + } + E.showMenu(menu); +})