Bluetooth Low Energy (a.k.a Bluetooth LE, BTLE, Bluetooth Smart)

Gatt Client support

09 Sep 2015

Your LED peripheral blinking endlessly is the correct behaviour for this demo. It must mean that you've connected and are able to read and write as a client.

09 Sep 2015

is there some document like PDF that explain the BLE blinker works( to avoid to bordering you). now i need to handle the central with a local serial port like PC UART. the goal is to connect different BLE peripherals with a central unit BLE . I saw on the terminal of my PC some messages coming from the Uart of the status of the central, is there some commands to handle it as asked above ? many thansk in advance!

09 Sep 2015

Enrico,

Sequence flow of the LEDBlinker: upon a connection, the connectionCallback() calls launchServiceDiscovery() which results in the characteristicDiscoveryCallback. In characteristicDiscoveryCallback we note that we've discovered the LED characteristic. in main(), after the termination of service discovery, if we find that we're discovered the LEDCharacteristic, we issue ledCharacteristic.read(). The completion of read() will result in triggerToggledWrite() (because that's the callback we set). triggerToggledWrite() will issue a ledCharacteristic.write(1, &toggledValue). completion of write() will result in triggerRead(), and the sequence continues.

I'm sorry I don't understand the rest of the question relating to connecting multiple BLE peripherals. It should be no different than connecting a single peripheral (as long as the application is able to keep per-connection information).

09 Sep 2015

about the question you didn't understand: i get info like the picture attached here /media/uploads/Erry/info.bmp.bmp

what i need i to send via PC some command to manage/debug the Central. is it possible ?

for example -i want to connect the peripheral -i want to disconnect the peripheral -stop peripheral led blinking from the central -restore peripheral led blinking from the central

what i really need is a UART or SPP with 2 board (central and peripheral) ,is there some code ready ? can you give me the link? thks Bye

09 Sep 2015

printf() and scanf() work like normal and use the UART through the mbed interface chip. You can have the peripheral scan for a command which is sent over UART. You can hook all this together using python scripts running on a host PC reading and writing to the consoles of the central and peripheral.

09 Sep 2015

ok ,clear so,it means that there is no code ready as UART between 2 module.

thanks Regards

16 Sep 2015

i am hacking the uart service code. the central talks to the gatt client instead of the server. this allows the two modules to connect the uart service. it works for the communication from central to peripheral. but the central receives nothing. the event ondatawriten for the gattclient always delivers 0 as data length. this means the gatt client receives something ...

need more time for hacking ...

16 Sep 2015

Hi are there same metods, libraries or anytingh to genrate a pwm hardware?! i think that the pwm as follow is software is correct?!. pwm.period_us(50); pwm.pulsewidth_us(x);

16 Sep 2015
16 Sep 2015

with this i can create a pwm hardware?

This is hardware or softWare? where i can find a example for hardware?

PwmOut pwm (p21); pwm.period_us(20); pwm.pulsewidth(x);

Sorry but i'am new

16 Sep 2015

software in mbed doesn't generate hardware (yet). Please take this discussion elsewhere or start a new post somewhere appropriate under mbed.

20 Oct 2015

Hi I have a question here, when I connect to a peripheral and jump to launchServiceDiscovery() characteristicDiscoveryCallback works fine but serviceDiscoveryCallback not work, showing uart message like below, Do you have any clue for this? May I make flow like this onServiceDiscoveryTermination discoverServices terminateServiceDiscovery launchServiceDiscovery (for Characteristic discovery)

/media/uploads/gillwei7/lauch_service_discovery.jpg

20 Oct 2015

Please refer to the docs around launch(). In particular, https://github.com/ARMmbed/ble/blob/master/ble/ServiceDiscovery.h#L96. You might want to provide NULL as the characteristic callback: https://github.com/ARMmbed/ble/blob/master/ble/ServiceDiscovery.h#L105

27 Oct 2015

robert bouwens wrote:

i am hacking the uart service code. the central talks to the gatt client instead of the server. this allows the two modules to connect the uart service. it works for the communication from central to peripheral. but the central receives nothing. the event ondatawriten for the gattclient always delivers 0 as data length. this means the gatt client receives something ...

need more time for hacking ...

Hi Robert, all,

Following on from a few points which you made.

I have two nrf51822 platforms, both running an application that has a BLE UART service and also a custom BLE service ( similar to ButtonSense ) which I use as a means of passing control characteristics and a status.

I connect to one device from the Nordic toolbox UART app and then pass it a command over the UART to scan and connect to peripherals. While doing this I am writing to the UART serice rx characteristic as a means of debug and then I get the prints in the app. I am familiar with the notification mechanism by which the rx characteristic notifies the app of a write to that characteristic.

My firmware works correctly in that one nrf51822 device operating in CENTRAL mode can control another nrf51822 as peripheral and the nrf51822 operating in CENTRAL is also still connected as a PERIPHERAL to the app. However, as soon as I call connect I no longer get any UART notifications back on the Rx characteristic, however the writes I make to the Tx characteristic to send data to the firmware from the Nordic app do seem to work.

So in my scanning callback. I see the two BLE_UART_WRITEs ( macro resolves to pUartService->writeString() ) and then call connect.

void scanAdvertCallback (const Gap::AdvertisementCallbackParams_t *params)
{

  BLE_UART_WRITE ("Ad[%02x %02x %02x %02x %02x %02x]\r\n", // I SEE THIS UART WRITE IN THE NORDIC APP
           params->peerAddr[5], params->peerAddr[4], params->peerAddr[3], params->peerAddr[2], params->peerAddr[1], params->peerAddr[0] );

  BLE_UART_WRITE ("Rssi %d, %u, AdType %u\r\n",
           params->rssi, params->isScanResponse, params->type ); // AND THIS ONE...

  m_ble.gap().connect( params->peerAddr, Gap::ADDR_TYPE_RANDOM_STATIC, NULL, NULL );

}

and in my connection callback...

void connectionCallback ( const Gap::ConnectionCallbackParams_t *params ) 
{
  if (params->role == Gap::CENTRAL) 
  {
    BLE_UART_WRITE ( "Perip Conn %d\n", params->handle ); // BUT AFTER I HAVE CONNECTED TO THE PERIPHERAL I DON't SEE THIS ONE.
    
    gCtrl.hPeriphConnHandle = params->handle;

    m_ble.gattClient().onServiceDiscoveryTermination( discTermCallback );
    m_ble.gattClient().launchServiceDiscovery( params->handle, servDiscCallback, charDiscCallback );
  }
  else if (params->role == Gap::PERIPHERAL)
  {  ... etc

My connection to the peripheral succeeds however I never see anymore BLE_UART_WRITEs in the Nordic app.

Any ideas, I have a recollection about reading about something like this in the past but I cannot find the thread after soom searching. Is this some how related to the scope in which I am operating, for example if I am receiving a BLE connectionback in CENTRAL role and write to the UART service am I writing to a different UART service context than if I am writing in PERIPHERAL ROLE?

Cheers,

Allen

27 Oct 2015

Hi Allen, All

The current implementation of the UART service can't work properly if the device has multiple connections. The problem is that the write and the writeString member function of the UARTService class does not specify on which connection the data should be written.

You can workaround this limitation by adding overloads of write and writeString methods to the UARTService class

    size_t write(Gap::Handle_t connectionHandle, const void *_buffer, size_t length) {
        size_t         origLength = length;
        const uint8_t *buffer     = static_cast<const uint8_t *>(_buffer);

        if (ble.getGapState().connected) {
            unsigned bufferIndex = 0;
            while (length) {
                unsigned bytesRemainingInSendBuffer = BLE_UART_SERVICE_MAX_DATA_LEN - sendBufferIndex;
                unsigned bytesToCopy                = (length < bytesRemainingInSendBuffer) ? length : bytesRemainingInSendBuffer;

                /* copy bytes into sendBuffer */
                memcpy(&sendBuffer[sendBufferIndex], &buffer[bufferIndex], bytesToCopy);
                length          -= bytesToCopy;
                sendBufferIndex += bytesToCopy;
                bufferIndex     += bytesToCopy;

                /* have we collected enough? */
                if ((sendBufferIndex == BLE_UART_SERVICE_MAX_DATA_LEN) ||
                    // (sendBuffer[sendBufferIndex - 1] == '\r')          ||
                    (sendBuffer[sendBufferIndex - 1] == '\n')) {
                    // THIS IS WHERE THE CONNECTION HANDLE SHOULD BE SPECIFIED
                    ble.gattServer().write(connectionHandle, getRXCharacteristicHandle(), static_cast<const uint8_t *>(sendBuffer), sendBufferIndex);
                    sendBufferIndex = 0;
                }
            }
        }

        return origLength;
    }

    size_t writeString(Gap::Handle_t connectionHandle, const char *str) {
        return write(connectionHandle, str, strlen(str));
    }

with that little change, you will be able to specify the target connection of the write operation.

#define INVALID_CONNECTION_HANDLE                0xFFFF
Gap::Handle_t peripheralConnectionHandle = INVALID_CONNECTION_HANDLE;

#define BLE_UART_WRITE (STR)  \
do { \
  if(peripheralConnectionHandle != INVALID_CONNECTION_HANDLE) { \
    pUartService->writeString(peripheralConnectionHandle, STR); \
  } \
while(0)


void connectionCallback ( const Gap::ConnectionCallbackParams_t *params ) 
{
  if (params->role == Gap::PERIPHERAL) {  
     // setup connection handle
     peripheralConnectionHandle = params->handle;
     // ...
  }
}


void disconnectionCallback(const Gap::ConnectionCallbackParams_t *params) {
  if(params->handle == peripheralConnectionHandle) {
        // cleanup connection handle 
        peripheralConnectionHandle = INVALID_CONNECTION_HANDLE;
        // ...
  }
}

Of course, this is just a quick workaround, we will update our UARTService really soon.

Regards,

Vincent

27 Oct 2015

Hi Vincent,

thanks for the update! Looking forward to test the new uart Service.

Cheers Robert

24 Nov 2015

Hi all,

I am not sure this is the right place to post this issue but I don't know where to start. I have stumbled upon some major issue using the BLE_API and Serial communication. It apears that the application hang, similar to the issues with the Ticker class reported earlier.

I am using the online compiler with the BLENano modules and NRF51-Dongle. This is running a GATT client in central role and all I am doing is take bytes from the serial line and send it on a BLE characteristic discovered by the GATT client.

I found this statement in one UART example from Nordic: "This UART example is configured with flow control enabled which is necessary when softdevice is enabled, in order to prevent data loss." https://github.com/NordicSemiconductor/nrf51-UART-examples/blob/master/app_uart_library_example_with_ble/main.c

I tried to enable the flow control using: m_serial_port.set_flow_control( RTSCTS, p08, p10); However this does not appear to be implemented in the nRF51822 library, correct?

I am experiencing that some baudrates work better than other and this makes me think there is something related to BLE and Serial interoperability. Did someone experience character loss or something similar using Serial and BLE together?

Please advice,

/Lars

24 Nov 2015

Lars,

The UART example from Nordic is unrelated to BLE_API or mbed. It is not clear what you're trying to do. I'll assume you're working with mbed-classic, since you've mentioned using the online IDE. If you're having trouble sending a data stream received from UART over BLE, perhaps you can attempt to send a fixed byte stream (like from a pre-initialized const byte array) over BLE and see if that works better.

rohit.

25 Nov 2015

Rohit,

I agree I am unclear in my post. Also, I realize this might not be the best place to post the question. I just wanted to see if someone jumped on the problem with Soft Device and Serial port related to flow control. I guess half of my concern is related to BLE_API and the other to the Serial implementation. I understand that the example from nordic is unrelatad to mbed, except that both are using S130 soft device, right? My point is that according to the statement from Nordic it is not possible to use the UART together with the Soft Device unless UART Flow Control is enabled. And since Serial::set_flow_control() is not implemented for nrf51822 in mbed classic, the UART can not be used together with BLE_API in mbed in a safe way, or am I missing some magic behind the scene? Also, I tested your suggestion and it works perfectly.

/Lars

26 Nov 2015

Lars, without H/W flow control and the Soft Device enabled then speeds up to 9600 baud will 'work'.

26 Nov 2015

Lars, the nRF51 family of targets is run at a low clock-speed to conserve energy. The default is to run them at 32KHz (and switch to the 16MHz only for BLE stack activity). User level code runs at the slow clock speed of 32kHz. So if you run your UART at high baud rates, you might not be feeding a fast enough clock into the UART hardware to be able to drive fast communication.

If you want to enable a faster clock (like 16mhz), you can do so by setting `NRF_CLOCK->TASKS_HFCLKSTART = 1;` and waiting for `while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) ;`. You might need to include some headers from the NordicSDK to satisfy the compiler. Refer to SystemInit() to see how the low-frequency clock is setup as the default during startup.

27 Nov 2015

Back on topic of the Gatt Client stuff :)

When I call,

m_ble.gap().setScanParams(500, 400, 5); m_ble.gap().startScan(scanAdvertCallback);

I scan and get a callback with Gap::AdvertisementCallbackParams_t, I can then check the address, rssi and and other advertising data and choose whether I want to connect to that device.

A few questions supposing I have a device operating as master and three other identical peripherals all advertising the same services.

1 . I do a m_ble.gap().startScan() what criteria dictate which device I will get the callback for. Is it purely random or based on rssi? i.e. the device with the highest rssi is found first.

2. If I then decide, based on info in Gap::AdvertisementCallbackParams_t that I don't want to connect to that device and call startScan() again, will I get a different device the next time I get a callback or the same one.

So the main question is, supposing I have a collection of devices I want to scan for and interrate through until I find the one I am looking for based on the Gap::AdvertisementCallbackParams_t, what is the most efficent way of doing it.

Thanks,

Allen

27 Nov 2015

Hi Allen,

First of all you will not have three completely identical devices because the MAC address will be different for each devices.

1) the startScan function will start a scan procedure. This procedure will stop when you call stopScan. In between, each time the Bluetooth chip receive an advertisement, the callback you have passed to the startScan function will be called.

2) You don't have to call startScan again, the scan procedure will remains active until you stop it. Your callback registered will be called after each advertisement packet receive. There is no filtering on any sort.

3) It is very simple, just start the scan and once the desired device show up connect to it. If you want to optimize discovery (time or power consumption), you can play with the scanParams.

Vincent

27 Nov 2015

@Afoster I imagine that when you mean "identical", what you mean is that they are running the same application, offering the same services, etc. But they will have a different address. So your master could decide which peripheral to connect to based on address or any other values that it can get. I will no try to answer your questions:

> 1 . I do a m_ble.gap().startScan() what criteria dictate which device I will get the callback for. Is it purely random or based on rssi? i.e. the device with the highest rssi is found first.

-> From what I understand this entirely depends on the environment, probably the device that is closes to your master is likely to get picked up first. But I believe that you should definitely NOT rely in an order of getting advertisement packets. Rather something like the address to filter what you would like to connect to.

> 2. If I then decide, based on info in Gap::AdvertisementCallbackParams_t that I don't want to connect to that device and call startScan() again, will I get a different device the next time I get a callback or the same one.

-> The callback will get executed whenever the BLE hardware detects an advertisement packet, so if the peripheral that you already picked up broadcasts another advertising packet and your master picks it up then you will get another callback. I believe that if you do not set the timeout scan parameter you will scan until you explicitly stop the scanning procedure. Alternatively, if you timeout you can decide to start again if you havent connected to anything.

> So the main question is, supposing I have a collection of devices I want to scan for and interrate through until I find the one I am looking for based on the Gap::AdvertisementCallbackParams_t, what is the most efficent way of doing it.

-> Probably the most difficult question. What do you mean by "efficient"? If you mean faster then just set your master to have a very long window and short interval and set your peripherals to have very short interval for advertisement. If you mean power efficient then try the opposite. I suppose the answer is: its entirely up to what you are seeking and how you set your scanParams, so I would suggest to play with them.

If you want to experiment with this, but do not want to write the whole code, then use the available examples such as LEDBlinker (master) and LED (peripheral) and see what you get.

27 Nov 2015

Thanks guys for the quick replies.

I have working example code. I am just playing around with some connection strategies.

At the minute I have added code to add the results of scanning callbacks to list if the address is not already present in that list. I then have a function to grab the highest rssi.

27 Nov 2015

Thanks guys for the quick replies.

I have working example code. I am just playing around with some connection strategies.

At the minute I have added code to add the results of scanning callbacks to list if the address is not already present in that list. I then have a function to grab the highest rssi.

29 Nov 2015

Wayne & Rohit, thanks for your comments.

With some optimization of my user level code, together with a Baudrate of 9600, my application now runs fine without flow control. However, I found that setting 'NRF_UART0->CONFIG = 1;' apears to enable the HW flow control.

Thanks for the support

30 Nov 2015

Good work on this everyone!

Any chance we can get an object/method pair signature for the gattClient methods (onDataRead, onDataWritten, launchServiceDiscovery, onServiceDiscoveryTermination)? I'd like to pass in a member function as the callback.

Jay

01 Dec 2015

@Jay: We recently updated FunctionPointerWithContext to accept member pointers. You should be able to pass a FunctionPointerWithContext object containing your <object, member> tuple.

01 Dec 2015

Hi Jay,

It is possible to pass a member function pointer for the Gatt client callbacks. A change to allow these operations has been introduced last week.

Right now, there is no object/method pair signature but you can pass a member function bound to an instance by using the function makeFunctionPointer.

class MyGattClientDelegate { 
public:
    MyGattClientDelegate(GattClient& _client) : client(_client) { 
        // initialization of callbacks
        client.onServiceDiscoveryTermination(makeFunctionPointer(this, &MyGattClientDelegate::whenServiceDiscovered));

        // read, write and HVX are 'persitents' callbacks 
        client.onDataWritten(makeFunctionPointer(this, &MyGattClientDelegate::whenDataWritten));
        client.onDataRead(makeFunctionPointer(this, &MyGattClientDelegate::whenDataRead));
    }

    ~MyGattClientDelegate() { 
        // unregister persistent callbacks 
        client.onDataWritten().detach(makeFunctionPointer(this, &MyGattClientDelegate::whenDataWritten));
        client.onDataRead().detach(makeFunctionPointer(this, &MyGattClientDelegate::whenDataRead));
    }

    ble_error_t launchDiscovery(Gap::Handle_t connectionHandle) { 
        return client.launchServiceDiscovery(
            connectionHandle, 
            makeFunctionPointer(this, &MyGattClientDelegate::whenServiceDiscovered),
            makeFunctionPointer(this, &MyGattClientDelegate::whenCharacteristicDiscovered)
        );
    }

    void whenDataRead(const GattReadCallbackParams* params) { 
        // implementation goes here
    }

    void whenDataWritten(const GattWriteCallbackParams* params) { 
        // implementation goes here
    }

    void whenServiceDiscovered(const DiscoveredService* params) { 
        // implementation goes here
    }

    void whenCharacteristicDiscovered(const DiscoveredCharacteristic* params) { 
        // implementation goes here
    }

    void whenDiscoveryTerminate(Gap::Handle_t connectionHandle) { 
        // implementation goes here
    }

private:
    /* Disallow copy and assignment. */
    MyGattClientDelegate(const MyGattClientDelegate &);
    MyGattClientDelegate& operator=(const MyGattClientDelegate &);

    GattClient& client;
};