This example show you how to use Feather nRF52/nRF52840 as a Central to talk to other Bluefruit (nRF52 or nRF51) peripherals exposing the bleuart (AKA 'NUS') service.
Client Services
Since the Central role accesses the GATT server on the peripheral, we first need to declare a client bleuart instance using the BLEClientUart helper class. We can also conveniently read Device Information if BLEClientDis is also used.
BLEClientDis clientDis; BLEClientUart clientUart;
Before we can configure client services, Bluefruit.begin()
must be called with at least 1 for the number of concurrent connections supported in central mode. Since we won't be running the nRF52 as a peripheral in this instance, we will set the peripheral count to 0:
// Initialize Bluefruit with maximum connections as Peripheral = 0, Central = 1 Bluefruit.begin(0, 1);
Afterward this, the client service(s) must be initialized by calling their begin()
function, and you can setup any callbacks that you wish to use from the helper class:
// Configure DIS client clientDis.begin(); // Init BLE Central Uart Serivce clientUart.begin(); clientUart.setRxCallback(bleuart_rx_callback);
Scanner
Let's start the advertising scanner to find a peripheral.
We'll hook up the scan result callback with setRxCallback().
Whenever advertising data is found by the scanner, it will be passed to this callback handler, and we can examine the advertising data there, and only connect to peripheral(s) that advertise the bleuart service.
Note: If the peripheral has multiple services and bleuart is not included in the UUID list in the advertising packet, you could optionally use another check such as matching the MAC address, name checking, using "another service", etc.
Once we find a peripheral that we wish to communicate with, call Bluefruit.Central.connect()
to establish connection with it:
void setup() { // Other set up ..... /* Start Central Scanning * - Enable auto scan if disconnected * - Interval = 100 ms, window = 80 ms * - Don't use active scan * - Start(timeout) with timeout = 0 will scan forever (until connected) */ Bluefruit.Scanner.setRxCallback(scan_callback); Bluefruit.Scanner.restartOnDisconnect(true); Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms Bluefruit.Scanner.useActiveScan(false); Bluefruit.Scanner.start(0); // // 0 = Don't stop scanning after n seconds } /** * Callback invoked when scanner pick up an advertising data * @param report Structural advertising data */ void scan_callback(ble_gap_evt_adv_report_t* report) { // Check if advertising contain BleUart service if ( Bluefruit.Scanner.checkReportForService(report, clientUart) ) { Serial.print("BLE UART service detected. Connecting ... "); // Connect to device with bleuart service in advertising Bluefruit.Central.connect(report); } }
Central Role
You normally need to setup the Central mode device's connect callback, which fires when a connection is established/disconnected with a peripheral device. Alternatively you could poll the connection status with connected(), but callbacks help to simplify the code significantly:
// Callbacks for Central Bluefruit.Central.setConnectCallback(connect_callback); Bluefruit.Central.setDisconnectCallback(disconnect_callback);
In the connect callback, we will try to discover the bleuart service by browsing the GATT table of the peripheral. This will help to determine the handle values for characteristics (e.g TXD, RXD, etc.). This is all done by BLEClientUart's .discover()
. Once the service is found, enable the TXD characteristic's CCCD to allow the peripheral to send data, and we are ready to send data back and forth between the devices:
void connect_callback(uint16_t conn_handle) { Serial.println("Connected"); Serial.print("Dicovering DIS ... "); if ( clientDis.discover(conn_handle) ) { Serial.println("Found it"); char buffer[32+1]; // read and print out Manufacturer memset(buffer, 0, sizeof(buffer)); if ( clientDis.getManufacturer(buffer, sizeof(buffer)) ) { Serial.print("Manufacturer: "); Serial.println(buffer); } // read and print out Model Number memset(buffer, 0, sizeof(buffer)); if ( clientDis.getModel(buffer, sizeof(buffer)) ) { Serial.print("Model: "); Serial.println(buffer); } Serial.println(); } Serial.print("Discovering BLE Uart Service ... "); if ( clientUart.discover(conn_handle) ) { Serial.println("Found it"); Serial.println("Enable TXD's notify"); clientUart.enableTXD(); Serial.println("Ready to receive from peripheral"); }else { Serial.println("Found NONE"); // disconect since we couldn't find bleuart service Bluefruit.Central.disconnect(conn_handle); } }
/********************************************************************* This is an example for our nRF52 based Bluefruit LE modules Pick one up today in the adafruit shop! Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! MIT license, check LICENSE for more information All text above, and the splash screen below must be included in any redistribution *********************************************************************/ /* * This sketch demonstrate the central API(). A additional bluefruit * that has bleuart as peripheral is required for the demo. */ #include <bluefruit.h> BLEClientBas clientBas; // battery client BLEClientDis clientDis; // device information client BLEClientUart clientUart; // bleuart client void setup() { Serial.begin(115200); // while ( !Serial ) delay(10); // for nrf52840 with native usb Serial.println("Bluefruit52 Central BLEUART Example"); Serial.println("-----------------------------------\n"); // Initialize Bluefruit with maximum connections as Peripheral = 0, Central = 1 // SRAM usage required by SoftDevice will increase dramatically with number of connections Bluefruit.begin(0, 1); Bluefruit.setName("Bluefruit52 Central"); // Configure Battery client clientBas.begin(); // Configure DIS client clientDis.begin(); // Init BLE Central Uart Serivce clientUart.begin(); clientUart.setRxCallback(bleuart_rx_callback); // Increase Blink rate to different from PrPh advertising mode Bluefruit.setConnLedInterval(250); // Callbacks for Central Bluefruit.Central.setConnectCallback(connect_callback); Bluefruit.Central.setDisconnectCallback(disconnect_callback); /* Start Central Scanning * - Enable auto scan if disconnected * - Interval = 100 ms, window = 80 ms * - Don't use active scan * - Start(timeout) with timeout = 0 will scan forever (until connected) */ Bluefruit.Scanner.setRxCallback(scan_callback); Bluefruit.Scanner.restartOnDisconnect(true); Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms Bluefruit.Scanner.useActiveScan(false); Bluefruit.Scanner.start(0); // // 0 = Don't stop scanning after n seconds } /** * Callback invoked when scanner pick up an advertising data * @param report Structural advertising data */ void scan_callback(ble_gap_evt_adv_report_t* report) { // Check if advertising contain BleUart service if ( Bluefruit.Scanner.checkReportForService(report, clientUart) ) { Serial.print("BLE UART service detected. Connecting ... "); // Connect to device with bleuart service in advertising Bluefruit.Central.connect(report); }else { // For Softdevice v6: after received a report, scanner will be paused // We need to call Scanner resume() to continue scanning Bluefruit.Scanner.resume(); } } /** * Callback invoked when an connection is established * @param conn_handle */ void connect_callback(uint16_t conn_handle) { Serial.println("Connected"); Serial.print("Dicovering Device Information ... "); if ( clientDis.discover(conn_handle) ) { Serial.println("Found it"); char buffer[32+1]; // read and print out Manufacturer memset(buffer, 0, sizeof(buffer)); if ( clientDis.getManufacturer(buffer, sizeof(buffer)) ) { Serial.print("Manufacturer: "); Serial.println(buffer); } // read and print out Model Number memset(buffer, 0, sizeof(buffer)); if ( clientDis.getModel(buffer, sizeof(buffer)) ) { Serial.print("Model: "); Serial.println(buffer); } Serial.println(); }else { Serial.println("Found NONE"); } Serial.print("Dicovering Battery ... "); if ( clientBas.discover(conn_handle) ) { Serial.println("Found it"); Serial.print("Battery level: "); Serial.print(clientBas.read()); Serial.println("%"); }else { Serial.println("Found NONE"); } Serial.print("Discovering BLE Uart Service ... "); if ( clientUart.discover(conn_handle) ) { Serial.println("Found it"); Serial.println("Enable TXD's notify"); clientUart.enableTXD(); Serial.println("Ready to receive from peripheral"); }else { Serial.println("Found NONE"); // disconnect since we couldn't find bleuart service Bluefruit.disconnect(conn_handle); } } /** * Callback invoked when a connection is dropped * @param conn_handle * @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h */ void disconnect_callback(uint16_t conn_handle, uint8_t reason) { (void) conn_handle; (void) reason; Serial.print("Disconnected, reason = 0x"); Serial.println(reason, HEX); } /** * Callback invoked when uart received data * @param uart_svc Reference object to the service where the data * arrived. In this example it is clientUart */ void bleuart_rx_callback(BLEClientUart& uart_svc) { Serial.print("[RX]: "); while ( uart_svc.available() ) { Serial.print( (char) uart_svc.read() ); } Serial.println(); } void loop() { if ( Bluefruit.Central.connected() ) { // Not discovered yet if ( clientUart.discovered() ) { // Discovered means in working state // Get Serial input and send to Peripheral if ( Serial.available() ) { delay(2); // delay a bit for all characters to arrive char str[20+1] = { 0 }; Serial.readBytes(str, 20); clientUart.print( str ); } } } }
And bluefruit.h
/* * The MIT License (MIT) * * Copyright (c) 2019, hathach (tinyusb.org) for Adafruit * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifndef BLUEFRUIT_H_ #define BLUEFRUIT_H_ #include <Arduino.h> #include "bluefruit_common.h" #define CFG_ADV_BLINKY_INTERVAL 500 /* Note changing these parameters will affect APP_RAM_BASE * --> need to update RAM region in linker file * - BLE_GATT_ATT_MTU_MAX from 23 (default) to 247 */ #define BLE_GATT_ATT_MTU_MAX 247 #define BLE_MAX_CONNECTION 20 // SD support up to 20 connections // Allocate more memory for GATT table for 840 #if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) #define CFG_SD_ATTR_TABLE_SIZE 0x1000 #else #define CFG_SD_ATTR_TABLE_SIZE 0xC00 #endif #include "BLEUuid.h" #include "BLEAdvertising.h" #include "BLECharacteristic.h" #include "BLEService.h" #include "BLEScanner.h" #include "BLEPeriph.h" #include "BLECentral.h" #include "BLEClientCharacteristic.h" #include "BLEClientService.h" #include "BLEDiscovery.h" #include "BLEConnection.h" #include "BLEGatt.h" #include "BLESecurity.h" // Services #include "services/BLEDis.h" #include "services/BLEDfu.h" #include "services/BLEUart.h" #include "services/BLEBas.h" #include "services/BLEIas.h" #include "services/BLEBeacon.h" #include "services/BLEHidGeneric.h" #include "services/BLEHidAdafruit.h" #include "services/BLEHidGamepad.h" #include "services/BLEMidi.h" #include "services/EddyStone.h" #include "clients/BLEAncs.h" #include "clients/BLEClientUart.h" #include "clients/BLEClientDis.h" #include "clients/BLEClientCts.h" #include "clients/BLEClientHidAdafruit.h" #include "clients/BLEClientBas.h" #include "clients/BLEClientIas.h" #include "utility/AdaCallback.h" #include "utility/bonding.h" enum { BANDWIDTH_AUTO = 0, BANDWIDTH_LOW, BANDWIDTH_NORMAL, BANDWIDTH_HIGH, BANDWIDTH_MAX, }; enum { CONN_CFG_PERIPHERAL = 1, CONN_CFG_CENTRAL = 2, }; extern "C" { void SD_EVT_IRQHandler(void); } class AdafruitBluefruit { public: typedef void (*event_cb_t) (ble_evt_t* evt); typedef void (*rssi_cb_t) (uint16_t conn_hdl, int8_t rssi); AdafruitBluefruit(void); /*------------------------------------------------------------------*/ /* Lower Level Classes (Bluefruit.Advertising.*, etc.) *------------------------------------------------------------------*/ BLEPeriph Periph; BLECentral Central; BLESecurity Security; BLEGatt Gatt; BLEAdvertising Advertising; BLEAdvertisingData ScanResponse; BLEScanner Scanner; BLEDiscovery Discovery; /*------------------------------------------------------------------*/ /* SoftDevice Configure Functions, must call before begin(). * These function affect the SRAM consumed by SoftDevice. *------------------------------------------------------------------*/ void configServiceChanged (bool changed); void configUuid128Count (uint8_t uuid128_max); void configAttrTableSize (uint32_t attr_table_size); // Configure Bandwidth for connections void configPrphConn (uint16_t mtu_max, uint16_t event_len, uint8_t hvn_qsize, uint8_t wrcmd_qsize); void configCentralConn (uint16_t mtu_max, uint16_t event_len, uint8_t hvn_qsize, uint8_t wrcmd_qsize); void configPrphBandwidth (uint8_t bw); void configCentralBandwidth(uint8_t bw); bool begin(uint8_t prph_count = 1, uint8_t central_count = 0); /*------------------------------------------------------------------*/ /* General Functions *------------------------------------------------------------------*/ ble_gap_addr_t getAddr(void); uint8_t getAddr(uint8_t mac[6]); bool setAddr(ble_gap_addr_t* gap_addr); void setName (const char* str); uint8_t getName (char* name, uint16_t bufsize); // Supported tx_power values depending on mcu: // - nRF52832: -40dBm, -20dBm, -16dBm, -12dBm, -8dBm, -4dBm, 0dBm, +3dBm and +4dBm. // - nRF52840: -40dBm, -20dBm, -16dBm, -12dBm, -8dBm, -4dBm, 0dBm, +2dBm, +3dBm, +4dBm, +5dBm, +6dBm, +7dBm and +8dBm. bool setTxPower (int8_t power); int8_t getTxPower (void); bool setAppearance (uint16_t appear); uint16_t getAppearance (void); void autoConnLed (bool enabled); void setConnLedInterval (uint32_t ms); /*------------------------------------------------------------------*/ /* GAP, Connections and Bonding *------------------------------------------------------------------*/ uint8_t connected (void); // Number of connected bool connected (uint16_t conn_hdl); uint8_t getConnectedHandles(uint16_t* hdl_list, uint8_t max_count); uint16_t connHandle (void); // Alias to BLEConnection API() bool disconnect (uint16_t conn_hdl); uint16_t getMaxMtu(uint8_t role); BLEConnection* Connection(uint16_t conn_hdl); #ifdef ANT_LICENSE_KEY /*------------------------------------------------------------------* * Optional semaphore for additional event handlers for SD event. * It can be used for handling non-BLE SD events *------------------------------------------------------------------*/ void setMultiprotocolSemaphore(SemaphoreHandle_t mprot_event_semaphore) { _mprot_event_sem= mprot_event_semaphore; } #endif /*------------------------------------------------------------------*/ /* Callbacks *------------------------------------------------------------------*/ void setRssiCallback(rssi_cb_t fp); void setEventCallback(event_cb_t fp); /*------------------------------------------------------------------*/ /* INTERNAL USAGE ONLY * Although declare as public, it is meant to be invoked by internal * code. User should not call these directly *------------------------------------------------------------------*/ void _startConnLed (void); void _stopConnLed (void); void _setConnLed (bool on_off); void printInfo(void); private: /*------------- SoftDevice Configuration -------------*/ struct { uint32_t attr_table_size; uint8_t service_changed; uint8_t uuid128_max; // Bandwidth configuration struct { uint16_t mtu_max; uint16_t event_len; uint8_t hvn_qsize; uint8_t wrcmd_qsize; }prph, central; }_sd_cfg; uint8_t _prph_count; uint8_t _central_count; int8_t _tx_power; ble_gap_sec_params_t _sec_param; SemaphoreHandle_t _ble_event_sem; SemaphoreHandle_t _soc_event_sem; #ifdef ANT_LICENSE_KEY /* Optional semaphore for additional event handlers for SD event. * It can be used for handling non-BLE SD events */ SemaphoreHandle_t _mprot_event_sem; #endif // Auto LED Blinky TimerHandle_t _led_blink_th; bool _led_conn; uint16_t _conn_hdl; BLEConnection* _connection[BLE_MAX_CONNECTION]; //------------- Callbacks -------------// rssi_cb_t _rssi_cb; event_cb_t _event_cb; /*------------------------------------------------------------------*/ /* INTERNAL USAGE ONLY *------------------------------------------------------------------*/ void _ble_handler(ble_evt_t* evt); friend void SD_EVT_IRQHandler(void); friend void adafruit_ble_task(void* arg); friend void adafruit_soc_task(void* arg); friend class BLECentral; }; extern AdafruitBluefruit Bluefruit; #endif
Text editor powered by tinymce.