Linux low level BLE setup for EspruinoTools

Posted on
  • Hello there,

    It's been a week since i received my BangleJS watch (incredible project, kudos for that) but so far i've not be able to upload any application to it. I'm using a Linux based distro without any kind of gui available (pure tty console) and EspruinoTools.

    I don't know if this is relevant in this forum because my troubles comes with the Bluetooth connection and are not from the device itself, but i'm looking for someone who succeeded into connecting the watch to a computer using Linux Bluetooth cli.

    I've read a lot of Linux/Bluetooth dedicated forums, but with the recent deprecation of many tools used in the past and the confusion between Bluetooth Classic and Bluetooth LE, it's been increasingly complicated for me to find relevant information and as i'm not familiar with the BT technology i'm currently overwhelmed.

    According to this page, my controller is supposed to manage version 5.1 and below and i'm running everything as root so there is no permission problems.

    I'm able to detect the device, pair and connect via :

    bluetoothctl
    

    but the EspruinoTools doesn't detect anything :

    $ espruino
    ...
    Error: No Ports Found
    

    And i have no success neither when i manually set the Bluetooth address :

    $ espruino -p XX:XX:XX:XX:XX:XX (obvsly not the real value)
    ...
    Port "XX:XX:XX:XX:XX:XX" not found
    Unable to connect!
    Done
    

    The --verbose flag shows no other error nor warnings.

    If i understand everything correctly, EspruinoTools needs a serial communication socket linked to the watch, but i'm unsure how to open one as every tool i've found and tried works only with Bluetooth Classic.

    So, if anyone has any idea, i'd be glad to listen !

    Best regards,

    Gouwi

  • Ok, yeah - not having a GUI is going to make life more difficult! Do you have an Android phone you could use for the App Uploads?

    To actually upload apps from the CLI I'd recommend the 'apploader.js' file in BangleApps rather that EspruinoTools: https://github.com/espruino/BangleApps/b­lob/master/bin/apploader.js

    It's quite a bit simpler so could be easier to figure out what's wrong (and it automatically uploads the relevant files for each app). EspruinoTools is really if you're planning to do app development.

    You could also take a look at this example code for Noble. If you can get that working, everything else should work: https://www.espruino.com/Interfacing#nod­e-js-javascript

    I think first step would be whether you get a poweredOn event from just this code:

    const noble = require('@abandonware/noble');
    noble.on('stateChange', state => {
      if (state === 'poweredOn') {
        console.log("Success!");
      }
    });
    

    My guess would be that maybe Noble isn't finding your bluetooth radio. There's a 'whitelist' in bluetooth-hci-socket (which Noble uses) and it's possible your Bluetooth radio isn't in there:

    https://github.com/abandonware/node-blue­tooth-hci-socket/blob/master/lib/usb.js#­L14

    It's entirely possibly that just editing that file (in node_modules/node-bluetooth-hci-socket) with your device's USB IDs will make it work.

  • Hello,

    try connecting to the Bangle with espruino when it is not paired to your machine!

    I tried the same in the beginning, and I succeeded to start espruino in server mode to have the IDE connect to it. I just tried it again, but I had to unpair the device from the Bluetooth manager. Only then you will find it with espruino --list. I then connect to it using
    espruino -d Bangle.js

    I have not tried uploading to it from the terminal then however.

  • Thanks @Gordon and @Sebastian for your answers ! I did some progress !

    Do you have an Android phone you could use for the App Uploads?

    Unfortunatly, i have a smartphone with a custom ROM instead of a stock Android one and the web browser i'm using does not work with the WedIDE.

    EspruinoTools is really if you're planning to do app development.

    Well, i'm a software engineer and i do plan to develop some of my own applications, especially tools for sailing ! =)

    You could also take a look at this example code for Noble. If you can get that working, everything else should work: https://www.espruino.com/Interfacing#nod­e-js-javascript

    That's what i've done : i managed to get the poweredOn event fired without any kind of trouble. Then, i wrote a small snippet of code to scan and discover nearby devices (more likely a glorified copy/paste of this example).

    But no devices where found and the script juste hang there, doing nothing. I dived into the noble code and into the bluetooth-hci-socket library and with some trial and error i managed to understand two things :

    1. The value of the bluetooth controller was undefined
    2. The Noble library tried to bind the socket using a raw mode

    So i used the global environnement variables to manually set the bind mode to user and the controller to 0 (hci0) and managed to get the snippet of code to list nearby devices, included my watch :

    $ HCI_CHANNEL_USER=1 NOBLE_HCI_DEVICE_ID=0 node bt_test.js
    powered on
    ...
    aa:bb:cc:dd:ee:ff Bangle.js xxxx
    ...
    

    Finally, i tried to do the same with EspruinoTools :

    $ HCI_CHANNEL_USER=1 NOBLE_HCI_DEVICE_ID=0 espruino --list
    Espruino Command-line Tool 0.1.37
    ...
    
    PORTS:
        aa:bb:cc:dd:ee:ff (Bangle.js xxxx) RSSI -82
    

    However, if i run the command without the --list, i get an error :

    ...
    RangeError [ERR_OUT_OF_RANGE]: The value of "offset" is out of range. It must be >= 0 and <= 3. Received 5
    ...
    

    It's another kind of problem and for now i'm quite satisfied with the progress so far, i'm gonna sleep a little bit and continue later. I will mark the thread as solved as soon as i've successfully uploaded an application to the watch.

    I'll keep you informed !

  • Wow, thanks for the update! It's surprising as I've never had anyone have to do that before.

    It sounds like you're getting pretty close, but one option you do have is just to buy an external USB Bluetooth dongle and then do NOBLE_HCI_DEVICE_ID=0. There are some listed here: http://www.espruino.com/Quick+Start+BLE#­requirements

    While it's not ideal, the dongles are less than $10, so it's just how much you value your time :)

  • Wow, thanks for the update! It's surprising as I've never had anyone have to do that before.

    Well, i'm not suprised : the OS i'm using is called Alpine Linux and it's a Unix / Linux distribution based on Busybox / Musl / OpenRC, far from the GNU / Systemd combo you can find in the many others distributions you can get. On one hand it's an incredibly fast and efficient OS, but on the other hand you need to do most of the things by yourself. I like it because it's also very simple and well designed, and it forces you to grasp an understanding of how systems are articulated but you also need a lot of time to work around problems. It's okay, i'm fine with that !

    It sounds like you're getting pretty close, but one option you do have is just to buy an external USB Bluetooth dongle and then do NOBLE_HCI_DEVICE_ID=0. There are some listed here: http://www.espruino.com/Quick+Start+BLE#­requirements

    While it's not ideal, the dongles are less than $10, so it's just how much you value your time :)

    Thanks for the idea, but'll stick with the integrated Bluetooth controller. I have the feeling that the current error is more related to the interaction between Node.js and Alpine than with the BT connection.

    The error is linked to hci.js, somehow the method Hci.writeAclDataPkt is trying to write a bit of data out of the bounds of a buffer. My guess is that it's size is not correctly computed. I'll investigate as soon as i have some time.

  • Alright, i've made some little progress :

    I know understand parts of the out of bounds error that i got from my last attempt. In this file, in the function Hci.prototype.writeAclDataPkt, we allocate a buffer named first. The size of this buffer is 5 + aclLength, aclLength is the minimum between l2capLength and this._aclBuffers.length. For some reason, the later is 0 in my use case, which means that aclLength is set with the value 0 and finally the buffer is allocated with a size of 5.

    Few lines under that, line 487 and 488, we try to set some data on the buffer at indexes 5 & 7, outside of the buffer range.

    Hci.prototype.writeAclDataPkt = function (handle, cid, data) {
      const l2capLength = 4 + data.length; // data length is 3 therefore l2capLength is 7
    
      const aclLength = Math.min(l2capLength, this._aclBuffers.length); // this._aclBuffers.length is 0, which means that aclLength is 0 as well 
    
      const first = Buffer.alloc(aclLength + 5); // buffer is allocated with a length of 5
    
      // acl header
      first.writeUInt8(HCI_ACLDATA_PKT, 0);
      first.writeUInt16LE(handle | ACL_START_NO_FLUSH << 12, 1);
      first.writeUInt16LE(aclLength, 3);
    
      // l2cap header
      first.writeUInt16LE(data.length, 5); // OUT_OF_BOUNDS error
      first.writeUInt16LE(cid, 7); // OUT_OF_BOUNDS error
    
      data.copy(first, 9);
      data = data.slice(first.length - 9);
    ...
    

    I compared this code snippet with the "old" repository of Noble (this file) and changed the code a little bit to reflect the old version.

    Hci.prototype.writeAclDataPkt = function (handle, cid, data) {
      var pkt = new Buffer(9 + data.length);
    
      pkt.writeUInt8(HCI_ACLDATA_PKT, 0);
      pkt.writeUInt16LE(handle | ACL_START_NO_FLUSH << 12, 1);
      pkt.writeUInt16LE(data.length + 4, 3);
      pkt.writeUInt16LE(data.length, 5);
      pkt.writeUInt16LE(cid, 7);
    
      data.copy(first, 9);
      data = data.slice(first.length - 9);
    ...
    

    I also changed the line 497 :

    // from 
    const fragAclLength = Math.min(data.length, this._aclBuffers.length);
    // to
    const fragAclLength = data.length;
    

    Somehow it "worked" a little bit : the watch received the connection (the screen lights up and the BT logo turns blue) and the EspruinoTools logs says :

    ...
    Noble: Stopping scan (openSerial)
    BT> Connecting
    BT> Connected
    

    Then, after a timeout of a few seconds, i got an error message saying :

    Unable to connect!
    

    This message comes from the espruino-cli.js, line 530.

    I have the feeling that the code i've changed is not enough and that the acl packet is not correctly sent to the watch. Also, it looks like this._aclBuffers.length is not supposed to be 0.

    Does anyone has any idea ? Should i ask on the abandonware/noble github instead ?

  • Wow, that looks promising. Definitely looks like a bug - however I guess this works in normal cases, so in those cases, this._aclBuffers.length would be nonzero - maybe it's worth trying to figure out why that is the case?

    Honestly, I don't think you'll have much luck on abandonware/noble - you can try, but as I understand it it's basically rzr who got fed up with the standard noble not being updated at all. He's trying his best with it, but I don't think he's got much free time.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Linux low level BLE setup for EspruinoTools

Posted by Avatar for Gouwi @Gouwi

Actions