PuckJS and Espruino Hub and MQTT bridge

Posted on
  • Use a Puck to advertise data and try to read them via MQTT client, but data is not visible via mqtt client.

    Configuration and snipped to advertise taken from the the Espruino Hub repo page

    Device: d9:c7:0b:0a:48:20 - Puck.js 4820 (RSSI -59)

    snippet on for Puck.js 4820

    function onInit(){
          var data = {a:"1",b:2};
          NRF.setAdvertising({},{
                showName:false,
                manufacturer:0x0590,
                manufacturerData:JSON.stringify(data)
          });
    }
    
    setTimeout(save,1E3);
    

    Advertising is visible on Espruino Hub and BluefruitConnect.

    Connect to the mqtt server and subscribe to /ble/advertise/d9:c7:0b:0a:48:20/espruin­o and /ble/advertise/d9:c7:0b:0a:48:20/#. Get many payloads for the second sub but none for the first.

    Any hints what's missing?


    2 Attachments

    • Bildschirmfoto 2021-05-13 um 17.57.51.jpg
    • Bildschirmfoto 2021-05-13 um 18.02.50.jpg
  • Used a second Puck to verify that JSON is valid

    // Puck.js : { "a": "1", "b": 2 }
    NRF.requestDevice({ filters: [ { id:"d9:c7:0b:0a:48:20 random"}]}).then(
      function(d) { 
       console.log(JSON.parse(String.fromCharCo­de.apply(null, d.manufacturerData)));
    } );
    /* output
    =Promise: {  }
    {
      "a": "1",
      "b": 2 }
    */
    
  • Got the solution, the config.json has "mqtt_advertise_manufacturer_data": false.

    After setting "mqtt_advertise_manufacturer_data": true and restarting the EspruinoHub the manufacturer data is available.

  • Great! Yeah, I think we were trying to cut down on the 'noise' from MQTT, but that one is a bit frustrating.

    I've just updated the docs...

  • I've just updated the docs...

    Thanks

  • Also worth noting that you were asking about JSON5 in another post. You can do manufacturerData:E.toJS(data) and EspruinoHub is smart enough to parse the more compact JSON5

  • Yep, I am on my way to change this whole communication by manufacturer data to JSON5. For this approach I didn't wanted to have to many changes at the same time ......

  • During tests I realized that it can happen that manufacturerData is missing because got some different advertisements, so it might be helpful to extend the filter.

    filter1 = [{ manufacturerData:{0x0590:{}}}];
    filter2 = [{ id:"d9:c7:0b:0a:48:20 random"}];
    filtersAll = filter1.concat(filter2);
    NRF.requestDevice({ filters: filtersAll }).then(
        function(d) { 
            if ( d.manufacturerData ) 
                console.log(JSON.parse(String.fromCharCo­de.apply(null, d.manufacturerData)));
            console.log('.');
        }
    );
    
    /* output
    =Promise: {  }
    { "w": 2953 }
    .
    */
    
    

    So it look's like the filters work as logic OR. Is there a way to change to AND?

  • Did not look at the code yet, but could imagine - from a design point of view - that putting multiple attributes in a filter object, these attributes work (hopefully?) as AND.

    filter = [{ manufacturerData:{0x0590:{}}}
              , id:"d9:c7:0b:0a:48:20 random"
              }];
    NRF.requestDevice({ filters: filters }).then(
        function(d) { 
            if ( d.manufacturerData ) 
                console.log(JSON.parse(String.fromCharCo­de.apply(null, d.manufacturerData)));
            console.log('.');
        }
    );
    /* output
    =Promise: {  }
    { "w": 2953 }
    .
    */
    

    If this is not the case or would break backward compatibility, a simple solution for interpretation is to use nested array: arrays of ('OR') filters as array elements are AND of OR-filters:

    Ex: [ [f0,f1,f2...] , [fa,fb,fc,...] ] equals (f0||f1||f2||...) && (fa||fb||fc||...).

    'OR' of 'AND' filters look then like this:

    Ex: [ [ [f0,f1,f2...] , [fa,fb,fc,...] ] , [ [f9,f8,f7...] , [fz,fx,fy,...] ].

    This evaluator has to 'look ahead' (way too far, all the way thru) to determine wether the next element is an OR or AND element (odd or even [] enclosed, and that is bad. Therefore, I hope that multiple properties in a filter work as AND (as stated in the very beginning): [ and ] 'Operators' demarcate an OR expression, { and } demarcate an AND expression, with the AND expression element has a refining, 'deep-peeling' AND ({} - nesting).

    Comparing this to logical expressions, notation of precedence seems inverse: in a mixed and and or logical expression, ANDs have precedence and parenthesis allow to inject ORs as AND elements. As long as nesting stays within 2 levels, the filter evaluator needs only one extra 'accumulator' / variable to hold the temporary result while calculating an inner expression. Going full nested of any depth, requires a stack... and all things become way more complicated

  • putting multiple attributes in a filter object, these attributes work (hopefully?) as AND

    That's right, yes! :)

  • Thanks @allObjects and @Gordon for the confirmation.

  • Did some test with two pucks, Puck.js f5b9 and Puck.js 4820, using NRF.requestDevice and try to create a AND for id and manufacturerData.

    d9:c7:0b:0a:48:20 - Puck.js 4820 (RSSI -59)
    f0:d8:ef:29:f5:b9 - Puck.js f5b9 (RSSI -58)

    // advertise code for both Puck.js
    function onInit(){
          var data = {a:"1",b:2};
          NRF.setAdvertising({},{
                showName:true,
                manufacturer:0x0590,
                manufacturerData:JSON.stringify(data)
          });
    }
    setTimeout(save,1E3);
    

    Then run this simple code

    // manufacturerData and name as AND?
    filter = [{manufacturerData:{0x0590:{}}},{name:"P­uck.js 4820"}];
    
    // some times this 
    >NRF.requestDevice({ filters: filter}).then(function(d){console.log(d)­;});
    =Promise: {  }
    BluetoothDevice: {
      "id": "f0:d8:ef:29:f5:b9 random",   <-- wrong device
      "rssi": -61,
      "data": new Uint8Array([2, 1, 6, 18, 255, 144, 5, 123, 34, 97, 34, 58, 34, 49, 34, 44, 34, 98, 34, 58, 50, 125, 8, 8, 80, 117, 99, 107, 46, 106, 115]).buffer,
      "manufacturer": 1424,
      "manufacturerData": new Uint8Array([123, 34, 97, 34, 58, 34, 49, 34, 44, 34, 98, 34, 58, 50, 125]).buffer,
      "shortName": "Puck.js"
     }
    
    // and some times that
    >NRF.requestDevice({ filters: filter}).then(function(d){console.log(d)­;});
    =Promise: {  }
    BluetoothDevice: {
      "id": "d9:c7:0b:0a:48:20 random",   <-- correct device
      "rssi": -44,
      "data": new Uint8Array([2, 1, 6, 18, 255, 144, 5, 123, 34, 97, 34, 58, 34, 49, 34, 44, 34, 98, 34, 58, 50, 125, 8, 8, 80, 117, 99, 107, 46, 106, 115]).buffer,
      "manufacturer": 1424,
      "manufacturerData": new Uint8Array([123, 34, 97, 34, 58, 34, 49, 34, 44, 34, 98, 34, 58, 50, 125]).buffer,
      "shortName": "Puck.js"
     }
    

    Try different filter possibility

    // manufacturerData and name as AND ?
    filter = [[{manufacturerData:{0x0590:{}}}],[{name­:"Puck.js 4820"}]];
    
    // some times this
    >NRF.requestDevice({ filters: filter}).then(function(d){console.log(d)­;});
    =Promise: {  }
    BluetoothDevice: {
      "id": "d9:c7:0b:0a:48:20 random",
      "rssi": -44,
      "data": new Uint8Array([2, 1, 6, 18, 255, 144, 5, 123, 34, 97, 34, 58, 34, 49, 34, 44, 34, 98, 34, 58, 50, 125, 8, 8, 80, 117, 99, 107, 46, 106, 115]).buffer,
      "manufacturer": 1424,
      "manufacturerData": new Uint8Array([123, 34, 97, 34, 58, 34, 49, 34, 44, 34, 98, 34, 58, 50, 125]).buffer,
      "shortName": "Puck.js"
     }
    
    // or sometimes this
    >NRF.requestDevice({ filters: filter}).then(function(d){console.log(d)­;});
    =Promise: {  }
    BluetoothDevice: {
      "id": "de:30:d5:92:80:7f random",
      "rssi": -54,
      "data": new Uint8Array([2, 1, 6, 13, 9, 77, 68, 66, 84, 52, 50, 81, 32, 56, 48, 55, 102]).buffer,
      "name": "MDBT42Q 807f"
     }
    // or sometimes this 
    >NRF.requestDevice({ filters: filter}).then(function(d){console.log(d)­;});
    =Promise: {  }
    BluetoothDevice: {
      "id": "f0:d8:ef:29:f5:b9 random",
      "rssi": -60,
      "data": new Uint8Array([2, 1, 6, 18, 255, 144, 5, 123, 34, 97, 34, 58, 34, 49, 34, 44, 34, 98, 34, 58, 50, 125, 8, 8, 80, 117, 99, 107, 46, 106, 115]).buffer,
      "manufacturer": 1424,
      "manufacturerData": new Uint8Array([123, 34, 97, 34, 58, 34, 49, 34, 44, 34, 98, 34, 58, 50, 125]).buffer,
      "shortName": "Puck.js"
     }
    
    

    Hmm, not sure about filters any more.....

  • Hmm - there should be AND... https://github.com/espruino/Espruino/blo­b/master/libs/bluetooth/jswrap_bluetooth­.c#L1462

    So unless there's a bug, jswrap_ble_filter_device should set matches to false if anything in a group doesn't match (so that's AND), and then if any group matches it returns true (so that should be OR).

    // OR
    filter = [{manufacturerData:{0x0590:{}}},{name:"P­uck.js 4820"}];
    // AND
    filter = [{manufacturerData:{0x0590:{}}, name:"Puck.js 4820"}];
    
    
  • Hmm, let me try again, was confused about this text:

    /// Filter device based on a list of filters (like .requestDevice. Return true if it matches ANY of the filters one line above L1461.

  • ...is:

    • properties of an object are AND
    • objects in the array are OR ( ORof what the Objects result to )
  • The AND filter is not working on my side.

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

PuckJS and Espruino Hub and MQTT bridge

Posted by Avatar for MaBe @MaBe

Actions