I have my BLE app working fine for the peripheral device.
I am now attempting to create a BLE app to act as the central client device, which will enable the NOTIFY CCCD etc.
I have based my app on the MBED LED Blinker example and now stuck in the middle of the code conversion.
The problem is that I am having a hard time getting my “discoverDesciptors” function to work. It returns an error saying BLE_ERROR_INVALID_STATE.
/* mbed Microcontroller Library
* Copyright (c) 2006-2015 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <events/mbed_events.h>
#include <mbed.h>
#include "ble/BLE.h"
#include "ble/DiscoveredCharacteristic.h"
#include "ble/DiscoveredService.h"
#include "ble/Gap.h"
#include "ble/gap/AdvertisingDataParser.h"
#include "pretty_printer.h"
const static char PEER_NAME[] = "ButtonLED";
const static uint16_t PEER_UUID = 0x1815;
const static uint16_t PEER_CHAR_UUID = 0x2A56;
static EventQueue event_queue(/* event count */ 10 * EVENTS_EVENT_SIZE);
static DiscoveredCharacteristic led_characteristic[3];
static uint8_t cIndex = 0;
static bool trigger_led_characteristic = false;
typedef CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t DiscoveryCallbackParams_t;
typedef CharacteristicDescriptorDiscovery::TerminationCallbackParams_t TerminationCallbackParams_t;
void update_led_characteristic(void) {
if (!BLE::Instance().gattClient().isServiceDiscoveryActive()) {
//printf("Button state is: %u\r\n", led_characteristic[0].read());
//printf("Button colour is: %u\r\n", led_characteristic[1].read());
}
}
// This gets triggered once connected
void service_discovery(const DiscoveredService *service) {
if (service->getUUID().shortOrLong() == UUID::UUID_TYPE_SHORT) {
printf("Service UUID-%x attrs[%u - %u]\r\n", service->getUUID().getShortUUID(), service->getStartHandle(), service->getEndHandle());
} else {
printf("S UUID-");
const uint8_t *longUUIDBytes = service->getUUID().getBaseUUID();
for (unsigned i = 0; i < UUID::LENGTH_OF_LONG_UUID; i++) {
printf("%02x", longUUIDBytes[i]);
}
printf(" attrs[%u %u]\r\n", service->getStartHandle(), service->getEndHandle());
}
}
/**
* Handle the discovery of the characteristic descriptors.
*
* If the descriptor found is a CCCD then stop the discovery. Once the
* process has ended subscribe to server initiated events by writing the
* value of the CCCD.
*/
void when_descriptor_discovered(const DiscoveryCallbackParams_t* event)
{
printf(" - Descriptor discovered at %u", event->descriptor.getAttributeHandle());
if (event->descriptor.getUUID() == BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG) {
BLE::Instance().gattClient().terminateCharacteristicDescriptorDiscovery(event->characteristic);
printf(" - CCCD found\r\n");
}
}
/**
* If a CCCD has been found subscribe to server initiated events by writing
* its value.
*/
void when_descriptor_discovery_ends(const TerminationCallbackParams_t *event) {
printf(" - when_descriptor_discovery_ends\r\n");
}
// This gets triggered once connected
void characteristic_discovery(const DiscoveredCharacteristic *characteristicP) {
printf(" Char UUID-%x valueHandle[%u] declHandle[%u] connHandle[%u]\r\n",
characteristicP->getUUID().getShortUUID(),
characteristicP->getValueHandle(),
characteristicP->getDeclHandle(),
characteristicP->getConnectionHandle());
if (characteristicP->getProperties().broadcast()) printf(" Char has Broadcast property\r\n");
if (characteristicP->getProperties().indicate()) printf(" Char has Indicate property\r\n");
if (characteristicP->getProperties().notify()) {
printf(" Char has Notify property\r\n");
printf(" - Initiating descriptor discovery of %u.\r\n", characteristicP->getValueHandle());
ble_error_t error = characteristicP->discoverDescriptors(&when_descriptor_discovered, &when_descriptor_discovery_ends);
if (error) print_error(error, "Error caused by discoverDescriptors");
}
if (characteristicP->getProperties().read()) printf(" Char has Read property\r\n");
if (characteristicP->getProperties().write()) printf(" Char has Write property\r\n");
if (characteristicP->getProperties().writeWoResp()) printf(" Char has Write NoResp property\r\n");
if (cIndex == sizeof(led_characteristic)/sizeof(led_characteristic[0])-1) trigger_led_characteristic = true;
if (cIndex < sizeof(led_characteristic)/sizeof(led_characteristic[0])) cIndex++;
}
void discovery_termination(ble::connection_handle_t connectionHandle) {
printf("Terminated Discovery for handle %u\r\n", connectionHandle);
// Here we need to register for notification events.
if (trigger_led_characteristic) {
trigger_led_characteristic = false;
event_queue.call(update_led_characteristic);
}
}
void when_DataRead(const GattReadCallbackParams *response) {
if (response->handle == led_characteristic[0].getValueHandle()) {
printf("handle %u, offset %u, len %u\r\n", response->handle, response->offset, response->len);
for (unsigned index = 0; index < response->len; index++) {
printf("%c[%02x]", response->data[index], response->data[index]);
}
printf("\r\n");
// --------------------uint8_t toggledValue = response->data[0] ^ 0x1;
// --------------------led_characteristic.write(1, &toggledValue);
}
else if (response->handle == led_characteristic[1].getValueHandle()) {
printf("handle %u, offset %u, len %u\r\n", response->handle, response->offset, response->len);
for (unsigned index = 0; index < response->len; index++) {
printf("%c[%02x]", response->data[index], response->data[index]);
}
printf("\r\n");
// --------------------uint8_t toggledValue = response->data[0] ^ 0x1;
// --------------------led_characteristic.write(1, &toggledValue);
}
}
void when_written(const GattWriteCallbackParams *response) {
printf("Something was written using handle %u\r\n",response->handle);
/*
if (response->handle == led_characteristic[0].getValueHandle()) {
printf("Button state is: %u\r\n", led_characteristic[0].read());
}
else if (response->handle == led_characteristic[1].getValueHandle()) {
printf("Button colour is: %u\r\n", led_characteristic[1].read());
}
*/
}
void trigger_hvx(const GattHVXCallbackParams *response) {
if (response->handle == led_characteristic[0].getConnectionHandle()) {
printf("Button state is: %u\r\n", led_characteristic[0].read());
}
else if (response->handle == led_characteristic[1].getConnectionHandle()) {
printf("Button colour is: %u\r\n", led_characteristic[1].read());
}
}
class LEDBlinkerDemo : ble::Gap::EventHandler {
public:
LEDBlinkerDemo(BLE &ble, events::EventQueue &event_queue) :
_ble(ble),
_event_queue(event_queue),
_red_led(LED1, 1),
_green_led(LED2, 1),
_blue_led(LED3, 1),
_is_connecting(false),
_is_connected(false) {
}
~LEDBlinkerDemo() { }
void start() {
_ble.gap().setEventHandler(this);
_ble.init(this, &LEDBlinkerDemo::on_init_complete);
_event_queue.call_every(500ms, this, &LEDBlinkerDemo::blink);
_event_queue.dispatch_forever();
}
private:
/** Callback triggered when the ble initialization process has finished */
void on_init_complete(BLE::InitializationCompleteCallbackContext *params) {
if (params->error != BLE_ERROR_NONE) {
printf("Ble initialization failed.");
return;
}
_ble.gattClient().onDataRead(when_DataRead);
_ble.gattClient().onDataWritten(when_written);
_ble.gattClient().onHVX(trigger_hvx);
print_mac_address();
ble::ScanParameters scan_params;
_ble.gap().setScanParameters(scan_params);
_ble.gap().startScan();
}
void blink() {
if (!_is_connected) _red_led = !_red_led;
}
/* Event handler */
void onDisconnectionComplete(const ble::DisconnectionCompleteEvent&) {
printf("Disconnected. Start scanning...\r\n");
_ble.gap().startScan();
_is_connecting = false;
_is_connected = false;
}
void onConnectionComplete(const ble::ConnectionCompleteEvent& event) {
if (event.getOwnRole() == ble::connection_role_t::CENTRAL) {
_ble.gattClient().onServiceDiscoveryTermination(discovery_termination);
_ble.gattClient().launchServiceDiscovery(
event.getConnectionHandle(),
service_discovery,
characteristic_discovery,
PEER_UUID,
PEER_CHAR_UUID
);
printf("Now Connected...\r\n");
_is_connected = true;
if (cIndex == sizeof(led_characteristic)/sizeof(led_characteristic[0])) {
printf("Enabling Notify property\r\n");
//_ble.gattClient().discoverCharacteristicDescriptors();
}
} else {
_ble.gap().startScan();
}
_is_connecting = false;
}
void onAdvertisingReport(const ble::AdvertisingReportEvent &event) {
/* don't bother with analysing scan result if we're already connecting */
if (_is_connecting) {
return;
}
ble::AdvertisingDataParser adv_data(event.getPayload());
/* parse the advertising payload, looking for a discoverable device */
while (adv_data.hasNext()) {
ble::AdvertisingDataParser::element_t field = adv_data.next();
if (field.type == ble::adv_data_type_t::INCOMPLETE_LIST_16BIT_SERVICE_IDS) {
printf("INCOMPLETE_16BIT_SERVIDS FOUND...\r\n");
}
else if (field.type == ble::adv_data_type_t::COMPLETE_LIST_16BIT_SERVICE_IDS) {
printf("COMPLETE_16BIT_SERVIDS FOUND...\r\n");
if ((field.value.size() > 1) && (field.value.size() < 256)) {
for (uint8_t xx = 0; xx < field.value.size(); xx +=2) {
printf(" - Service UUID: 0x%02X%02X ", field.value[xx+1], field.value[xx]);
}
printf("\r\n");
}
}
else if (field.type == ble::adv_data_type_t::INCOMPLETE_LIST_128BIT_SERVICE_IDS) {
printf("INCOMPLETE_128BIT_SERVIDS FOUND...\r\n");
}
else if (field.type == ble::adv_data_type_t::COMPLETE_LIST_128BIT_SERVICE_IDS) {
printf("COMPLETE_128BIT_SERVIDS FOUND...\r\n");
}
/* connect to a discoverable device */
if (field.type == ble::adv_data_type_t::COMPLETE_LOCAL_NAME &&
field.value.size() == strlen(PEER_NAME) &&
(memcmp(field.value.data(), PEER_NAME, field.value.size()) == 0)) {
printf("This MAC Adr matches local name: ");
print_address(event.getPeerAddress().data());
printf(" rssi: %d, scan response: %u, connectable: %u\r\n",
event.getRssi(), event.getType().scan_response(), event.getType().connectable());
ble_error_t error = _ble.gap().stopScan();
if (error) {
print_error(error, "Error caused by Gap::stopScan");
return;
}
const ble::ConnectionParameters connection_params;
error = _ble.gap().connect(
event.getPeerAddressType(),
event.getPeerAddress(),
connection_params
);
if (error) {
_ble.gap().startScan();
return;
}
/* we may have already scan events waiting
* to be processed so we need to remember
* that we are already connecting and ignore them */
_is_connecting = true;
// Set up the LED's
_red_led = 0;
_green_led = 1;
_blue_led = 1;
return;
}
}
}
private:
BLE &_ble;
events::EventQueue &_event_queue;
DigitalOut _red_led;
DigitalOut _green_led;
DigitalOut _blue_led;
bool _is_connecting;
bool _is_connected;
};
/** Schedule processing of events from the BLE middleware in the event queue. */
void schedule_ble_events(BLE::OnEventsToProcessCallbackContext *context) {
event_queue.call(Callback<void()>(&context->ble, &BLE::processEvents));
}
int main()
{
BLE &ble = BLE::Instance();
ble.onEventsToProcess(schedule_ble_events);
LEDBlinkerDemo demo(ble, event_queue);
demo.start();
return 0;
}