• Just an update to this for other AI & ML nerds like me. With Bangle.js2 I'm now able to spell the English alphabet in the air by "drawing" characters in the air (CAPS only + space and backspace, left out W). If you want to see it in action, check a short video I uploaded to my LinkedIn account, the link should work for anyone.

    The main changes I've done compared to the tutorial I wrote, is putting below code snippet in the beginning of the programs used to collect the gestures and to recognise them. The code sets the sensitivity for starting and ending the gestures. Depending on your use case, you might want to tweak the settings accordingly.

    Bangle.setOptions({gestureEndThresh: Math.pow(700, 2), gestureStartThresh: Math.pow(600,2), gestureInactiveCount: 6, gestureMinLength: 15});
    
    

    If you want to connect your Bangle to e.g. a computer, and write in any application, the below code is what I uploaded to Bangle. I'm sure it could be optimized and cleaned (and commented!), but as a proof of concept, it works good enough for me. Feel free to provide improvements though, that way I'll learn JS better myself.

    var storage = require('Storage');
    var kb = require("ble_hid_keyboard");
    NRF.setServices(undefined, { hid : kb.report });
    
    const settings = storage.readJSON('setting.json',1) || { HID: false };
    
    var sendHid, next, prev, toggle, up, down, profile;
    var time_on_screen = 500;
    
    Bangle.setOptions({gestureEndThresh: Math.pow(700, 2), gestureStartThresh: Math.pow(700,2), gestureInactiveCount: 6, gestureMinLength: 15});
    
    
    if (settings.HID=="kb" || settings.HID=="kbmedia") {
      profile = 'Keyboard';
      if (settings.HID=="kbmedia") {
        sendHid = function (code, cb) {
          try {
            NRF.sendHIDReport([2,0,0,code,0,0,0,0,0]­, () => {
              NRF.sendHIDReport([2,0,0,0,0,0,0,0,0], () => {
                if (cb) cb();
              });
            });
          } catch(e) {
            print(e);
          }
        };
      } else {
        sendHid = function (code, cb) {
          try {
            NRF.sendHIDReport([0,0,code,0,0,0,0,0], () => {
              NRF.sendHIDReport([0,0,0,0,0,0,0,0], () => {
                if (cb) cb();
              });
            });
          } catch(e) {
            print(e);
          }
        };
      }
      next = function (cb) { sendHid(0x4f, cb); };
      prev = function (cb) { sendHid(0x50, cb); };
      toggle = function (cb) { sendHid(0x2c, cb); };
      up = function (cb) {sendHid(0x52, cb); };
      down = function (cb) { sendHid(0x51, cb); };
    } else {
      E.showPrompt("Enable HID?",{title:"HID disabled"}).then(function(enable) {
        if (enable) {
          settings.HID = "kb";
          require("Storage").write('setting.json',­ settings);
          setTimeout(load, 1000, "hidkbd.app.js");
        } else setTimeout(load, 1000);
      });
    }
    
    function drawApp() {
      g.clear();
      g.setFont("6x8",2);
      g.setFontAlign(0,0);
      g.drawString(profile, 120, 120);
      const d = g.getWidth() - 18;
    
      function c(a) {
        return {
          width: 8,
          height: a.length,
          bpp: 1,
          buffer: (new Uint8Array(a)).buffer
        };
      }
    
      g.drawImage(c([16,56,124,254,16,16,16,16­]),d,40);
      g.drawImage(c([16,16,16,16,254,124,56,16­]),d,194);
      g.drawImage(c([0,8,12,14,255,14,12,8]),d­,116);
    }
    
    if (next) {
      Bangle.on('aiGesture', (v) => {
        E.showMessage(v);
        switch (v) {
          case 'A':
            kb.tap(kb.KEY.A, 0);
    //        next(() => {});
            break;
          case 'B':
            kb.tap(kb.KEY.B, 0);
    //        next(() => {});
            break;
          case 'BACKSPACE':
            kb.tap(kb.KEY.BACKSPACE, 0);
            break;
          case 'C':
            kb.tap(kb.KEY.C, 0);
    //        prev(() => {});
            break;
          case 'D':
            kb.tap(kb.KEY.D, 0);
            break;
          case 'E':
            kb.tap(kb.KEY.E, 0);
            break;
          case 'F':
            kb.tap(kb.KEY.F, 0);
            break;
          case 'G':
            kb.tap(kb.KEY.G, 0);
            break;
          case 'H':
            kb.tap(kb.KEY.H, 0);
            break;
          case 'I':
            kb.tap(kb.KEY.I, 0);
            break;
          case 'J':
            kb.tap(kb.KEY.J, 0);
            break;
          case 'K':
            kb.tap(kb.KEY.K, 0);
            break;
          case 'L':
            kb.tap(kb.KEY.L, 0);
            break;
          case 'M':
            kb.tap(kb.KEY.M, 0);
            break;
          case 'N':
            kb.tap(kb.KEY.N, 0);
            break;
          case 'O':
            kb.tap(kb.KEY.O, 0);
            break;
          case 'P':
            kb.tap(kb.KEY.P, 0);
            break;
          case 'Q':
            kb.tap(kb.KEY.Q, 0);
            break;
          case 'R':
            kb.tap(kb.KEY.R, 0);
            break;
          case 'S':
            kb.tap(kb.KEY.S, 0);
            break;
          case 'SPACE':
            kb.tap(kb.KEY[" "], 2);
            break;
          case 'T':
            kb.tap(kb.KEY.T, 0);
            break;
          case 'U':
            kb.tap(kb.KEY.U, 0);
            break;
          case 'V':
            kb.tap(kb.KEY.V, 0);
            break;
          case 'X':
            kb.tap(kb.KEY.X, 0);
            break;
          case 'Y':
            kb.tap(kb.KEY.Y, 0);
            break;
          case 'Z':
            kb.tap(kb.KEY.Z, 0);
            break;
        }
        setTimeout(drawApp, time_on_screen);
      });
    
      setWatch(function(e) {
        var len = e.time - e.lastTime;
        if (len > 0.3 && len < 0.9) {
          E.showMessage('prev');
          setTimeout(drawApp, 1000);
          prev(() => {});
        } else {
          E.showMessage('up');
          setTimeout(drawApp, 1000);
          up(() => {});
        }
      }, BTN1, { edge:"falling",repeat:true,debounce:50})­;
    
      setWatch(function(e) {
        var len = e.time - e.lastTime;
        if (len > 0.3 && len < 0.9) {
          E.showMessage('next');
          setTimeout(drawApp, 1000);
          next(() => {});
        } else {
          E.showMessage('down');
          setTimeout(drawApp, 1000);
          down(() => {});
        }
      }, BTN3, { edge:"falling",repeat:true,debounce:50})­;
    
      setWatch(function(e) {
        E.showMessage('toggle');
        setTimeout(drawApp, 1000);
        toggle();
      }, BTN2, { edge:"falling",repeat:true,debounce:50})­;
    
      drawApp();
    }
    
  • Wow! Looks amazing!
    And the video demonstration turned out to be very epic, I watched it several times and I want more :)

About