Write hex value to a characteristic?

Posted on
Page
of 2
/ 2
Next
  • Hi,

    I'd like to control some BLE light bulbs with my Puck and I just read the following tutorial: https://www.espruino.com/Puck.js+and+Blu­etooth+Lightbulbs

    However the lightbulb I want to control expects hex values and I don't know how to write these values with writeValue(). In the nRF Connect app I have to send a byte array: 0x 00FF0000 for red or 0x 0000FF00 for green for instance.

    Any idea?

  • In Espruino you just need to split it off into an array of bytes - so take each pair of 2 characters and put 0x in front to tell Espruino that each one is a hex value:

    00FF0000 -> [ 0x00, 0xFF, 0x00, 0x00 ]
    

    hope that helps!

  • Thanks Gordon. I just tried but this does nothing (just displays Done! in the console). If I set the same value using the nrf app, the color is set to red.

    I believe the Puck can connect to the device, get the service and characteristic because I don't have any exception but the color is not modified when I write a value to the characteristic.

    function setLight() {
      var gatt;
      NRF.connect("AC:E6:4B:06:6F:CD").then(fu­nction(g) {
        gatt = g;
        return gatt.getPrimaryService("0000ff02-0000-10­00-8000-00805f9b34fb");
      }).then(function(service) {
        return service.getCharacteristic("0000fffc-0000­-1000-8000-00805f9b34fb");
      }).then(function(characteristic) {
        characteristic.writeValue([ 0x00, 0xFF, 0x00, 0x00 ]);
      }).then(function() {
        gatt.disconnect();
        console.log("Done!");
      });
    }
    
    setLight();
    
  • I'm not sure what to suggest there - what you've got looks fine.

    Are you sure you've got the right characteristic there?

  • Yes I checked multiple times. And I don't get any exception so I guess the Puck is able to write the value. If I specify a non-existing characteristic, I do get an exception.

  • Just checking - do you have up to date firmware on Puck.js (1v91?) - I guess it could help.

    You could also try waiting a second with setTimeout before disconnecting. I guess the bulb may fail if it doesn't think it can update the status.

    Out of interest, what's the light bulb you're trying to write to?

  • I just updated the firmware but it didn't change anything. I also tried with a timeout.

    Here are the devices I want to control: http://www.playbulb.com/en/playbulb-cand­le-bluetooth-smart-led-flameless-candle.­html
    I also have some light bulbs from the same manufacturer. Here's a blog I wrote about the candles a while ago: https://pdominique.wordpress.com/2015/01­/02/hacking-playbulb-candles/

  • Ahh, great! I actually saw that blog post when I was looking around at BLE lightbulbs :)

    Really strange though - I've used a few BLE bulb like devices and haven't found one I couldn't control yet. Have you tried reading the characteristic (if it supports it?)

  • Well actually this is what I get when I use readValue() and stringify it:

    console.log("Got:", JSON.stringify(value));
    
    
    >
     _____                 _
    |   __|___ ___ ___ _ _|_|___ ___
    |   __|_ -| . |  _| | | |   | . |
    |_____|___|  _|_| |___|_|_|_|___|
              |_| http://espruino.com
     1v91 Copyright 2016 G.Williams
    >
    =undefined
    Got: "\x00\x00\xFF\x00"
    Done!
    > 
    
  • And what colour is the light when that happens? I'm wondering if the value has actually been set correctly and hasn't taken effect for some reason, or if it just hasn't set the value at all

  • I can set the color with the nrf app and readValue returns the right value. But I can't set the color with the Puck for some reason.

  • Strange - not sure what to suggest I'm afraid.

    Do you have any Bluetooth sniffing devices you might be able to use to see if there's a difference in how Espruino writes to the Playbulb?

    The only thing I can think is that the Playbulb maybe has the handles for the characteristic in a strange order, and that's confusing Puck.js?

  • I just bought me a playbulb, too (because I was curious) and will have a look at it tonight.

    What I'd try:
    On iOS there is the LightBlue app that can create/clone and simulate any BLE device from service/characteristic perspective.
    When I pretend to be a PlayBulb on device A (iOS running lightBlue) I can track what the PlayBulb app on Device B actually sends to do certain things.
    (Kind of "man in the middle" debugging)

  • I don't have a sniffing device but using LightBlue to simulate the candle is a good idea.

  • Can you run this on your Puck, with the PlayBulb's address? It could take a while to complete.

    var gatt;
    NRF.connect("c4:be:84:de:a9:ba").then(fu­nction(g) {
      gatt = g;
      console.log("Connected - finding services");
      return gatt.getPrimaryServices();
    }).then(function(services) {
      print(services);
      console.log("Finding characteristics");
        function sender(resolve, reject) {
          if (services.length) {
            var service = services.shift();
            service.getCharacteristics().then(functi­on(characteristics) {
              console.log("Service "+service.uuid+" => ", characteristics);
              sender(resolve, reject);
            }).catch(reject);
          } else  {
            resolve();
          }
        }
        return new Promise(sender);  
    }).then(function() {
      gatt.disconnect();
      console.log("Done! Disconnected.");
    });
    

    It should dump what I thinks all the characteristics are - I've just noticed having some trouble grabbing the UUID from one smart bulb (I get "0x0000[vendor]") and the problems could be related?

  • Here you go:

    Connected - finding services
    [
      BluetoothRemoteGATTService {
        "uuid": "0x1800",
        "isPrimary": true, "start_handle": 1, "end_handle": 7 },
      BluetoothRemoteGATTService {
        "uuid": "0x1801",
        "isPrimary": true, "start_handle": 8, "end_handle": 8 },
      BluetoothRemoteGATTService {
        "uuid": "0xff02",
        "isPrimary": true, "start_handle": 9, "end_handle": 28 },
      BluetoothRemoteGATTService {
        "uuid": "0x180f",
        "isPrimary": true, "start_handle": 29, "end_handle": 32 },
      BluetoothRemoteGATTService {
        "uuid": "0x180a",
        "isPrimary": true, "start_handle": 33, "end_handle": 65535 }
     ]
    Finding characteristics
    Service 0x1800 =>  [
      BluetoothRemoteGATTCharacteristic {
        "uuid": "0x2a00",
        "handle_value": 3, "handle_decl": 2 },
      BluetoothRemoteGATTCharacteristic {
        "uuid": "0x2a01",
        "handle_value": 5, "handle_decl": 4 },
      BluetoothRemoteGATTCharacteristic {
        "uuid": "0x2a04",
        "handle_value": 7, "handle_decl": 6 }
     ]
    Service 0x1801 =>  [  ]
    Service 0xff02 =>  [
      BluetoothRemoteGATTCharacteristic {
        "uuid": "0x2a37",
        "handle_value": 11, "handle_decl": 10 },
      BluetoothRemoteGATTCharacteristic {
        "uuid": "0xfff8",
        "handle_value": 14, "handle_decl": 13 },
      BluetoothRemoteGATTCharacteristic {
        "uuid": "0xfff9",
        "handle_value": 16, "handle_decl": 15 },
      BluetoothRemoteGATTCharacteristic {
        "uuid": "0xfffa",
        "handle_value": 18, "handle_decl": 17 },
      BluetoothRemoteGATTCharacteristic {
        "uuid": "0xfffb",
        "handle_value": 20, "handle_decl": 19 },
      BluetoothRemoteGATTCharacteristic {
        "uuid": "0xfffc",
        "handle_value": 22, "handle_decl": 21 },
      BluetoothRemoteGATTCharacteristic {
        "uuid": "0xfffd",
        "handle_value": 24, "handle_decl": 23 },
      BluetoothRemoteGATTCharacteristic {
        "uuid": "0xfffe",
        "handle_value": 26, "handle_decl": 25 },
      BluetoothRemoteGATTCharacteristic {
        "uuid": "0xffff",
        "handle_value": 28, "handle_decl": 27 }
     ]
    Service 0x180f =>  [
      BluetoothRemoteGATTCharacteristic {
        "uuid": "0x2a19",
        "handle_value": 31, "handle_decl": 30 }
     ]
    Service 0x180a =>  [
      BluetoothRemoteGATTCharacteristic {
        "uuid": "0x2a25",
        "handle_value": 35, "handle_decl": 34 },
      BluetoothRemoteGATTCharacteristic {
        "uuid": "0x2a27",
        "handle_value": 37, "handle_decl": 36 },
      BluetoothRemoteGATTCharacteristic {
        "uuid": "0x2a26",
        "handle_value": 39, "handle_decl": 38 },
      BluetoothRemoteGATTCharacteristic {
        "uuid": "0x2a28",
        "handle_value": 41, "handle_decl": 40 },
      BluetoothRemoteGATTCharacteristic {
        "uuid": "0x2a29",
        "handle_value": 43, "handle_decl": 42 },
      BluetoothRemoteGATTCharacteristic {
        "uuid": "0x2a50",
        "handle_value": 45, "handle_decl": 44 }
     ]
    Done! Disconnected.
    
  • Wow, that all looks totally fine. Sorry - not sure what could be the issue there at all then.

    All I can think is that the characteristic you're writing to expects 'Write with Response' when Puck.js only performs a 'Write'? I guess you could check with nRF Connect/LightBlue/etc and see what flags are set on the characteristic

  • nRF Connect says Read and WriteWithoutResponse.

    I tried to simulate the device with LightBlue but the Puck can't connect to the fake device (unhandled exception in promise or something). The official app doesn't seem to work with the fake device either. nRF Connect can connect and write to the characteristic just fine though. I can see the new value being written in the LightBlue app.

  • I also tried the simulation. At first the PlayBulb app always crashed.
    Then I also entered some (most) of the default values I found in the original PlayBulb (e. g. the version strings).
    The app then worked, but I could not find/read any values in LightBlue that were changed.

    However, I have just now started testing the puck against the real device.
    Will give you an update in a few minutes.

  • OK - does not work for me either.

    I have not hardcoded the MAC but search for devices with the corresponding service:

    function setLight( r, g, b ) {
      var gatt;
      var c;
    
      console.log( "rgb=", r, g, b );
      NRF.requestDevice(
        {filters:[{services:["FF02"]}]}
      ).then( dev=>{
        console.log( "found", dev.name );
        return dev.gatt.connect();
      }).then( g => {
        console.log( "connected" );
        gatt = g;
        return gatt.getPrimaryService("FF02");
      }).then(function(service) {
        console.log( "service found" );
        return service.getCharacteristic("FFFC");
      }).then(function(characteristic) {
        console.log( "characteristic found" );
        c = characteristic;
        return c.readValue();
      }).then( v => {
        console.log( "value", v );
        return c.writeValue(String.fromCharCode(0,r,g,b­));
      }).then( r => {
        console.log( "result", r );
        gatt.disconnect();
        console.log("Done!");
      });
    }
    
    
    var col = 1;
    
    setWatch(
      evt => {
        var long = ( evt.time - evt.lastTime ) > 0.2;
        if( long ) {
          setLight( 0, 0, 0 );
        }
        else {
          col++;
          if( col > 7 ) col = 1;
    
          console.log( "col", col );
          setLight( 
            col & 1 ? 0xFF : 0, 
            col & 2 ? 0xFF : 0,
            col & 4 ? 0xFF : 0
          );
        }
      },
      BTN,
      { edge: "falling", debounce: 50, repeat: true } 
    );
    

    readValue()correctly returns the 4 bytes (white-red-green-blue) I have written with LightBlue to Service FF02 / Characteristic FFFC before.

    But writeValue()does not do or return anything.

    @Gordon: You are right. LightBlue shows WriteWithoutResponse for the Characteristic in question by the way.

    Is that something the puck BLE code does not handle yet?


    2 Attachments

    • IMG_8475.PNG
    • IMG_8474.PNG
  • I just checked - actually Puck.js does do WriteWithResponse (it doesn't do a simple write).

    So I'm not really sure what the issue could be here - I think realistically we're going to need to sniff the BLE traffic while writing with the Puck and with another device, and compare the two

  • I just ordered a BLE sniffer, I'll see if I can use it to sniff the communication and find out what's wrong.

  • Great, thanks!

  • Ok I just sniffed the communication between the nrf app and the candle and then the Puck and the candle. The opcode is definitely different and that seems to be the issue (WriteWithResponse not supported by the candle?).

    nrf app Write Request:

    Bluetooth Attribute Protocol (0x0004)
    Opcode: Write Request (0x52)
    Handle: 0x0016
    Value: 000000ff
    

    Puck Write Request:

    Bluetooth Attribute Protocol (0x0004)
    Opcode: Write Request (0x12)
    Handle: 0x0016
    Value: 00ff0000
    

    Puck Error Response:

    Bluetooth Attribute Protocol
    Opcode: Error Response (0x01)
    Request Opcode in Error: Write Request (0x12)
    Handle in Error: 0x0016
    Error Code: Write Not Permitted (0x03)
    
  • Thanks for checking that - it looks likely that's the issue then.

    The opcodes aren't the ones I send to the BLE stack (eg. BLE_GATT_OP_WRITE_REQ vs BLE_GATT_OP_WRITE_CMD) but I suppose it's likely that the Bluetooth stack would use its own enumerations anyway.

    If you're able to compile yourself, you could change the opcode used in jsble_central_characteristicWrite and see if it makes any difference.

    Otherwise it looks like I need to add code to the BLE_GATTC_EVT_CHAR_DISC_RSP handler to store whether 'write with response' is available for a specific characteristic, and then to check that when writing to see which write should be performed.

    I've just filed a bug for it here: https://github.com/espruino/Espruino/iss­ues/1059

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

Write hex value to a characteristic?

Posted by Avatar for pdominique @pdominique

Actions