• edit: OMG, there is actually a critical bug in the code (buf[bufi] = 0; when bufi == buf.length). If this turns out to be the source, please apologize the distraction!

    Unfortunately I can't reliably reproduce the issue. It happened thrice in a row (2 outdoors, 1 indoors), then once at BT disconnect (Android blessed.BluetoothPeripheral.closeConnect­ion; saw the bootloader screen) followed by 2 times without issues. My guess now is that the BT device in my phone is buggy and somehow triggers an extra hard reset on connection loss/disconnect.

    I will therefore rewrite the app to recover in case of a hard reset and look for any pattern in the data.

    Code:

    var ACC = 1;
    var MAG = 2;
    var GPS = 3;
    var TIM = 4;
    
    var bufs = [];
    var buf = new ArrayBuffer(1000);
    var bufi = 0;
    
    function get_dv(len) {
      if (buf.length < (bufi + len)) {
        buf[bufi] = 0;
        bufs.push(buf);
        buf = new ArrayBuffer(1000);
        bufi = 0;
      }
      var dv = new DataView(buf, bufi);
      bufi += len;
      return dv;
    }
    
    function send_buf() {
      if (bufs.length) {
        Bluetooth.write(btoa(bufs[0]));
      }
      Bluetooth.write('\n');
    }
    
    function remove_buf() {
      bufs.shift();
    }
    
    function on_accel(a) {
      var v = get_dv(7);
      v.setUint8(0, ACC);
      v.setInt16(1, a.x * 8192);
      v.setInt16(3, a.y * 8192);
      v.setInt16(5, a.z * 8192);
    }
    
    function on_mag(m) {
      var v = get_dv(7);
      v.setUint8(0, MAG);
      v.setInt16(1, m.x);
      v.setInt16(3, m.y);
      v.setInt16(5, m.z);
    }
    
    function on_gps(g) {
      if (g.fix == 0) return;
      var v = get_dv(26);
      v.setUint8(0, GPS);
      v.setInt32(1, g.lat * 11930464);
      v.setInt32(5, g.lon * 11930464);
      v.setUint16(9, clamp(g.alt * 16, 0, 65535));
      v.setFloat64(11, g.time.getTime());
      v.setUint8(19, g.fix);
      v.setUint8(20, clamp(g.hdop * 12, 0, 255));
      v.setUint8(21, g.satellites);
      v.setUint16(22, g.course * 182);
      v.setUint16(24, clamp(g.speed * 327, 0, 65535));
    }
    
    var clamp = (a, mi, ma) => a < mi ? mi : (ma < a ? ma : a);
    
    function log(s) {
      Terminal.println(s);
    }
    
    g.clear();
    Bangle.loadWidgets();
    Bangle.drawWidgets();
    
    log('# Cyclometer');
    log('press top button to start');
    Bangle.setGPSPower(true, 'cyc');
    
    var satc = 0;
    function ready(g) {
      if (g.fix) {
        Bangle.removeListener('GPS', ready);
        Bangle.buzz(500);
        log('GPS ready');
      } else if (g.satellites != satc) {
        satc = g.satellites;
        log('GPS satellites: ' + satc);
      }
    }
    Bangle.on('GPS', ready);
    
    setWatch(() => {
      log('starting ...');
      Bangle.setLCDTimeout(0);
      Bangle.setOptions({btnLoadTimeout: 0});
      Bangle.setCompassPower(true, 'cyc');
      Bangle.on('accel', on_accel);
      Bangle.on('mag', on_mag);
      Bangle.on('GPS', on_gps);
    
      var tim_id = setInterval(() => {
        var v = get_dv(9);
        v.setUint8(0, TIM);
        v.setFloat64(1, getTime());
      }, 60 * 1000);
    
      var log_id = setInterval(() => {
        var m = process.memory();
        var p = Math.floor((m.usage / m.total) * 100);
        log('queue: ' + bufs.length + ' memory: ' + p + '%');
        if (67 <= p) {
          Bangle.buzz(500);
          setTimeout(() => Bangle.buzz(500), 1000);
        }
      }, 5 * 1000);
    
      log('started');
      log('hold top button for 3 seconds to stop');
    
      var cnt;
      var cnt_id;
      var btn_id = setWatch((e) => {
        if (e.state) {
          log('stop in 3');
          cnt = 3;
          cnt_id = setTimeout(stop, 1000);
          Bangle.buzz(300);
        } else if (cnt_id) {
          clearTimeout(cnt_id);
          cnt_id = undefined;
        }
      }, BTN1, {repeat: true, edge: 'both'});
    
      function stop() {
        cnt--;
        if (cnt == 0) {
          log('stopping ...');
          Bangle.setLCDTimeout(10);
          Bangle.setOptions({btnLoadTimeout: 1500});
          Bangle.setGPSPower(false, 'cyc');
          Bangle.setCompassPower(false, 'cyc');
          var _ = Bangle.removeListener.bind(Bangle);
          _('accel', on_accel);
          _('mag', on_mag);
          _('GPS', on_gps);
          clearInterval(tim_id);
          clearInterval(log_id);
          clearWatch(btn_id);
          log('stopped');
        } else {
          log('stop in ' + cnt);
          cnt_id = setTimeout(stop, 1000);
          Bangle.buzz(300);
        }
      }
    }, BTN1);
    

About

Avatar for !evil @!evil started