Puck.js Connect to Adafruit Feather Board

Posted on
Page
of 2
Prev
/ 2
  • Hi Gordon, thank you again for spending time helping me with this.

    I would like to be able to communicate back and forth between the Arduino and the Puck.

    Is there something that I can share from the Bluefruit that would help?

    When I use:

    return require("ble_uart").connect(device);
    

    I am receiving the error "Uncaught Error: Unhandled promise rejection: CCCD Handle not found"

    But if I use

    return require("ble_simple_uart").write(device, "1");
    

    The puck will write "1" to the feather.

  • I'll try to add as much code and images as possible to see what might help figure this out.

    Here is my bare bones Arduino Code. It should receive the value from the Puck and then if it matches, send a value back.

    [#include](https://forum.espruino.com/search/?q=%23include) <Arduino.h>
    [#include](https://forum.espruino.com/search/?q=%23include) <SPI.h>
    [#include](https://forum.espruino.com/search/?q=%23include) "Adafruit_BLE.h"
    [#include](https://forum.espruino.com/search/?q=%23include) "Adafruit_BluefruitLE_SPI.h"
    [#include](https://forum.espruino.com/search/?q=%23include) "Adafruit_BluefruitLE_UART.h"
    [#include](https://forum.espruino.com/search/?q=%23include) "BluefruitConfig.h"
    
    [#if](https://forum.espruino.com/search/?q=%23if) SOFTWARE_SERIAL_AVAILABLE
      [#include](https://forum.espruino.com/search/?q=%23include) <SoftwareSerial.h>
    [#endif](https://forum.espruino.com/search/?q=%23endif)
    
    [#define](https://forum.espruino.com/search/?q=%23define) FACTORYRESET_ENABLE         1
    [#define](https://forum.espruino.com/search/?q=%23define) MINIMUM_FIRMWARE_VERSION    "0.6.6"
    [#define](https://forum.espruino.com/search/?q=%23define) MODE_LED_BEHAVIOUR          "MODE"
    [#define](https://forum.espruino.com/search/?q=%23define) LED_BUILTIN                 13
    
    Adafruit_BluefruitLE_SPI ble(BLUEFRUIT_SPI_CS, BLUEFRUIT_SPI_IRQ, BLUEFRUIT_SPI_RST);
    
    void error(const __FlashStringHelper* err) {
      Serial.println(err);
      while (1);
    }
    
    int incomingByte = 0;
    String readString;
    
    void setup() {
      pinMode(LED_BUILTIN, OUTPUT);
      Serial.begin(9600);
      Serial.println();
    
      Serial.println("Setting beacon configuration details: ");
      Serial.println("---------------------------------------");
    
      if (!ble.begin(VERBOSE_MODE)) {
        error(F("Couldn't find Bluefruit, make sure it's in CoMmanD mode & check wiring?"));
      }
    
      if (FACTORYRESET_ENABLE) {
        Serial.println(F("Performing a factory reset: "));
        if (!ble.factoryReset()) {
          error(F("Couldn't factory reset"));
        }
      }
    
      ble.echo(false);
      Serial.println("Requesting Bluefruit info:");
      ble.info();
      Serial.println();
    
      ble.verbose(false);
    
      Serial.println(F("Setting device name to 'Splitz Start Beacon'"));
      if (!ble.sendCommandCheckOK(F("AT+GAPDEVNAME=Splitz Start Beacon"))) {
        error(F("Could not set device name?"));
      }
    
      Serial.println(F("Switching to DATA mode!"));
      ble.setMode(BLUEFRUIT_MODE_DATA);
    
      while (!ble.isConnected()) {
        delay(500);
      }
    
      if (ble.isConnected()) {
        Serial.println(F("BLE device is connected."));
      }
    }
    
    void loop() {
      if (ble.available()) {
        char input[64];
        int len = ble.readBytesUntil('\n', input, sizeof(input)-1);
        input[len] = 0;
        
        Serial.print(F("Received: "));
        Serial.println(input);
    
        if (strcmp(input, "hi") == 0) {
          Serial.println(F("Sending reply: hello"));
          ble.println("hello");
          // Write to the characteristic
          ble.sendCommandCheckOK(("AT+GATTCHAR=1,hello"));
        }
      }
    }
    

    The first image is the Serial response.

    This is the code I have for the Puck.

    // Blink green for 100ms
    function blinkGreen() {
      LED2.write(true);
      setTimeout(function () { LED2.write(false); }, 100);
    }
    
    // Variables
    var feather = "ff:8d:05:ae:17:64 random";
    let gattServer;
    
    // UART Service UUID
    const UART_SERVICE_UUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E";
    // UART RX Characteristic UUID (for sending data to the Arduino)
    const UART_RX_CHARACTERISTIC_UUID = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E";
    // UART TX Characteristic UUID (for receiving data from the Arduino)
    const UART_TX_CHARACTERISTIC_UUID = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E";
    
    // Function to connect and send data
    function connectAndSend() {
      console.log("Trying to connect to Arduino Board.");
      NRF.requestDevice({ filters: [{ id: feather }], active: true })
        .then(function(device) {
          console.log("Connecting to device...");
          return device.gatt.connect();
        })
        .then(function(gatt) {
          console.log("Connected to GATT server");
          gattServer = gatt;
          return gatt.getPrimaryService(UART_SERVICE_UUID);
        })
        .then(function(service) {
          console.log("Got UART service");
          return service.getCharacteristic(UART_RX_CHARACTERISTIC_UUID);
        })
        .then(function(characteristic) {
          console.log("Got RX characteristic, sending 'hi'...");
          return characteristic.writeValue("hi");
        })
        .then(function() {
          console.log("Message sent successfully");
          blinkGreen();
          return gattServer.getPrimaryService(UART_SERVICE_UUID);
        })
        .then(function(service) {
          console.log("Setting up TX Service...");
          return service.getCharacteristic(UART_TX_CHARACTERISTIC_UUID);
        }).then(function(characteristic) {
          console.log("Setting up listener for incoming data");
          characteristic.on('characteristicvaluechanged', function(event) {
            console.log("RX: "+JSON.stringify(event.target.value.buffer));
          });
          return characteristic.startNotifications();
        })
        .catch(function(error) {
          console.log("Error: " + error);
        });
    }
    
    // Function to disconnect
    function disconnect() {
      if (gattServer && gattServer.connected) {
        gattServer.disconnect();
        console.log("Disconnected");
      }
    }
    
    // Button press to initiate connection and sending
    setWatch(connectAndSend, BTN, {edge:"rising", repeat:true, debounce:50});
    

    The 2nd image is the output I receive. Even if I move the TX handle to before the RX , I still get a CCCD error.

    I also just added log to the Primary, RX, and TX return so that we can see what is being returned.

    Trying to connect to Arduino Board.
    Connecting to device...
    Connected to GATT server
    Got UART service
    BluetoothRemoteGATTService: {
      "device": BluetoothDevice: {
        "id": "ff:8d:05:ae:17:64 random",
        "rssi": -61,
        "data": new Uint8Array([2, 1, 6, 2, 10, 0, 17, 6, 158, 202, 220, 36, 14, 229, 169, 224, 147, 243, 163, 181, 1, 0, 64, 110]).buffer,
        "services": [
          "6e400001-b5a3-f393-e0a9-e50e24dcca9e"
         ],
        "gatt": BluetoothRemoteGATTServer: {
          "device":  ... ,
          "connected": true, "handle": 1 }
       },
      "uuid": "6e400001-b5a3-f393-e0a9-e50e24dcca9e",
      "isPrimary": true, "start_handle": 31, "end_handle": 65535 }
     
    Got RX characteristic, sending 'hi'...
    BluetoothRemoteGATTCharacteristic: {
      "service": BluetoothRemoteGATTService: {
        "device": BluetoothDevice: {
          "id": "ff:8d:05:ae:17:64 random",
          "rssi": -61,
          "data": new Uint8Array([2, 1, 6, 2, 10, 0, 17, 6, 158, 202, 220, 36, 14, 229, 169, 224, 147, 243, 163, 181, 1, 0, 64, 110]).buffer,
          "services": [
    "6e400001-b5a3-f393-e0a9-e50e24dcca9e"
           ],
          "gatt": BluetoothRemoteGATTServer: {
    "device":  ... ,
    "connected": true, "handle": 1 }
         },
        "uuid": "6e400001-b5a3-f393-e0a9-e50e24dcca9e",
        "isPrimary": true, "start_handle": 31, "end_handle": 65535 },
      "uuid": "6e400002-b5a3-f393-e0a9-e50e24dcca9e",
      "handle_value": 37, "handle_decl": 36,
      "properties": { "broadcast": false, "read": false, "writeWithoutResponse": true, "write": true,
        "notify": false, "indicate": false, "authenticatedSignedWrites": false }
     }
     
    Setting up TX Service...
    BluetoothRemoteGATTCharacteristic: {
      "service": BluetoothRemoteGATTService: {
        "device": BluetoothDevice: {
          "id": "ff:8d:05:ae:17:64 random",
          "rssi": -61,
          "data": new Uint8Array([2, 1, 6, 2, 10, 0, 17, 6, 158, 202, 220, 36, 14, 229, 169, 224, 147, 243, 163, 181, 1, 0, 64, 110]).buffer,
          "services": [
    "6e400001-b5a3-f393-e0a9-e50e24dcca9e"
           ],
          "gatt": BluetoothRemoteGATTServer: {
    "device":  ... ,
    "connected": true, "handle": 1 }
         },
        "uuid": "6e400001-b5a3-f393-e0a9-e50e24dcca9e",
        "isPrimary": true, "start_handle": 31, "end_handle": 65535 },
      "uuid": "6e400003-b5a3-f393-e0a9-e50e24dcca9e",
      "handle_value": 33, "handle_decl": 32,
      "properties": { "broadcast": false, "read": false, "writeWithoutResponse": false, "write": false,
        "notify": true, "indicate": false, "authenticatedSignedWrites": false }
     }
     
    Setting up listener for incoming data
    Error: CCCD Handle not found
    

    2 Attachments

    • Screenshot 2024-09-28 at 7.34.50.png
    • Screenshot 2024-09-28 at 7.30.54.png
  • Yes, you'd expect simple_uart to work because that's not trying to get the CCCD characteristic.

    Please could you try running this? It should dump all the info Espruino knows about the device:

    var device,gatt;
    const feather = "ff:8d:05:ae:17:64 random"; // Change me!
    
    NRF.requestDevice({ filters: [{ id: feather }] }).then(function(d) {
      device = d;
      console.log("Connecting");
      return device.gatt.connect();
    }).then(function(g) {
      gatt = g;
      console.log("Connected");
      return gatt.getPrimaryServices();
    }).then(function getServices(services) {
      if (services.length==0) return;
      let service = services.shift();
      print(`Service ${service.uuid} ${service.start_handle}=>${service.end_handle}`);
      return service.getCharacteristics().then(characteristics => {
        if (characteristics===undefined)
          print("  No Characteristics");
        else characteristics.forEach(char => {
          print(`  Characteristic ${char.uuid} ${char.handle_value} (desc=${char.handle_decl}) ${E.toJS(char.properties)}`);
        });
        return Promise.resolve(services).then(getServices);
      });
    }).then(() => {
      gatt.disconnect();
    });
    

    But also if you could run NRF Connect on your phone, connect to the Arduino device,
    then tap on the Nordic UART Service entry to expand it and screenshot what you see, that'd be great.

  • Hi Gordon - thanks so much for all of your support!

    This is what the Puck returns after connection:

    >
    Connecting
    Connected
    >
    Service 0x1800 1=>7
      Characteristic 0x2a00 3 (CCCD 2) {broadcast:false,read:true,writeWithoutResponse:false,write:true,notify:false,indicate:false,authenticatedSignedWrites:false}
      Characteristic 0x2a01 5 (CCCD 4) {broadcast:false,read:true,writeWithoutResponse:false,write:false,notify:false,indicate:false,authenticatedSignedWrites:false}
      Characteristic 0x2a04 7 (CCCD 6) {broadcast:false,read:true,writeWithoutResponse:false,write:false,notify:false,indicate:false,authenticatedSignedWrites:false}
    Service 0x1801 8=>11
      Characteristic 0x2a05 10 (CCCD 9) {broadcast:false,read:false,writeWithoutResponse:false,write:false,notify:false,indicate:true,authenticatedSignedWrites:false}
    Service 0x0000[vendor] 12=>19
      Characteristic 0x0000[vendor] 14 (CCCD 13) {broadcast:false,read:false,writeWithoutResponse:true,write:false,notify:false,indicate:false,authenticatedSignedWrites:false}
      Characteristic 0x0000[vendor] 16 (CCCD 15) {broadcast:false,read:false,writeWithoutResponse:false,write:true,notify:true,indicate:false,authenticatedSignedWrites:false}
      Characteristic 0x0000[vendor] 19 (CCCD 18) {broadcast:false,read:true,writeWithoutResponse:false,write:false,notify:false,indicate:false,authenticatedSignedWrites:false}
    Service 0x180a 20=>30
      Characteristic 0x2a29 22 (CCCD 21) {broadcast:false,read:true,writeWithoutResponse:false,write:false,notify:false,indicate:false,authenticatedSignedWrites:false}
      Characteristic 0x2a24 24 (CCCD 23) {broadcast:false,read:true,writeWithoutResponse:false,write:false,notify:false,indicate:false,authenticatedSignedWrites:false}
      Characteristic 0x2a28 26 (CCCD 25) {broadcast:false,read:true,writeWithoutResponse:false,write:false,notify:false,indicate:false,authenticatedSignedWrites:false}
      Characteristic 0x2a26 28 (CCCD 27) {broadcast:false,read:true,writeWithoutResponse:false,write:false,notify:false,indicate:false,authenticatedSignedWrites:false}
      Characteristic 0x2a27 30 (CCCD 29) {broadcast:false,read:true,writeWithoutResponse:false,write:false,notify:false,indicate:false,authenticatedSignedWrites:false}
    Service 6e400001-b5a3-f393-e0a9-e50e24dcca9e 31=>65535
      Characteristic 6e400003-b5a3-f393-e0a9-e50e24dcca9e 33 (CCCD 32) {broadcast:false,read:false,writeWithoutResponse:false,write:false,notify:true,indicate:false,authenticatedSignedWrites:false}
      Characteristic 6e400002-b5a3-f393-e0a9-e50e24dcca9e 37 (CCCD 36) {broadcast:false,read:false,writeWithoutResponse:true,write:true,notify:false,indicate:false,authenticatedSignedWrites:false}
    

    I've attached all of the pictures from nRF Connect.


    4 Attachments

    • IMG_3474.PNG
    • IMG_3473.PNG
    • IMG_3472.PNG
    • IMG_3475.PNG
  • Thanks! So I've been trying to track this down, and as far as I can see, the issue is that the Bluefruit isn't putting the CCCD handle right after the characteristic, which is what Espruino is expecting (and which seems to work on most other devices).

    Please can you try changing the code as follows:

        }).then(function(characteristic) {
          console.log("Setting up listener for incoming data");
          characteristic.on('characteristicvaluechanged', function(event) {
            console.log("RX: "+JSON.stringify(event.target.value.buffer));
          });
          characteristic.handle_cccd = 35; // <--------- this line is new
          return characteristic.startNotifications();
        })
    

    Unfortunately NRF connect doesn't show what the 'handle values' are for the characteristics, and I don't think Adafruit publish the source code for their Bluefruits so I can't actually see what's going on.

    If 35 doesn't work, maybe try 34? failing that, 38 or 39.

    Sorry it's such a mess - but this is the first time in 5+ years it's come up and it feels like the Bluefruits are doing something a bit different to the way other devices do.

  • BTW, you can see all handle numbers with gatttool in linux, see e.g. this blog post https://cxiao.net/posts/2015-12-13-gatttool/

  • Hi Gordon!

    Thank you for your patience and support with my project. Good news, we have success!!! I'll post the final code below for anyone that is having a similar issue with the BlueFruit Feather. What seemed to make it finally work were two things - 1) The direct setting of the CCCD handle to 35. 2) Moving the TX up so that it registers the notification before sending the value to the Arduino board.

    In the code below I've also added the buffer conversion, and a disconnect once it receives the correct value so that you can push the Puck to start the process again.

    Again, thank you so much for your patience and support!

    // Blink green for 100ms
    function blinkGreen() {
      LED2.write(true);
      setTimeout(function () { LED2.write(false); }, 100);
    }
    
    // Variables
    var feather = "ff:8d:05:ae:17:64 random";
    let gattServer;
    
    // UART Service UUID
    const UART_SERVICE_UUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E";
    // UART RX Characteristic UUID (for sending data to the Arduino)
    const UART_RX_CHARACTERISTIC_UUID = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E";
    // UART TX Characteristic UUID (for receiving data from the Arduino)
    const UART_TX_CHARACTERISTIC_UUID = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E";
    
    // Function to connect and send data
    function connectAndSend() {
      console.log("Trying to connect to Arduino Board.");
      NRF.requestDevice({ filters: [{ id: feather }], active: true })
        .then(function(device) {
          console.log("Connecting to device...");
          return device.gatt.connect();
        })
        .then(function(gatt) {
          console.log("Connected to GATT server");
          gattServer = gatt;
          return gatt.getPrimaryService(UART_SERVICE_UUID);
        })
        .then(function(service) {
          console.log("Setting up TX Service...");
          return service.getCharacteristic(UART_TX_CHARACTERISTIC_UUID);
        }).then(function(txcharacteristic) {
          console.log("Setting up listener for incoming data...");
          txcharacteristic.on('characteristicvaluechanged', function(event) {
            console.log("Received: "+JSON.stringify(event.target.value.buffer));
            // Convert the event target value to a Uint8Array
            let receivedData = new Uint8Array(event.target.value.buffer);
            // Convert the array of ASCII codes to a string
            let decodedValue = String.fromCharCode.apply(null, receivedData);
            // Log the received value
            console.log("RX: " + decodedValue);
            if (decodedValue.trim() === "1") {
              disconnect();
            } else {
              console.log("Received unexpected value: " + decodedValue);
            }
          });
          txcharacteristic.handle_cccd = 35;
          return txcharacteristic.startNotifications();
        })
        .then(function() {
          return gattServer.getPrimaryService(UART_SERVICE_UUID);
        })
        .then(function(service) {
          console.log("Got UART service");
          //console.log(service);
          console.log(" ");
          return service.getCharacteristic(UART_RX_CHARACTERISTIC_UUID);
        })
        .then(function(rxcharacteristic) {
          console.log("Got RX characteristic, sending '1'...");
          //console.log(rxcharacteristic);
          console.log(" ");
          return rxcharacteristic.writeValue("1");
        })
        .catch(function(error) {
          console.log("Error: " + error);
        });
    }
    
    // Function to disconnect
    function disconnect() {
      if (gattServer && gattServer.connected) {
        gattServer.disconnect();
        console.log("Disconnected");
      }
    }
    
    // Button press to initiate connection and sending
    setWatch(connectAndSend, BTN, {edge:"rising", repeat:true, debounce:50});
    
  • Great! Glad it's working now!

    I made a change to the firmware so if it doesn't find the CCCD in the normal place it looks one after.

    So please could you try installing a 'cutting edge' firmware and removing the characteristic.handle_cccd = 35; line? I think it might be ok after that and you could even use the default ble_uart module.

  • Hi Gordon,
    For sure, happy to try it. Let me know what you need.

  • Thanks! Literally all you need to do for now is update to the latest (cutting edge) firmware, and then try and run the code you posted above but without the txcharacteristic.handle_cccd = 35; line

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

Puck.js Connect to Adafruit Feather Board

Posted by Avatar for user158306 @user158306

Actions