-
Hi there @Gordon, I was wondering what the release plan / schedule is for the 2.9 firmware and the Oxford Pedometer. Steps are basically broken, even after tweaking the ActivePedom settings its not very accurate. I use a sensivity level of 90.
-
I have written the following function to check for firmware greater than X.Y.Z but its a bit messy.
Wondering if there is a better way.function checkFirmware(maj,min,bld) { var major = process.env.VERSION.split(".")[0].split("v")[0]; var minor = process.env.VERSION.split(".")[0].split("v")[1]; var build = process.env.VERSION.split(".")[1]; if (major > maj) return true; if (major == 2 && minor > min) return true; if (major == 2 && minor == min && build >= bld) return true; return false; }
And then in the App...
if (!checkFirmware(2,8,187)) { g.setColor(1,1,1); g.drawString("E-FW", 120, Y_ACTIVITY); return; }
Also was thinking that the format of the firware version string is a bit odd.
Why not use major.minor.build instead of major. "v0"minor.build ? -
Apps have some dependencies but it is not very obvious (ex: stepometer clock)
Hi. I wrote the Stepometer clock. I have other Apps with dependancies on a later version of the firmware and I usually state this upfront in the one line description in the App loader and also in the README file. Stepometer has no dependancies to my knowledge, what did you observe ?
Ah - I think you mean you have to install one of the pedominter widgets. It will work with either. I will update the README when I get a chance.
Remember we are all hobbyists with day jobs burning the midnight oil to write these apps :)
-
-
-
I mistakenly logged 2 walks in the same GPS log file. So I downloaded the file and split them. However when I upload the file I end up with quotes round the filename. How could I upload these files ? Also I am wondering what is the purpose / benefit of having these files have (Storage File) appended on the end of the filename ?
-
During development I tend to repeatedly upload the same file again and again. So in the IDE I click on the disk icon, select upload file, browse to the source directory and select the file. I also found it saves time if you just call the source file the same as it will be when it is deployed through the App Loader. So instead of something generic like app.js, which will have to be renamed before upload, use myappid.app.js. Its is useful not to have the code mimified during development as you can recover a file you accidently delete in your sourec tree by downloading it from the bangle.
-
-
-
Here's the latest code for arrow. I can reproduce the problem with this code. There is no use of g.flip(). flip1() and flip2() just draw buf1 and buf2 which are different sizes. I have also taken out the setLCDTimeout(30);
The LCD will only go of if the watch is totally still. I have disabled switch on on twist in the settings.
For simplicity I have taken out the calibrate functions in the code below.
var pal1color = new Uint16Array([0x0000,0xFFC0],0,1); var pal2color = new Uint16Array([0x0000,0xffff],0,1); var buf1 = Graphics.createArrayBuffer(128,128,1,{msb:true}); var buf2 = Graphics.createArrayBuffer(80,40,1,{msb:true}); var intervalRef; var bearing=0; // always point north var heading = 0; var oldHeading = 0; var candraw = false; var CALIBDATA = require("Storage").readJSON("magnav.json",1)||null; function flip1(x,y) { g.drawImage({width:128,height:128,bpp:1,buffer:buf1.buffer, palette:pal1color},x,y); buf1.clear(); } function flip2(x,y) { g.drawImage({width:80,height:40,bpp:1,buffer:buf2.buffer, palette:pal2color},x,y); buf2.clear(); } function radians(d) { return (d*Math.PI) / 180; } // takes 32ms function drawCompass(hd) { if(!candraw) return; if (Math.abs(hd - oldHeading) < 2) return 0; var t1 = getTime(); hd=hd*Math.PI/180; var p = [0, 1.1071, Math.PI/4, 2.8198, 3.4633, 7*Math.PI/4 , 5.1760]; // using polar cordinates, 64,64 is the offset from the 0,0 origin 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); var t = Math.round((getTime() - t1)*1000); LED1.write((t > 100)); } // stops violent compass swings and wobbles, takes 3ms function newHeading(m,h){ 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; 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){ var start = Date.now(); 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 reading() { var d = tiltfixread(CALIBDATA.offset,CALIBDATA.scale); heading = newHeading(d,heading); var dir = bearing - heading; if (dir < 0) dir += 360; if (dir > 360) dir -= 360; drawCompass(dir); // we want compass to show us where to go oldHeading = dir; buf2.setColor(1); buf2.setFontAlign(-1,-1); buf2.setFont("Vector",38); var course = Math.round(heading); var cs = course.toString(); cs = course<10?"00"+cs : course<100 ?"0"+cs : cs; buf2.drawString(cs,0,0); flip2(90, 200); } function startdraw(){ g.clear(); g.setColor(1,1,1); Bangle.drawWidgets(); candraw = true; intervalRef = setInterval(reading,500); } function stopdraw() { candraw=false; if(intervalRef) {clearInterval(intervalRef);} } function setButtons(){ setWatch(()=>{load();}, BTN1, {repeat:false,edge:"falling"}); setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"}); } Bangle.on('lcdPower',function(on) { if (on) { startdraw(); } else { stopdraw(); } }); Bangle.on('kill',()=>{Bangle.setCompassPower(0);}); Bangle.loadWidgets(); Bangle.setCompassPower(1); startdraw(); setButtons();
-
-
I do have wake on twist enabled, though I have set it to be fairly insensitive. I only used magnav as an example as I have similar code that runs like
set a 200ms timer
check the compass
adjust the compass based on tilt
draw the compassI have noticed that sometimes with the watch 100% stationary the screen does not always timeout and I dont see the Bangle.on('lcdPower', .......); being called.
-
Here is the result of my investigations.
1) the drawCompass() function was taking 98,99ms. But sometimes I was seeing this go to 130ms.
Then when the buttons become unresponsive I was seeing it take 1300ms. It is as if 100ms is just too much of a chunk of processing and stuff that the firmware does starts to back up.This is really interesting. Here are the different timings for different methods to draw the compass arrow:
1) Original method tha rotated an image for the arrow - 98ms.
// takes ~95-100ms, have seen it take 1200ms though which will cause tasks to back up function drawCompass(hd) { var t1 = getTime(); buf1.setColor(1); buf1.fillCircle(80,80,79,79); buf1.setColor(0); buf1.fillCircle(80,80,69,69); buf1.setColor(1); buf1.drawImage(img, 80, 80, {scale:3, rotate:radians(hd)} ); flip1(40, 30); var t = Math.round((getTime() - t1)*1000); LED1.write((t > 130)); }
2) TEST_7: An experiment using a modified version of the arrow() function in the route App.
Takes 9ms BUT you have to call it twice once to draw the arrow and the next time to remove the old one. So 18ms total.function arrow(angle,col) { var t1 = getTime(); angle=angle*Math.PI/180; var p = [0, 1.1071, Math.PI/4, 2.8198, 3.4633, 7*Math.PI/4 , 5.1760]; g.setColor(col); var poly = [ 120+60*Math.sin(angle+p[0]), 120-60*Math.cos(angle+p[0]), 120+44.7214*Math.sin(angle+p[1]), 120-44.7214*Math.cos(angle+p[1]), 120+28.2843*Math.sin(angle+p[2]), 120-28.2843*Math.cos(angle+p[2]), 120+63.2455*Math.sin(angle+p[3]), 120-63.2455*Math.cos(angle+p[3]), 120+63.2455*Math.sin(angle+p[4]), 120-63.2455*Math.cos(angle+p[4]), 120+28.2843*Math.sin(angle+p[5]), 120-28.2843*Math.cos(angle+p[5]), 120+44.7214*Math.sin(angle+p[6]), 120-44.7214*Math.cos(angle+p[6]) ]; //console.log(poly); g.fillPoly(poly); console.log("arrow: " + Math.round((getTime() - t1)*1000) ); }
TEST_8: The arrow shape I require: Takes twice as long, called twice tatal = 32ms.
function arrow(angle,col) { var t1 = getTime(); angle=angle*Math.PI/180; var p = [0, 1.1071, Math.PI/4, 2.8198, 3.4633, 7*Math.PI/4 , 5.1760]; g.setColor(col); poly = [ 120+60*Math.sin(angle+p[0]), 120-60*Math.cos(angle+p[0]), 120+44.7214*Math.sin(angle+p[1]), 120-44.7214*Math.cos(angle+p[1]), 120+28.2843*Math.sin(angle+p[2]), 120-28.2843*Math.cos(angle+p[2]), 120+63.2455*Math.sin(angle+p[3]), 120-63.2455*Math.cos(angle+p[3]), 120+63.2455*Math.sin(angle+p[4]), 120-63.2455*Math.cos(angle+p[4]), 120+28.2843*Math.sin(angle+p[5]), 120-28.2843*Math.cos(angle+p[5]), 120+44.7214*Math.sin(angle+p[6]), 120-44.7214*Math.cos(angle+p[6]) ]; g.fillPoly(poly); var t = Math.round((getTime() - t1)*1000); LED1.write((t > 30)); //console.log("T8: " + t); return t; }
TEST_10: The required Arrow shape, with a circle round it and using an arrayBuffer. 62ms.
BUT i have seen this go to 200ms on occassion, so there is obviously a risk that this can cause issues and become unresponsive.// takes 62 ms function drawCompass(hd) { if (hd === oldHeading) return; var t1 = getTime(); buf1.setColor(1); buf1.fillCircle(80,80,79,79); buf1.setColor(0); buf1.fillCircle(80,80,69,69); buf1.setColor(1); 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 = [ 80+60*Math.sin(hd+p[0]), 80-60*Math.cos(hd+p[0]), 80+44.7214*Math.sin(hd+p[1]), 80-44.7214*Math.cos(hd+p[1]), 80+28.2843*Math.sin(hd+p[2]), 80-28.2843*Math.cos(hd+p[2]), 80+63.2455*Math.sin(hd+p[3]), 80-63.2455*Math.cos(hd+p[3]), 80+63.2455*Math.sin(hd+p[4]), 80-63.2455*Math.cos(hd+p[4]), 80+28.2843*Math.sin(hd+p[5]), 80-28.2843*Math.cos(hd+p[5]), 80+44.7214*Math.sin(hd+p[6]), 80-44.7214*Math.cos(hd+p[6]) ]; buf1.fillPoly(poly); flip1(40, 30); var t = Math.round((getTime() - t1)*1000); LED1.write((t > 100)); //console.log("T10: " + t); return t; }
-
If it's getting slower over time, is it possible you're not doing a
clearInterval(..) for your intervalRefSec = setInterval(readCompass,
200);?This compass code seems to get slower over time. There is a stopTimer() call which gets called everytime the LCD screen goes off.
function stopTimer() { if(intervalRefSec) {intervalRefSec=clearInterval(intervalRefSec);} if(intervalPerf) {intervalPerf=clearInterval(intervalPerf);} }
Overtime, or sometimes fairly quickly the Buttons become unresponsive as it the edge detection did not kick in. Not sure if the firmware is interrupt driven or just polls the state of the buttons. Clearly something is going on with the readCompass() (the code is copied from the Navigation Compass - I think the problem maybe the tilt correction code as it has a lot of Math.sin tan etc in it.
The intervalPerf timer is acting a bit like a watchdog for the purpose of me understanding what load is being run on the CPU. So if this time is delayed by 300+ms then something is seriously wrong.
Changing perfCheck() to switch LED1 on/off if we detect that the timer was delayed has allowed me to see that when there is a lot of arm movement then the 1000ms timer gets delayed and that is pointing me at the tilt correction code in the Magnav compass.
I will also profile the times for the different funtions.
function perfCheck() { var tNow = getTime(); var tDiff = 1000*(tNow - tPerf) - 1000; tPerf = tNow; //console.log("perf=" + tDiff); LED1.write((tDiff > 50)); }
-
I have the GPS setup in SuperE power mode and an update interval of 30
seconds.Thats not how it works. Super-E gives the GPS full control over when it gets updates and there is no control of the frequency of fixes apart from changing the baud rate. But the GPS effectively stays on all the time and sends a fix every 1 second. This should work fine with GPS Recorder.
The interval time control only applies when using PSMOO mode. It would clash with the the way GPS recorder works as GPS recorder assumed that a fix comes every second and then counts then number of fixes recieved and then logs the next one.
Just spotted that @Gordon has done a change to GPS recorder.
-
-
It looks to me that watches tells you what you have bound to the Buttons.
Below is the result of my stepo watch, which detects press and release of BTN2.global["\xFF"].watches =[ undefined, { pin: D22, recur: true, debounce: 26214, edge: 1, callback: function () { ... }, state: false }, { pin: D22, recur: true, debounce: 26214, edge: -1, callback: function () { ... }, state: false } ] >
Below is the result of my experimental multiclock where I want to have {BTN1, BTN2} short and long presses passed onto the current watch face. I use BTN3 rising edge to switch faces and leave BTN3 long press for the reset.
>global["\xFF"].watches =[ undefined, { pin: D24, recur: true, debounce: 26214, edge: 1, callback: function (btn) { ... }, state: false }, { pin: D22, recur: true, debounce: 26214, edge: 1, callback: function (btn) { ... }, state: false }, { pin: D23, recur: true, debounce: 26214, edge: 1, callback: function () { ... }, state: false, lastTime: 1615420296.37918376922 }, { pin: D24, recur: true, debounce: 26214, edge: -1, callback: function (btn) { ... }, state: false }, { pin: D22, recur: true, debounce: 26214, edge: -1, callback: function (btn) { ... }, state: false } ]
So these are both what I would expect.
setWatch(btn_pressed.bind(null,1), BTN1, {repeat:true,edge:"rising"}); setWatch(btn_pressed.bind(null,2), BTN2, {repeat:true,edge:"rising"}); setWatch(nextFace, BTN3, {repeat:true,edge:"rising"}); setWatch(btn_released.bind(null,1), BTN1, {repeat:true,edge:"falling"}); setWatch(btn_released.bind(null,2), BTN2, {repeat:true,edge:"falling"}); // BTN 3 long press should always reset the bangle
-
I do however have an odd performance issue with one of the Apps.
I have a compass app based on the Magnav code. I use the same compass code in Waypointer and Arrow. All of these compass apps start to run slow after a while and become unresponsive to BTN3 presses. I have been trying to track down the issue.
I am wondering if the following approach is an indicator of performance.
Set a timer for a known time
Record the time using getTime
Then check that the time against the expected time.function startTimer() { draw(); readCompass(); intervalRefSec = setInterval(readCompass, 200); tPerf = getTime(); intervalPerf = setInterval(perfCheck, 1000); } function perfCheck() { var tNow = getTime(); var tDiff = 1000*(tNow - tPerf) - 1000; tPerf = tNow; console.log("perf=" + tDiff); }
If all is good you would expect the timer to come in close to the expected number of milliseconds. In this case if the diff is greater than 1000ms then the timer was delayed.
Sometimes I am seeing this sort of output.> ____ _ | __|___ ___ ___ _ _|_|___ ___ | __|_ -| . | _| | | | | . | |____|___| _|_| |___|_|_|_|___| |_| espruino.com 2v08.187 (c) 2019 G.Williams perf=2.01586914062 perf=-0.63916015625 perf=0.06274414062 perf=-0.08984375 perf=0.0322265625 perf=-0.02880859375 perf=0.00170898437 perf=0.0322265625 perf=140.07739257812 <<<<=== LCD times out, have to BTN3 press perf=1.7412109375 perf=0.06274414062 perf=-0.02880859375 perf=-0.02880859375 perf=0.00170898437 perf=0.00170898437 perf=0.00170898437 perf=0.154296875 perf=-0.08984375 perf=1.77172851562 perf=0.0322265625 perf=-0.02880859375 perf=0.00170898437 perf=1.52758789062 perf=-1.52416992187 perf=0.00170898437 perf=1794.40478515625 <<<<=== LCD times out, have to BTN3 press perf=335.90869140625 <<<< now we see 300+ms delays on a 1000s timer perf=363.13037109375 perf=346.37622070312 perf=1860.96362304687 perf=432.55786132812 perf=427.15625 perf=443.17797851562 perf=338.38061523437 perf=355.68408203125 perf=364.16796875 perf=443.30004882812 perf=443.78833007812 perf=1974.03125 perf=414.91870117187 perf=431.39819335937 perf=421.87670898437 perf=419.43530273437 perf=433.10717773437 perf=364.13745117187 perf=336.15283203125 perf=341.79858398437 perf=442.537109375 perf=398.4697265625 perf=399.53784179687 > >
The above is a lucky example, it does not usually happen with the screen going off (I think that is cooincidence here). I can keep the screen on by pressing BTN3 before it goes off and sometimes I will see this.
If feels like something else is grabbing resource.
Kind of need the equivalent of the unix uptime command to see how loaded the CPU is.
-
I tracked it down. See comments below.
function startdraw() { Bangle.drawWidgets(); ///face.init(); // No as startDraw() will be called when LCD comes on again face.startTimer(); } function setButtons(){ function nextFace(){ iface += 1 iface = iface % FACES.length; stopdraw(); face = FACES[iface](); face.init(); // only call init() when switching faces g.clear(); g.reset(); startdraw(); }
the standard code for stopping / starting the draw/timers
Bangle.on('lcdPower',function(on) { if (on) { startdraw(); } else { stopdraw(); } });
-
I am attempting to build my ultimate multiclock style watch which is a combination of 3 of my clocks, namely "stepo", "waypointer" and "walkers clock". Each uses an arrayBuffer for the display so I need to be careful not to have any memory leaks or bad code.
Each watch face will have an init() function to create any resources and a freeResource() function to set all the variables to undefined.
(() => { function getFace(){ var buf1; // desclare variables function init() { buf1 = Graphics.createArrayBuffer(160,160,1, {msb:true}); // etc } function freeResources() { buf1 = undefined; // etc } // other functions as required return {init:init, freeResources:freeResources, startTimer:startTimer, stopTimer:stopTimer, onButtonShort:onButtonShort, onButtonLong:onButtonLong}; } return getFace; })();
When switching watch faces...
function stopdraw() { face.stopTimer(); face.freeResources(); } function startdraw() { Bangle.drawWidgets(); face.init(); face.startTimer(); } function setButtons(){ function nextFace(){ iface += 1 iface = iface % FACES.length; stopdraw(); face = FACES[iface](); g.clear(); g.reset(); startdraw(); }
Everything works fine for the first cycle through the watch faces. But within a few minutes I get a RED MEMORY_LOW warning at the bottom of the watch face. All the individual Apps work without memory problems.
Anyone any suggestions on if the approach should work and whre to look for memory leaks.
Hugh
-
-
I had a bit of code that was repeatedly doing:
function d() { var img = require("heatshrink").decompress(atob("...")); g.drawImage(img, 80, 80, {scale:3, rotate:radians(course)} ); }
when what was effective / needed was:
var img = require("heatshrink").decompress(atob("...")); function d() { g.drawImage(img, 80, 80, {scale:3, rotate:radians(course)} ); }
-
I am wondering what the different costs are of the different aproaches to drawing graphics.
What is the least / most expensive in terms of CPU time and how could it be meaured ?For example:
// option (1) draw using fillpoly to the screen direct // example: gps Route Viwer g.fillPoly(....); // option (2) have the image as a Object String in a function // example widgps (most widgets) var img = E.toArrayBuffer(atob("....")); g.drawImage(img, x, y); // option (3) draw image read out of flash // example ?? g.drawImage(require("Storage").read("cloud.img")); // option (4) write the image to an ArrayBuffer // example magnav, arrow compass var buf1 = Graphics.createArrayBuffer(160,160,1, {msb:true}); function d() { var img = require("heatshrink").decompress(atob("...")); buf1.drawImage(img, 80, 80, {scale:3, rotate:radians(course)} ); // other draws, then call a flip function flip1(40, 30); // draws the image into the graphics array }
My reason for asking is that I have done an app with graphics using option (4). The app is a vraiant of GPS Navigation which an animated arrow for the compass direction. I have noticed with my App and GPS Navigation that after a while the buttons become unresponsive and often have to be pressed to 2,3,4 times to get the necessary response. It is maybe not noticeable with Apps that dont require much interaction wth the butons.
Be great to have a sample App that did the same thing in differnce ways but you could compare the cost in terms of RAM, CPU, time etc. Maybe one day I will get round to write it.
-
Its not that big an issue really. Unless you want to find geocaches you dont need 1m accuracy. It is just something to take into account when writing waypoint and route following Apps. I'm thinking once you get within 20m of the target you have effectively arrived.
I'll pass on the Kalman filter for now. I have walkers waypoint finder app in progress and after that I am thinking about looking at the GPS rceorder to setup from parameters to control the frequency and distance between logs.
Yeah the code code be improved, it was more the concept I wanted to discuss. Good point about
Bangle.isGPSOn!==undefind
thats a much better idea, no hard coding of version numbers etc.