1
0
Fork 0

Merge branch 'espruino:master' into master

master
Ronin0000 2021-09-04 08:47:39 -07:00 committed by GitHub
commit c2286a91d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 480 additions and 245 deletions

View File

@ -237,6 +237,11 @@ and which gives information about the app for the Launcher.
// like this one with 'storage','name' and 'id' set up
// see below for more info
"customConnect": true, // if supplied, ensure we are connected to a device
// before the "custom.html" iframe is loaded. An
// onInit function in "custom.html" is then called
// with info on the currently connected device.
"interface": "interface.html", // if supplied, apps/interface.html is loaded in an
// iframe, and it may interact with the connected Bangle
// to retrieve information from it

View File

@ -4,7 +4,7 @@
"tags": "tool,system,b2",
"type":"bootloader",
"icon": "bootloader.png",
"version":"0.28",
"version":"0.29",
"description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings",
"storage": [
{"name":".boot0","url":"boot0.js"},
@ -55,7 +55,7 @@
"name": "Launcher (Bangle.js 2)",
"shortName":"Launcher",
"icon": "app.png",
"version":"0.01",
"version":"0.02",
"description": "This is needed by Bangle.js 2.0 to display a menu allowing you to choose your own applications. It will not work on Bangle.js 1.0.",
"tags": "tool,system,launcher,b2,bno1",
"type":"launch",
@ -402,9 +402,9 @@
{ "id": "trex",
"name": "T-Rex",
"icon": "trex.png",
"version":"0.03",
"version":"0.04",
"description": "T-Rex game in the style of Chrome's offline game",
"tags": "game",
"tags": "game,b2",
"allow_emulator":true,
"storage": [
{"name":"trex.app.js","url":"trex.js"},
@ -418,9 +418,9 @@
{ "id": "astroid",
"name": "Asteroids!",
"icon": "asteroids.png",
"version":"0.02",
"version":"0.03",
"description": "Retro asteroids game",
"tags": "game",
"tags": "game,b2",
"allow_emulator":true,
"storage": [
{"name":"astroid.app.js","url":"asteroids.js"},
@ -814,8 +814,8 @@
"icon": "app.png",
"version":"0.02",
"description": "Use this to upload a customised QR code to Bangle.js",
"tags": "qrcode",
"custom": "custom.html",
"tags": "qrcode,b2",
"custom": "custom.html", "customConnect":true,
"storage": [
{"name":"qrcode.app.js"},
{"name":"qrcode.img","url":"app-icon.js","evaluate":true}
@ -1096,9 +1096,9 @@
{ "id": "flappy",
"name": "Flappy Bird",
"icon": "app.png",
"version":"0.04",
"version":"0.05",
"description": "A Flappy Bird game clone",
"tags": "game",
"tags": "game,b2",
"allow_emulator":true,
"storage": [
{"name":"flappy.app.js","url":"app.js"},
@ -1183,7 +1183,7 @@
{ "id": "widpedom",
"name": "Pedometer widget",
"icon": "widget.png",
"version":"0.14",
"version":"0.17",
"description": "Daily pedometer widget",
"tags": "widget,b2",
"type":"widget",
@ -1520,7 +1520,7 @@
"version":"0.08",
"description": "[BETA] Loads map tiles from OpenStreetMap onto your Bangle.js and displays a map of where you are",
"tags": "outdoors,gps,b2",
"custom": "custom.html",
"custom": "custom.html", "customConnect":true,
"storage": [
{"name":"openstmap","url":"openstmap.js"},
{"name":"openstmap.app.js","url":"app.js"},
@ -2039,7 +2039,7 @@
"id": "beebclock",
"name": "Beeb Clock",
"icon": "beebclock.png",
"version":"0.04",
"version":"0.05",
"description": "Clock face that may be coincidentally familiar to BBC viewers",
"tags": "clock",
"type": "clock",
@ -2735,7 +2735,7 @@
"name": "Heart Rate Variability monitor",
"shortName":"HRV monitor",
"icon": "hrv.png",
"version":"0.03",
"version":"0.04",
"description": "Heart Rate Variability monitor, see Readme for more info",
"tags": "",
"readme": "README.md",
@ -3208,7 +3208,7 @@
{ "id": "kitchen",
"name": "Kitchen Combo",
"icon": "kitchen.png",
"version":"0.12",
"version":"0.13",
"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",
"type":"clock",

View File

@ -1,3 +1,4 @@
0.01: New App!
0.02: Added options to either run as a one-off reading, or a continuous mode to log data until the watch is reset
0.03: Add RMSSD recording
0.04: Modify to work with new heart rate API, but still not sure it's working correctly

View File

@ -17,22 +17,23 @@ var csv = [
logfile.write(csv.join(",")+"\n");
var debugging = true;
var samples = 0; // how many samples have we connected?
var collectData = false; // are we currently collecting data?
var first_signals = 0; // ignore the first several signals
var heartrate = [];
var BPM_array = [];
var raw_HR_array = new Float32Array(1536);
var alternate_array = new Float32Array(3072);
var pulse_array = [];
var pulsecount = 0;
var cutoff_threshold = 0.5;
var sample_frequency = 51.6;
var gap_threshold = 0.15;
var hr_min = 40;
var hr_max = 160;
var movement = 0;
function storeMyData(data, file_type) {
var px = g.getWidth()/2;
var py = g.getHeight()/2;
var accel; // interval for acceleration logging
function storeMyData(data, file_type) { "ram"
log = raw_HR_array;
// shift elements backwards - note the 4, because a Float32 is 4 bytes
log.set(new Float32Array(log.buffer, 4 /*bytes*/));
@ -41,59 +42,58 @@ function storeMyData(data, file_type) {
}
function average(samples) {
var sum = 0;
return E.sum(samples) / samples.length; // faster builtin
/* var sum = 0;
for (var i = 0; i < samples.length; i++) {
sum += parseFloat(samples[i]);
}
var avg = sum / samples.length;
return avg;
return avg;*/
}
function StandardDeviation (array) {
const n = array.length;
const mean = array.reduce((a, b) => a + b) / n;
return Math.sqrt(array.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n);
const mean = E.sum(array) / n; //array.reduce((a, b) => a + b) / n;
//return Math.sqrt(array.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n);
return Math.sqrt(E.variance(array, mean));
}
function turn_off() {
Bangle.setHRMPower(0);
var accel = setInterval(function () {
movement = movement + Bangle.getAccel().diff;
}, 1000);
g.clear();
g.drawString("processing 1/5", 120, 120);
g.drawString("processing 1/5", px, py);
rolling_average(raw_HR_array,5);
g.clear();
g.drawString("processing 2/5", 120, 120);
g.drawString("processing 2/5", px, py);
upscale();
g.clear();
g.drawString("processing 3/5", 120, 120);
g.drawString("processing 3/5", px, py);
rolling_average(alternate_array,5);
g.clear();
g.drawString("processing 4/5", 120, 120);
g.drawString("processing 4/5", px, py);
apply_cutoff();
find_peaks();
g.clear();
g.drawString("processing 5/5", 120, 120);
g.drawString("processing 5/5", px, py);
calculate_HRV();
}
function bernstein(A, B, C, D, E, t) {
function bernstein(A, B, C, D, E, t) { "ram"
s = 1 - t;
x = (A * Math.pow(s, 4)) + (B * 4 * Math.pow(s, 3) * t) + (C * 6 * s * s * t * t)
+ (D * 4 * s * Math.pow(t, 3)) + (E * Math.pow(t, 4));
return x;
}
function upscale() {
function upscale() { "ram"
var index = 0;
for (let i = raw_HR_array.length - 1; i > 5; i -= 5) {
p0 = raw_HR_array[i];
@ -110,19 +110,18 @@ function upscale() {
}
}
function rolling_average(values, count) {
function rolling_average(values, count) { "ram"
var temp_array = [];
for (let i = 0; i < values.length; i++) {
temp_array = [];
for (let x = 0; x < count; x++)
temp_array.push(values[i + x]);
values[i] = average(temp_array);
}
}
function apply_cutoff() {
function apply_cutoff() { "ram"
var x;
for (let i = 0; i < alternate_array.length; i++) {
x = alternate_array[i];
@ -132,7 +131,7 @@ function apply_cutoff() {
}
}
function find_peaks() {
function find_peaks() { "ram"
var previous;
var previous_slope = 0;
var slope;
@ -157,17 +156,17 @@ function find_peaks() {
}
}
function RMSSD(samples){
function RMSSD(samples){ "ram"
var sum = 0;
var square = 0;
var data = [];
var value = 0;
for (let i = 0; i < samples.length-1; i++) {
value = Math.abs(samples[i]-samples[i+1])*((1 / (sample_frequency * 2)) * 1000);
data.push(value);
}
for (let i = 0; i < data.length; i++) {
square = data[i] * data[i];
Math.round(square);
@ -192,7 +191,7 @@ function calculate_HRV() {
gap_average = average(temp_array);
var calculatedHR = (sample_frequency*60)/(gap_average/2);
if(option == 0)
g.flip();
Bangle.setLCDPower(1);
g.clear();
//var display_stdv = StandardDeviation(pulse_array).toFixed(1);
var SDNN = (StandardDeviation(temp_array) * (1 / (sample_frequency * 2) * 1000)).toFixed(0);
@ -200,14 +199,13 @@ function calculate_HRV() {
g.drawString("SDNN:" + SDNN
+"\nRMSSD:" + RMS_SD
+ "\nHR:" + calculatedHR.toFixed(0)
+"\nSample Count:" + temp_array.length, 120, 120);
if(option == 0){
+"\nSample Count:" + temp_array.length, px, py);
Bangle.setLCDPower(1);
if(option == 0) { // single run
Bangle.buzz(500,1);
clearInterval(routine);
}
else{
option = null;
drawButtons();
} else {
var csv = [
0|getTime(),
temp_array.length,
@ -219,85 +217,87 @@ function calculate_HRV() {
];
logfile.write(csv.join(",")+"\n");
movement = 0;
// for (let i = 0; i < raw_HR_array.length; i++) {
// raw_HR_array[i] = null;
//}
//}
turn_on();
}
}
function btn1Pressed() {
if(option === null){
clearInterval(accel);
g.clear();
g.drawString("one-off assessment", 120, 120);
g.drawString("one-off assessment", px, py);
option = 0;
Bangle.setHRMPower(1);
turn_on();
}
}
function btn3Pressed() {
if(option === null){
logfile.write(""); //reset HRV log
clearInterval(accel);
g.clear();
g.drawString("continuous mode", 120, 120);
g.drawString("continuous mode", px, py);
option = 1;
Bangle.setHRMPower(1);
}
turn_on();
}
}
var routine = setInterval(function () {
clearInterval(accel);
first_signals = 0; // ignore the first several signals
pulsecount = 0;
function turn_on() {
BPM_array = [];
heartrate = [];
pulse_array = [];
samples = 0;
if (accel) clearInterval(accel);
movement = 0;
accel = setInterval(function () {
movement = movement + Bangle.getAccel().diff;
}, 1000);
Bangle.setHRMPower(1);
}, 180000);
collectData = true;
}
var accel = setInterval(function () {
movement = movement + Bangle.getAccel().diff;
}, 1000);
function drawButtons() {
g.setColor("#00ff7f");
g.setFont("6x8", 2);
g.setFontAlign(-1,1);
g.drawString("continuous", 120, 210);
g.drawString("one-time", 140, 50);
g.setColor("#ffffff");
g.setFontAlign(0, 0);
}
g.clear();
g.setColor("#00ff7f");
g.setFont("6x8", 2);
g.setFontAlign(-1,1);
g.drawString("continuous", 120, 210);
g.setFontAlign(-1,1);
g.drawString("one-time", 140, 50);
drawButtons();
g.setFont("6x8", 2);
g.setColor("#ffffff");
g.setFontAlign(0, 0); // center font
g.drawString("check app README", 120, 120);
g.drawString("for more info", 120, 140);
g.drawString("check app README\nfor more info", px, py);
setWatch(btn1Pressed, BTN1, {repeat:true});
setWatch(btn3Pressed, BTN3, {repeat:true});
Bangle.on('HRM', function (hrm) {
if(option == 0)
g.flip();
if (first_signals < 3) {
g.clear();
g.drawString("setting up...\nremain still " + first_signals * 20 + "%", 120, 120);
first_signals++;
}
else {
BPM_array = hrm.raw;
if(hrm.bpm > hr_min && hrm.bpm < hr_max)
heartrate.push(hrm.bpm);
if (pulsecount < 7) {
for (let i = 0; i < 256; i++) {
storeMyData(BPM_array[i], 0);
}
g.clear();
g.drawString("logging: " + ((pulsecount/6)*100).toFixed(0) + "%", 120, 120);
}
if(pulsecount == 6)
turn_off();
pulsecount++;
}
Bangle.on('HRM-raw', function (e) {
if (!collectData) return;
storeMyData(e.raw, 0);
if (!(samples & 7)) {
Bangle.setLCDPower(1);
g.clearRect(0, py-10, g.getWidth(), py+22);
if (samples < 100)
g.drawString("setting up...\nremain still " + samples + "%", px, py, true);
else
g.drawString("logging: " + (samples*100/raw_HR_array.length).toFixed(0) + "%", px, py, true);
}
if (samples > raw_HR_array.length) {
collectData = false;
turn_off();
}
samples++;
});

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
require("heatshrink").decompress(atob("oFAwhC/AH4A2xAedhGIwA+dAAI+dAAJAcEAJ/fD/4f5hAJIYSo6JIihaKxBATxAACC4YnBAAQ+TD7o/CFRJfTP5a/eb6pEOD/7JFwczAA0ybgwfUkUjD4QaDD6syiMSD4ZABCIa8JD547BCIY/ZHQQ+LD6Z/cHZYf/f6E4D6uCD4gACD6wgBkQACBAYfWABYf/D/4fPwf/ABgf/D6czABBf/P/5f/P/5f/D+WIABwfuAH4A/ADo="))
require("heatshrink").decompress(atob("mEwwgsphAXWxGACyoABDCwWVC/4XcXY8IDZsIxAXVXYQZDDwIGEC6gwMI5IvNGAJfVR5DXyPgczAAwSEC58iC4R3DC50xiMjC4QTBXY4XNGAIPCF6QTCI6gXCYYwXQcQ4XemUiC6IRBa4wXOABQX/C9OD/4AKC5WPC+Hzaw0zI/5H/C83zI/mIUo4ACnAXLABgXI"))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1 +1,2 @@
0.02: Add "ram" keyword to allow 2v06 Espruino builds to cache function that needs to be fast
0.03: Bangle 2 support

View File

@ -1,12 +1,24 @@
Bangle.setLCDMode("doublebuffered");
var BTNL, BTNR, BTNU, BTNA;
if (process.env.HWVERSION==2) {
var tap = {};
// use tapping on screen for left,right,accel
Bangle.on('drag',e=>tap=e);
BTNL = { read : _=>tap.b && tap.x < 58};
BTNR = { read : _=>tap.b && tap.x > 117};
BTNU = { read : _=>tap.b && tap.x > 58 && tap.x < 117};
// use button for fire
BTNA = BTN1;
} else {
// use hard buttons
BTNL = BTN4;
BTNR = BTN5;
BTNU = BTN1;
BTNA = BTN2;
Bangle.setLCDMode("doublebuffered");
}
var W = g.getWidth();
var H = g.getHeight();
g.setFontAlign(0,-1);
var BTNL = BTN4;
var BTNR = BTN5;
var BTNU = BTN1;
var BTNA = BTN2;
g.clear().setFontAlign(0,-1);
function newAst(x,y) {
var a = {
@ -92,8 +104,7 @@ function onFrame() {
}
g.clear();
g.drawString(score,120,0);
g.drawString(score,W-20,0);
var rs = Math.PI*0.8;
g.drawPoly([
ship.x+Math.cos(ship.r)*4, ship.y+Math.sin(ship.r)*4,

View File

@ -2,3 +2,5 @@
0.02: Fixes; widget support
0.03: Remove hardcoded hour buzz (you can install widchime if you miss it)
0.04: Update to use Bangle.setUI instead of setWatch
0.05: Avoid 'loadWidgets' at LCD on, which will cause memory leak
Avoid clearTimeout() usage, as it may break other widgets

View File

@ -5,6 +5,7 @@
const storage = require("Storage");
const filename = 'beebjson';
var timeout;
require('FontTeletext10x18Ascii').add(Graphics);
@ -153,15 +154,21 @@ let hours, minutes, seconds, date;
// Schedule event for calling at the start of the next second
const inOneSecond = (cb) => {
let now = new Date();
clearTimeout();
setTimeout(cb, 1000 - now.getMilliseconds());
if (timeout) clearTimeout(timeout);
timeout = setTimeout(function() {
timeout = undefined;
cb();
}, 1000 - now.getMilliseconds());
};
// Schedule event for calling at the start of the next minute
const inOneMinute = (cb) => {
let now = new Date();
clearTimeout();
setTimeout(cb, 60000 - (now.getSeconds() * 1000 + now.getMilliseconds()));
if (timeout) clearTimeout(timeout);
timeout = setTimeout(function() {
timeout = undefined;
cb();
}, 60000 - (now.getSeconds() * 1000 + now.getMilliseconds()));
};
// Draw a fat hour/minute hand
@ -356,6 +363,7 @@ const changeSeconds = () => {
};
Bangle.loadWidgets();
// widgets are drawn in drawAll()
// Restore mode
try {
@ -373,10 +381,9 @@ drawAll();
Bangle.on('lcdPower', (on) => {
if (on) {
Bangle.loadWidgets();
Bangle.drawWidgets();
drawAll();
} else {
clearTimeout();
if (timeout) clearTimeout(timeout);
timeout = undefined;
}
});

View File

@ -27,3 +27,5 @@
0.26: Remove buzz in setUI polyfill (#750)
0.27: Update polyfill for most recent changes
0.28: Fix double clock load after settings are changed
0.29: Update boot0 to avoid code block (faster execution)
Fix issues where 'Uncaught Error: Function not found' could happen with multiple .boot.js

View File

@ -6,7 +6,7 @@ var s = require('Storage').readJSON('setting.json',1)||{};
var isB2 = process.env.HWVERSION; // Is Bangle.js 2
var boot = "";
var CRC = E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\.boot\.js/));
boot += `if (E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\.boot\.js/))!=${CRC}) { eval(require('Storage').read('bootupdate.js'));} else {\n`;
boot += `if (E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\.boot\.js/))!=${CRC}) { eval(require('Storage').read('bootupdate.js')); throw "Storage Updated!"}\n`;
boot += `E.setFlags({pretokenise:1});\n`;
if (s.ble!==false) {
if (s.HID) { // Human interface device
@ -133,9 +133,11 @@ else if (mode=="updown") {
}
// Append *.boot.js files
require('Storage').list(/\.boot\.js/).forEach(bootFile=>{
boot += "//"+bootFile+"\n"+require('Storage').read(bootFile)+"\n";
// we add a semicolon so if the file is wrapped in (function(){ ... }()
// with no semicolon we don't end up with (function(){ ... }()(function(){ ... }()
// which would cause an error!
boot += require('Storage').read(bootFile)+";\n";
});
boot += "}\n";// initial 'if'
require('Storage').write('.boot0',boot);
delete boot;
E.showMessage("Reloading...");

View File

@ -1,3 +1,4 @@
0.02: Tweaks to make flappy bird run with less RAM available
0.03: A few tweaks to improve rendering speed
0.04: Add "ram" keyword to allow 2v06 Espruino builds to cache function that needs to be fast
0.05: Don't use Bangle.setLCDMode, just use offscreen buffer (allows widgets)

View File

@ -1,3 +1,21 @@
b = Graphics.createArrayBuffer(120,120,8);
var gimg = {
width:120,
height:104,
bpp:8,
buffer:b.buffer
};
if (process.env.HWVERSION==2) {
b.flip = function() {
g.drawImage(gimg,28,50);
};
} else {
b.flip = function() {
g.drawImage(gimg,0,24,{scale:2});
};
}
var BIRDIMG = E.toArrayBuffer(atob("EQyI/v7+/v7+/gAAAAAAAP7+/v7+/v7+/gYG0tLS0gDXAP7+/v7+/v4A0tLS0tIA19fXAP7+/v4AAAAA0tLS0gDX1wDXAP7+ANfX19cA0tLSANfXANcA/v4A19fX19cA0tLSANfX1wD+/gDS19fX0gDS0tLSAAAAAAD+/gDS0tIA0tLS0gDAwMDAwAD+/gAAAM3Nzc0AwAAAAAAA/v7+/v4Azc3Nzc0AwMDAwAD+/v7+/v4AAM3Nzc0AAAAAAP7+/v7+/v7+AAAAAP7+/v7+/g=="))
var FLOORIMG = require("heatshrink").decompress(atob("iEKxH+kklABuLAAlgAAwNFB34OLmAAO0YAO5wAOA"));
@ -33,26 +51,26 @@ function gameStop() {
function draw() {
"ram"
var H = g.getHeight()-24;
g.setColor("#71c6cf");
g.fillRect(0,0,g.getWidth(),H-1);
var H = b.getHeight()-24;
b.setColor("#71c6cf");
b.fillRect(0,0,b.getWidth(),H-1);
floorpos++;
for (var x=-(floorpos&15);x<g.getWidth();x+=16)
g.drawImage(FLOORIMG,x,H);
for (var x=-(floorpos&15);x<b.getWidth();x+=16)
b.drawImage(FLOORIMG,x,H);
if (!running) {
var x = g.getWidth()/2;
g.setColor("#000000");
g.setFontAlign(0,0);
g.setFont("4x6",2);
g.drawString("GAME OVER!",x,20);
g.setFont("6x8",1);
g.drawString("Score",x,40);
g.drawString(score,x,56);
g.drawString("Tap screen to",x,76);
g.drawString("restart and flap",x,84);
g.flip();
var x = b.getWidth()/2;
b.setColor("#000000");
b.setFontAlign(0,0);
b.setFont("4x6",2);
b.drawString("GAME OVER!",x,20);
b.setFont("6x8",1);
b.drawString("Score",x,40);
b.drawString(score,x,56);
b.drawString("Tap screen to",x,76);
b.drawString("restart and flap",x,84);
b.flip();
return;
}
@ -63,30 +81,30 @@ function draw() {
if (birdy > H)
gameStop();
// draw bird
g.drawImage(BIRDIMG, 6,birdy, {rotate:Math.atan2(birdvy,15)});
b.drawImage(BIRDIMG, 6,birdy, {rotate:Math.atan2(birdvy,15)});
// draw barriers
barriers.forEach(function(b) {
b.x1--;
b.x2--;
var btop = b.y-b.gap;
var bbot = b.y+b.gap;
g.setColor("#73bf2f"); // middle
g.fillRect(b.x1+4, 0, b.x2-4, btop-1);
g.fillRect(b.x1+4, bbot, b.x2-4, H-1);
g.setColor("#c0f181"); // left
g.fillRect(b.x1+1, 0, b.x1+3, btop-1);
g.fillRect(b.x1+1, bbot, b.x1+3, H-1);
g.setColor("#538917"); // right
g.fillRect(b.x2-3, 0, b.x2-1, btop-1);
g.fillRect(b.x2-3, bbot, b.x2-1, H-1);
g.setColor("#808080"); // outlines
g.drawRect(b.x1, btop-5, b.x2, btop); // top
g.drawLine(b.x1+1, 0, b.x1+1, btop-6);
g.drawLine(b.x2-2, 0, b.x2-2, btop-6);
g.drawRect(b.x1, bbot, b.x2, bbot+5); // bottom
g.drawLine(b.x1+1, bbot+6, b.x1+1, H-1);
g.drawLine(b.x2-1, bbot+6, b.x2-1, H-1);
if (b.x1<6 && (birdy-3<btop || birdy+3>bbot))
barriers.forEach(function(r) {
r.x1--;
r.x2--;
var btop = r.y-r.gap;
var bbot = r.y+r.gap;
b.setColor("#73bf2f"); // middle
b.fillRect(r.x1+4, 0, r.x2-4, btop-1);
b.fillRect(r.x1+4, bbot, r.x2-4, H-1);
b.setColor("#c0f181"); // left
b.fillRect(r.x1+1, 0, r.x1+3, btop-1);
b.fillRect(r.x1+1, bbot, r.x1+3, H-1);
b.setColor("#538917"); // right
b.fillRect(r.x2-3, 0, r.x2-1, btop-1);
b.fillRect(r.x2-3, bbot, r.x2-1, H-1);
b.setColor("#808080"); // outlines
b.drawRect(r.x1, btop-5, r.x2, btop); // top
b.drawLine(r.x1+1, 0, r.x1+1, btop-6);
b.drawLine(r.x2-2, 0, r.x2-2, btop-6);
b.drawRect(r.x1, bbot, r.x2, bbot+5); // bottom
b.drawLine(r.x1+1, bbot+6, r.x1+1, H-1);
b.drawLine(r.x2-1, bbot+6, r.x2-1, H-1);
if (r.x1<6 && (birdy-3<btop || birdy+3>bbot))
gameStop();
});
while (barriers.length && barriers[0].x2<=0) {
@ -94,7 +112,7 @@ function draw() {
newBarrier(g.getWidth());
}
g.flip();
b.flip();
}
Bangle.on('touch', function(button) {
@ -105,11 +123,9 @@ Bangle.on('touch', function(button) {
}
});
// Finally, start everything going
setTimeout(()=>{
Bangle.setLCDMode("120x120");
g.setBgColor("#e3db9d");
g.clear();
gameStart();
setInterval(draw, 100);
},10);
Bangle.loadWidgets();
g.clear();
Bangle.drawWidgets();
b.setBgColor("#e3db9d");
gameStart();
setInterval(draw, 100);

View File

@ -10,3 +10,4 @@
0.10: Converted Stepo to use direct screen writes, added a Trip Counter feature to stepo
0.11: Detect when waypoints.json is not present, error E-WPT
0.12: Added stepo2 as a replacement for stepo and digi
0.13: Added long press BTN2 toggle gpsrec status in GPS clock

View File

@ -85,14 +85,19 @@
function onButtonLong(btn) {
log_debug("markWaypoint()");
if (btn !== 1) return;
if (gpsObject.getState() !== gpsObject.GPS_RUNNING) return;
log_debug("markWaypoint()");
if (btn === 1) {
if (gpsObject.getState() !== gpsObject.GPS_RUNNING) return;
log_debug("markWaypoint()");
gpsObject.markWaypoint();
resetPrevious();
getWaypoint();
drawGPSData();
gpsObject.markWaypoint();
resetPrevious();
getWaypoint();
drawGPSData();
return;
}
if (btn === 2)
Bangle.showLauncher();
}
function getWaypoint() {

View File

@ -55,7 +55,10 @@
if (btn === 1) cycleInfoMode();
}
function onButtonLong(btn) {}
function onButtonLong(btn) {
if (btn === 2) Bangle.showLauncher();
}
function getGPSfix() { return undefined; }
function setGPSfix(f) {}

View File

@ -45,7 +45,15 @@
}
function onButtonLong(btn) {
if (btn === 1) toggleGPSPower();
switch(btn) {
case 1:
toggleGPSPower();
return;
case 2:
if (gpsObject.getState() === gpsObject.GPS_RUNNING)
gpsObject.toggleGpsLogging();
return;
}
}
function draw(){
@ -142,8 +150,9 @@
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);
g.setColor(1,1,1);
var age_and_logging = age + " logging " + gpsObject.loggingStatus();
g.drawString(age_and_logging, 120, Y_ACTIVITY + 46);
}
}

View File

@ -71,7 +71,8 @@ function buttonReleased(btn) {
face.onButtonLong(btn);
break;
case 2:
Bangle.showLauncher();
face.onButtonLong(btn);
//Bangle.showLauncher();
break;
case 3:
// do nothing
@ -94,8 +95,8 @@ function setButtons(){
}
Bangle.on('kill',()=>{
Bangle.setCompassPower(0);
Bangle.setGPSPower(0);
Bangle.setCompassPower(0,'kitchen');
Bangle.setGPSPower(0,'kitchen');
});
Bangle.on('lcdPower',function(on) {
@ -214,7 +215,7 @@ GPS.prototype.toggleGPSPower = function() {
this.log_debug("toggleGPSPower()");
this.gpsPowerState = Bangle.isGPSOn();
this.gpsPowerState = !this.gpsPowerState;
Bangle.setGPSPower(this.gpsPowerState ? 1 : 0);
Bangle.setGPSPower((this.gpsPowerState ? 1 : 0), 'kitchen');
this.resetLastFix();
this.determineGPSState();
@ -369,6 +370,26 @@ GPS.prototype.nextWaypoint = function(inc) {
return this.wp_current;
}
GPS.prototype.toggleGpsLogging = function() {
var settings = require("Storage").readJSON("gpsrec.json",1)||{};
if (settings == {}) return false;
settings.recording = !settings.recording;
require("Storage").write("gpsrec.json", settings);
if (WIDGETS["gpsrec"])
WIDGETS["gpsrec"].reload();
return true;
}
GPS.prototype.loggingStatus = function() {
var settings = require("Storage").readJSON("gpsrec.json",1)||{};
if (settings == {}) return "E-LOG";
if (settings.recording) return "ON";
return "OFF";
}
var gpsObj = new GPS();

View File

@ -19,11 +19,16 @@
}
function onButtonLong(btn) {
trip.resetTrip(getSteps());
trip.setTripState(true);
drawStepText();
if (btn === 1) {
trip.resetTrip(getSteps());
trip.setTripState(true);
drawStepText();
return;
}
if (btn === 2) Bangle.showLauncher();
}
function radians(a) {
return a*Math.PI/180;
}

View File

@ -62,12 +62,17 @@
}
function onButtonLong(btn) {
trip.resetTrip(getSteps());
infoMode = INFO_TRIP;
forceRedraw();
draw();
if (btn === 1) {
trip.resetTrip(getSteps());
infoMode = INFO_TRIP;
forceRedraw();
draw();
return;
}
if (btn === 2) Bangle.showLauncher();
}
function radians(a) {
return a*Math.PI/180;
}
@ -207,7 +212,8 @@
var midrot = -180 - (360 * percent);
var endrot = -360 - 180;
g.setColor(0x07FF); // light cyan
//g.setColor(0x07FF); // light cyan
g.setColor(0xFFC0); // yellow
// draw guauge
for (i = startrot; i > midrot; i -= 3) {
@ -218,8 +224,8 @@
// change the remaining color to RED if battery is below 25%
if (E.getBattery() > 25) {
//g.setColor(0x7BEF); // grey
g.setColor(0x000D); // dark navy
g.setColor(0x7BEF); // grey
//g.setColor(0x000D); // dark navy
} else {
g.setColor(0xF800); // red
}

View File

@ -31,7 +31,9 @@
}
}
function onButtonLong(btn) {}
function onButtonLong(btn) {
if (btn === 2) Bangle.showLauncher();
}
return {init:init, freeResources:freeResources, startTimer:startTimer, stopTimer:stopTimer,
onButtonShort:onButtonShort, onButtonLong:onButtonLong};

View File

@ -1 +1,2 @@
0.01: New App!
0.02: Fix occasional missed image when scrolling up

View File

@ -47,8 +47,13 @@ Bangle.on('drag',e=>{
g.reset().setClipRect(0,24,g.getWidth()-1,g.getHeight()-1);
g.scroll(0,dy);
menuScroll -= dy;
if (e.dy < 0) drawApp(Math.floor((menuScroll+24)/APPH)+n-1);
else drawApp(Math.floor((menuScroll+24)/APPH));
if (e.dy < 0) {
drawApp(Math.floor((menuScroll+24+g.getHeight())/APPH)-1);
if (e.dy <= -APPH) drawApp(Math.floor((menuScroll+24+g.getHeight())/APPH)-2);
} else {
drawApp(Math.floor((menuScroll+24)/APPH));
if (e.dy >= APPH) drawApp(Math.floor((menuScroll+24)/APPH)+1);
}
g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1);
});
Bangle.on("touch",(_,e)=>{

View File

@ -32,7 +32,7 @@
<div id="map">
</div>
<div id="controls">
<div style="display:inline-block;text-align:center;vertical-align: top;"> <input type="checkbox" id="3bit"></input><br/><span>3 bit</span></div>
<div style="display:inline-block;text-align:center;vertical-align: top;" id="3bitdiv"> <input type="checkbox" id="3bit"></input><br/><span>3 bit</span></div>
<button id="getmap" class="btn btn-primary">Get Map</button><br/>
<canvas id="maptiles" style="display:none"></canvas>
<div id="uploadbuttons" style="display:none"><button id="upload" class="btn btn-primary">Upload</button>
@ -72,6 +72,16 @@ TODO:
});
// Could optionally overlay trails: https://wiki.openstreetmap.org/wiki/Tiles
function onInit(device) {
if (device && device.info && device.info.g) {
// On 3 bit devices, don't even offer the option. 3 bit is the only way
if (device.info.g.bpp==3) {
document.getElementById("3bit").checked = true;
document.getElementById("3bitdiv").style = "display:none";
}
}
}
var mapFiles = [];
tileLayer.addTo(map);

View File

@ -34,6 +34,23 @@
<script src="../../core/lib/imageconverter.js"></script>
<script>
var targetWidth = 200;
var targetHeight = 200;
function onInit(device) {
if (device && device.info && device.info.g) {
targetWidth = device.info.g.width - 20;
targetHeight = device.info.g.height - 20;
}
qrcode = new QRCode("qrcode", {
text: document.getElementById("url").value,
width: targetWidth,
height: targetHeight,
colorDark : "#000000",
colorLight : "#ffffff",
});
}
//https://github.com/evgeni/qifi/blob/gh-pages/index.html#L168
function escapeString (string) {
var to_escape = ['\\', ';', ',', ':', '"'];
@ -70,13 +87,7 @@
qrcode.makeCode(document.getElementById("url").value);
}
}
var qrcode = new QRCode("qrcode", {
text: document.getElementById("url").value,
width: 200,
height: 200,
colorDark : "#000000",
colorLight : "#ffffff",
});
var qrcode;
document.getElementById("url").addEventListener("change", refreshQRCode);
document.getElementById("ssid").addEventListener("change",refreshQRCode);
@ -93,13 +104,11 @@
var img = imageconverter.canvastoString(document.getElementsByTagName("canvas")[0],{mode:"1bit",output:"string",compression:true});
var app = `var img = ${img};
var content = ${JSON.stringify(content)};
g.setColor(1,1,1);
g.fillRect(0,0,239,239);
g.drawImage(img,20,20);
g.setFontAlign(0,0);
g.setFont("6x8");
g.setColor(0,0,0);
g.drawString(content,120,230);
g.clear(1).setColor(1,1,1).setBgColor(0,0,0);
g.fillRect(0,0,g.getWidth()-1,g.getHeight()-1);
g.drawImage(img,(g.getWidth()-img[0])/2,(g.getHeight()-img[1])/2);
g.setFontAlign(0,0).setFont("6x8").setColor(0,0,0);
g.drawString(content,g.getWidth()/2,g.getHeight()-(g.getHeight()-img[1])/4));
g.setColor(1,1,1);
`;
sendCustomizedApp({

View File

@ -1,2 +1,3 @@
0.02: Add "ram" keyword to allow 2v06 Espruino builds to cache function that needs to be fast
0.03: Enabled BTN2 and BTN3, added highscore (score is saved to storage and can be reset in app settings menu)
0.04: Bangle.js 2 support

View File

@ -8,20 +8,42 @@ function saveHighScore(score) {
f.write(score + "\n");
}
greal = g;
g.clear();
g = Graphics.createArrayBuffer(120,64,1,{msb:true});
g.flip = function() {
greal.drawImage({
width:120,
height:64,
buffer:g.buffer
},0,(240-128)/2,{scale:2});
};
var BTNL, BTNR, BTNU;
if (process.env.HWVERSION==2) {
var tap = {};
// use tapping on screen for left and right
Bangle.on('drag',e=>tap=e);
BTNL = { read : _=>tap.b && tap.x < 88};
BTNR = { read : _=>tap.b && tap.x > 88};
// use button for jump
BTNU = BTN1;
greal = g;
g = Graphics.createArrayBuffer(88,64,1,{msb:true});
g.flip = function() {
greal.drawImage({
width:88,
height:64,
buffer:g.buffer
},0,(176-128)/2,{scale:2});
};
} else {
// use hard buttons
BTNL = BTN2;
BTNR = BTN3;
BTNU = BTN1;
greal = g;
g = Graphics.createArrayBuffer(120,64,1,{msb:true});
g.flip = function() {
greal.drawImage({
width:120,
height:64,
buffer:g.buffer
},0,(240-128)/2,{scale:2});
};
}
var W = g.getWidth();
var BTNL = BTN2;
var BTNR = BTN3;
var BTNU = BTN1;
// Images can be added like this in Espruino v2.00
var IMG = {

View File

@ -11,3 +11,6 @@
0.12: Respect Quiet Mode
0.13: Now use system color theme
0.14: Improve memory usage
0.15: Settings option to hide the widget icon
0.16: Settings option to show large digits in widget area
0.17: Cope with 2v10+ firmware sometimes reporting >1 step

View File

@ -5,6 +5,8 @@
let s = {
'goal': 10000,
'progress': false,
'large': false,
'hide': false
}
// ...and overwrite them with any saved values
// This way saved values are preserved if a new version adds more settings
@ -41,6 +43,22 @@
save()
},
},
'Large Digits': {
value: s.large,
format: () => (s.large ? 'Yes' : 'No'),
onchange: () => {
s.large = !s.large
save()
},
},
'Hide Widget': {
value: s.hide,
format: () => (s.hide ? 'Yes' : 'No'),
onchange: () => {
s.hide = !s.hide
save()
},
},
'< Back': back,
})
})

View File

@ -1,6 +1,9 @@
(() => {
const PEDOMFILE = "wpedom.json"
// Last time Bangle.on('step' was called
let lastUpdate = new Date();
// Last step count when Bangle.on('step' was called
var lastStepCount;
let stp_today = 0;
let settings;
@ -14,11 +17,14 @@
const DEFAULTS = {
'goal': 10000,
'progress': false,
'large': false,
'hide': false
}
return (key in settings) ? settings[key] : DEFAULTS[key];
}
function drawProgress(stps) {
if (setting('hide')) return;
const width = 24, half = width/2;
const goal = setting('goal'), left = Math.max(goal-stps,0);
const c = left ? "#00f" : "#090"; // blue or dark green
@ -46,13 +52,29 @@
}
}
// show the step count in the widget area in a readable sized font
function draw_large(st) {
var width = 12 * st.length;
g.reset();
g.clearRect(this.x, this.y, this.x + width, this.y + 16); // erase background
g.setColor(g.theme.fg);
g.setFont("6x8",2);
g.setFontAlign(-1, -1);
g.drawString(st, this.x + 4, this.y + 2);
}
// draw your widget
function draw() {
if (setting('hide')) return;
var width = 24;
if (stp_today > 99999){
stp_today = stp_today % 100000; // cap to five digits + comma = 6 characters
}
let stps = stp_today.toString();
if (setting('large')) {
draw_large.call(this, stps);
return;
}
g.reset().clearRect(this.x, this.y, this.x + width, this.y + 23); // erase background
if (setting('progress')){ drawProgress.call(this, stps); }
g.setColor(g.theme.fg);
@ -73,13 +95,16 @@
draw()
}
Bangle.on('step', (up) => {
Bangle.on('step', stepCount => {
var steps = stepCount-lastStepCount;
if (lastStepCount===undefined || steps<0) steps=1;
lastStepCount = stepCount;
let date = new Date();
if (lastUpdate.getDate() == date.getDate()){
stp_today ++;
stp_today += steps;
} else {
// TODO: could save this to PEDOMFILE for lastUpdate's day?
stp_today = 1;
stp_today = steps;
}
if (stp_today === setting('goal')
&& !(require('Storage').readJSON('setting.json',1)||{}).quiet) {

View File

@ -51,7 +51,7 @@ try{
const APP_KEYS = [
'id', 'name', 'shortName', 'version', 'icon', 'description', 'tags', 'type',
'sortorder', 'readme', 'custom', 'interface', 'storage', 'data', 'allow_emulator',
'sortorder', 'readme', 'custom', 'customConnect', 'interface', 'storage', 'data', 'allow_emulator',
'dependencies'
];
const STORAGE_KEYS = ['name', 'url', 'content', 'evaluate', 'noOverwite'];
@ -100,6 +100,7 @@ apps.forEach((app,appIdx) => {
if (!fs.existsSync(appDir+app.icon)) ERROR(`App ${app.id} icon doesn't exist`);
if (app.readme && !fs.existsSync(appDir+app.readme)) ERROR(`App ${app.id} README file doesn't exist`);
if (app.custom && !fs.existsSync(appDir+app.custom)) ERROR(`App ${app.id} custom HTML doesn't exist`);
if (app.customConnect && !app.custom) ERROR(`App ${app.id} has customConnect but no customn HTML`);
if (app.interface && !fs.existsSync(appDir+app.interface)) ERROR(`App ${app.id} interface HTML doesn't exist`);
if (app.dependencies) {
if (("object"==typeof app.dependencies) && !Array.isArray(app.dependencies)) {

2
core

@ -1 +1 @@
Subproject commit 27f9a7125146a38c4357d679ec783f6e98a983c6
Subproject commit 39f89acf1468dc7d58ac54d15a092b2bd6d73cce

View File

@ -3,14 +3,32 @@
Usage:
```
var Layout = require("Layout");
var layout = new Layout( layoutObject, btns )
layout.render(optionalObject);
```
For example:
```
var Layout = require("Layout");
var layout = new Layout( {
type:"v", c: [
{type:"txt", font:"20%", label:"12:00" },
{type:"txt", font:"6x8", label:"The Date" }
]
});
g.clear();
layout.render();
```
layoutObject has:
* A `type` field of:
* `undefined` - blank, can be used for padding
* `"txt"` - a text label, with value `label` and `r` for text rotation
* `"txt"` - a text label, with value `label` and `r` for text rotation. 'font' is required
* `"btn"` - a button, with value `label` and callback `cb`
* `"img"` - an image where the function `src` is called to return an image to draw
* `"custom"` - a custom block where `render(layoutObj)` is called to render
@ -83,10 +101,12 @@ function Layout(layout, buttons) {
this._l,
{type:"v", c: buttons.map(b=>(b.type="btn",b.h=btnHeight,b.w=32,b.r=1,b))}
]};
Bangle.touchHandler = (_,e) => touchHandler(this._l,e);
Bangle.on('touch',Bangle.touchHandler);
}
}
if (process.env.HWVERSION==2) {
Bangle.touchHandler = (_,e) => touchHandler(layout,e);
Bangle.on('touch',Bangle.touchHandler);
}
// add IDs
var ll = this;
@ -130,7 +150,7 @@ function updateMin(l) {
case "txt": {
if (l.font.endsWith("%"))
l.font = "Vector"+Math.round(g.getHeight()*l.font.slice(0,-1)/100);
// Not needed in new firmwares - 'font' is enough
// FIXME ':'/fsz not needed in new firmwares - it's handled internally
if (l.font.includes(":")) {
var f = l.font.split(":");
l.font = f[0];
@ -222,8 +242,8 @@ Layout.prototype.render = function (l) {
Layout.prototype.layout = function (l) {
// l = current layout element
// exw,exh = extra width/height available
var fillx = l.c.reduce((a,l)=>a+(0|l.fillx),0);
var filly = l.c.reduce((a,l)=>a+(0|l.filly),0);
var fillx = l.c && l.c.reduce((a,l)=>a+(0|l.fillx),0);
var filly = l.c && l.c.reduce((a,l)=>a+(0|l.filly),0);
switch (l.type) {
case "h": {
let x = l.x + (l.w-l._w)/2;

32
modules/Layout.min.js vendored
View File

@ -3,14 +3,32 @@
Usage:
```
var Layout = require("Layout");
var layout = new Layout( layoutObject, btns )
layout.render(optionalObject);
```
For example:
```
var Layout = require("Layout");
var layout = new Layout( {
type:"v", c: [
{type:"txt", font:"20%", label:"12:00" },
{type:"txt", font:"6x8", label:"The Date" }
]
});
g.clear();
layout.render();
```
layoutObject has:
* A `type` field of:
* `undefined` - blank, can be used for padding
* `"txt"` - a text label, with value `label` and `r` for text rotation
* `"txt"` - a text label, with value `label` and `r` for text rotation. 'font' is required
* `"btn"` - a button, with value `label` and callback `cb`
* `"img"` - an image where the function `src` is called to return an image to draw
* `"custom"` - a custom block where `render(layoutObj)` is called to render
@ -83,10 +101,12 @@ function Layout(layout, buttons) {
this._l,
{type:"v", c: buttons.map(b=>(b.type="btn",b.h=btnHeight,b.w=32,b.r=1,b))}
]};
Bangle.touchHandler = (_,e) => touchHandler(this._l,e);
Bangle.on('touch',Bangle.touchHandler);
}
}
if (process.env.HWVERSION==2) {
Bangle.touchHandler = (_,e) => touchHandler(layout,e);
Bangle.on('touch',Bangle.touchHandler);
}
// add IDs
var ll = this;
@ -130,7 +150,7 @@ function updateMin(l) {
case "txt": {
if (l.font.endsWith("%"))
l.font = "Vector"+Math.round(g.getHeight()*l.font.slice(0,-1)/100);
// Not needed in new firmwares - 'font' is enough
// FIXME ':'/fsz not needed in new firmwares - it's handled internally
if (l.font.includes(":")) {
var f = l.font.split(":");
l.font = f[0];
@ -222,8 +242,8 @@ Layout.prototype.render = function (l) {
Layout.prototype.layout = function (l) {
// l = current layout element
// exw,exh = extra width/height available
var fillx = l.c.reduce((a,l)=>a+(0|l.fillx),0);
var filly = l.c.reduce((a,l)=>a+(0|l.filly),0);
var fillx = l.c && l.c.reduce((a,l)=>a+(0|l.fillx),0);
var filly = l.c && l.c.reduce((a,l)=>a+(0|l.filly),0);
switch (l.type) {
case "h": {
let x = l.x + (l.w-l._w)/2;