diff options
Diffstat (limited to 'arduino/libraries/Bluefruit52Lib/src')
60 files changed, 12565 insertions, 0 deletions
diff --git a/arduino/libraries/Bluefruit52Lib/src/BLEAdvertising.cpp b/arduino/libraries/Bluefruit52Lib/src/BLEAdvertising.cpp new file mode 100755 index 0000000..8312395 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/BLEAdvertising.cpp @@ -0,0 +1,467 @@ +/**************************************************************************/ +/*! + @file BLEAdvertising.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" + +/*------------------------------------------------------------------*/ +/* BLEAdvertisingData shared between ADV and ScanResponse + *------------------------------------------------------------------*/ +BLEAdvertisingData::BLEAdvertisingData(void) +{ + _count = 0; + varclr(_data); +} + +bool BLEAdvertisingData::addData(uint8_t type, const void* data, uint8_t len) +{ + VERIFY( _count + len + 2 <= BLE_GAP_ADV_SET_DATA_SIZE_MAX ); + + uint8_t* adv_data = &_data[_count]; + + // len (1+data), type, data + *adv_data++ = (len+1); + *adv_data++ = type; + memcpy(adv_data, data, len); + + _count = _count + len + 2; + + return true; +} + +/*------------------------------------------------------------------*/ +/* Adding UUID + *------------------------------------------------------------------*/ +bool BLEAdvertisingData::addUuid(BLEUuid bleuuid) +{ + return addUuid(&bleuuid, 1); +} + +bool BLEAdvertisingData::addUuid(BLEUuid bleuuid1, BLEUuid bleuuid2) +{ + BLEUuid bleuuid[] = { bleuuid1, bleuuid2 }; + return addUuid(bleuuid, 2); +} + +bool BLEAdvertisingData::addUuid(BLEUuid bleuuid1, BLEUuid bleuuid2, BLEUuid bleuuid3) +{ + BLEUuid bleuuid[] = { bleuuid1, bleuuid2, bleuuid3}; + return addUuid(bleuuid, 3); +} + +bool BLEAdvertisingData::addUuid(BLEUuid bleuuid1, BLEUuid bleuuid2, BLEUuid bleuuid3, BLEUuid bleuuid4) +{ + BLEUuid bleuuid[] = { bleuuid1, bleuuid2, bleuuid3, bleuuid4 }; + return addUuid(bleuuid, 4); +} + +bool BLEAdvertisingData::addUuid(BLEUuid bleuuid[], uint8_t count) +{ + uint16_t uuid16_list[15]; + uint8_t uuid16_count = 0; + + uint8_t const* uuid128 = NULL; + + for(uint8_t i=0; i<count; i++) + { + switch ( bleuuid[i].size() ) + { + case 16: + uuid16_list[uuid16_count++] = bleuuid[i]._uuid.uuid; + break; + + case 128: + // cannot fit more than one uuid128 + if ( uuid128 ) return false; + uuid128 = bleuuid[i]._uuid128; + break; + + default: break; + } + } + + if (uuid16_count) + { + VERIFY( addData(BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE, uuid16_list, 2*uuid16_count) ); + } + + if (uuid128) + { + VERIFY( addData(BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE, uuid128, 16) ); + } + + return true; +} + +/*------------------------------------------------------------------*/ +/* Adding Service's UUID + *------------------------------------------------------------------*/ +bool BLEAdvertisingData::addService(BLEService& service) +{ + return addUuid(service.uuid); +} + +bool BLEAdvertisingData::addService(BLEService& service1, BLEService& service2) +{ + return addUuid(service1.uuid, service2.uuid); +} + +bool BLEAdvertisingData::addService(BLEService& service1, BLEService& service2, BLEService& service3) +{ + return addUuid(service1.uuid, service2.uuid, service3.uuid); +} + +bool BLEAdvertisingData::addService(BLEService& service1, BLEService& service2, BLEService& service3, BLEService& service4) +{ + return addUuid(service1.uuid, service2.uuid, service3.uuid, service4.uuid); +} + +bool BLEAdvertisingData::addService(BLEClientService& service) +{ + // Central service is added to Solicitation UUID + switch ( service.uuid.size() ) + { + case 16: + return addData(BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_16BIT, &service.uuid._uuid.uuid, 2); + break; + + case 128: + return addData(BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_128BIT, service.uuid._uuid128, 16); + break; + + default: break; + } + + return false; +} + +/*------------------------------------------------------------------*/ +/* Adding Others + *------------------------------------------------------------------*/ + +/** + * Add Name to Adv packet, use setName() to set + * @return true if full name is added, false if shorten name or not enough data to add name + */ +bool BLEAdvertisingData::addName(void) +{ + char name[BLE_GAP_ADV_SET_DATA_SIZE_MAX+1]; + + uint8_t type = BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME; + uint8_t len = Bluefruit.getName(name, sizeof(name)); + + // not enough for full name, chop it + if (_count + len + 2 > BLE_GAP_ADV_SET_DATA_SIZE_MAX) + { + type = BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME; + len = BLE_GAP_ADV_SET_DATA_SIZE_MAX - (_count+2); + } + + VERIFY( addData(type, name, len) ); + + return type == BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME; +} + +// tx power is set by setTxPower +bool BLEAdvertisingData::addTxPower(void) +{ + int8_t tx_power = Bluefruit.getTxPower(); + return addData(BLE_GAP_AD_TYPE_TX_POWER_LEVEL, &tx_power, 1); +} + +bool BLEAdvertisingData::addFlags(uint8_t flags) +{ + return addData(BLE_GAP_AD_TYPE_FLAGS, &flags, 1); +} + +bool BLEAdvertisingData::addAppearance(uint16_t appearance) +{ + return addData(BLE_GAP_AD_TYPE_APPEARANCE, &appearance, 2); +} + +bool BLEAdvertisingData::addManufacturerData(const void* data, uint8_t count) +{ + return addData(BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA, data, count); +} + +/*------------------------------------------------------------------*/ +/* CUSTOM API + *------------------------------------------------------------------*/ +uint8_t BLEAdvertisingData::count(void) +{ + return _count; +} + +uint8_t* BLEAdvertisingData::getData(void) +{ + return _data; +} + +bool BLEAdvertisingData::setData(uint8_t const * data, uint8_t count) +{ + VERIFY( data && (count <= BLE_GAP_ADV_SET_DATA_SIZE_MAX) ); + + memcpy(_data, data, count); + _count = count; + + return true; +} + +void BLEAdvertisingData::clearData(void) +{ + _count = 0; + varclr(_data); +} + +/*------------------------------------------------------------------*/ +/* BLEAdvertising only + *------------------------------------------------------------------*/ +BLEAdvertising::BLEAdvertising(void) +{ + _hdl = BLE_GAP_ADV_SET_HANDLE_NOT_SET; + _type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED; + _start_if_disconnect = true; + _runnning = false; + + _fast_interval = BLE_ADV_INTERVAL_FAST_DFLT; + _slow_interval = BLE_ADV_INTERVAL_SLOW_DFLT; + _active_interval = _fast_interval; + + _fast_timeout = BLE_ADV_FAST_TIMEOUT_DFLT; + _stop_timeout = _left_timeout = 0; + _stop_cb = NULL; + _slow_cb = NULL; +} + +void BLEAdvertising::setFastTimeout(uint16_t sec) +{ + _fast_timeout = sec; +} + +void BLEAdvertising::setType(uint8_t adv_type) +{ + _type = adv_type; +} + +/** + * Set Interval in unit of 0.625 ms + * @param fast Interval that is used in the first n seconds (configurable) + * @param slow Interval that is used after fast timeout + */ +void BLEAdvertising::setInterval(uint16_t fast, uint16_t slow) +{ + _fast_interval = fast; + _slow_interval = slow; + + // default is fast since it will be advertising first + _active_interval = _fast_interval; +} + +void BLEAdvertising::setIntervalMS(uint16_t fast, uint16_t slow) +{ + setInterval(MS1000TO625(fast), MS1000TO625(slow)); +} + +/** + * Get current active interval + * @return Either slow or fast interval in unit of 0.625 ms + */ +uint16_t BLEAdvertising::getInterval(void) +{ + return _active_interval; +} + +void BLEAdvertising::setSlowCallback(slow_callback_t fp) +{ + _slow_cb = fp; +} + +void BLEAdvertising::setStopCallback(stop_callback_t fp) +{ + _stop_cb = fp; +} + +bool BLEAdvertising::isRunning(void) +{ + return _runnning; +} + +bool BLEAdvertising::setBeacon(BLEBeacon& beacon) +{ + return beacon.start(*this); +} + +bool BLEAdvertising::setBeacon(EddyStoneUrl& eddy_url) +{ + return eddy_url.start(); +} + +void BLEAdvertising::restartOnDisconnect(bool enable) +{ + _start_if_disconnect = enable; +} + +bool BLEAdvertising::_start(uint16_t interval, uint16_t timeout) +{ + // ADV Params + ble_gap_adv_params_t adv_para = + { + .properties = { + .type = _type, + .anonymous = 0 + }, + .p_peer_addr = NULL , // Undirected advertisement + .interval = interval , // advertising interval (in units of 0.625 ms) + .duration = timeout*100 , // in 10-ms unit + + .max_adv_evts = 0, // TODO can be used for fast/slow mode + .channel_mask = { 0, 0, 0, 0, 0 } , // 40 channel, set 1 to disable + .filter_policy = BLE_GAP_ADV_FP_ANY , + + //.primary_phy, .secondary_phy, .set_id, .scan_req_notification + }; + + // gap_adv long-live is required by SD v6 + static ble_gap_adv_data_t gap_adv = + { + .adv_data = { .p_data = _data, .len = _count }, + .scan_rsp_data = { .p_data = Bluefruit.ScanResponse.getData(), .len = Bluefruit.ScanResponse.count() } + }; + VERIFY_STATUS( sd_ble_gap_adv_set_configure(&_hdl, &gap_adv, &adv_para), false ); + VERIFY_STATUS( sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_ADV, _hdl, Bluefruit.getTxPower() ), false ); + VERIFY_STATUS( sd_ble_gap_adv_start(_hdl, CONN_CFG_PERIPHERAL), false ); + + Bluefruit._startConnLed(); // start blinking + _runnning = true; + _active_interval = interval; + + _left_timeout -= min16(_left_timeout, timeout); + + return true; +} + +bool BLEAdvertising::start(uint16_t timeout) +{ + _stop_timeout = _left_timeout = timeout; + + // Initially advertising in fast mode + // Fast mode blink 2x than slow mode + Bluefruit.setConnLedInterval(CFG_ADV_BLINKY_INTERVAL/2); + VERIFY( _start(_fast_interval, _fast_timeout) ); + + return true; +} + +bool BLEAdvertising::stop(void) +{ + VERIFY_STATUS( sd_ble_gap_adv_stop(_hdl), false); + + _runnning = false; + Bluefruit._stopConnLed(); // stop blinking + + return true; +} + + +void BLEAdvertising::_eventHandler(ble_evt_t* evt) +{ + switch ( evt->header.evt_id ) + { + case BLE_GAP_EVT_CONNECTED: + { + ble_gap_evt_connected_t const * para = &evt->evt.gap_evt.params.connected; + + if ( para->role == BLE_GAP_ROLE_PERIPH) + { + _runnning = false; + + // Turn on Conn LED + Bluefruit._stopConnLed(); + Bluefruit._setConnLed(true); + } + } + break; + + case BLE_GAP_EVT_DISCONNECTED: + if ( BLE_GAP_ROLE_PERIPH == Bluefruit.Gap.getRole(evt->evt.common_evt.conn_handle) ) + { + // Turn off Conn LED + Bluefruit._setConnLed(false); + + // Auto start if enabled + if ( _start_if_disconnect ) start(_stop_timeout); + } + break; + + case BLE_GAP_EVT_ADV_SET_TERMINATED: + if (evt->evt.gap_evt.params.adv_set_terminated.reason == BLE_GAP_EVT_ADV_SET_TERMINATED_REASON_TIMEOUT) + { + _runnning = false; + + // If still advertising, it is only in slow mode --> blink normal + Bluefruit.setConnLedInterval(CFG_ADV_BLINKY_INTERVAL); + + if ( _stop_timeout == 0 ) + { + // Call slow callback if available + if (_slow_cb) _slow_cb(); + + // if stop_timeout is 0 --> no timeout + _start(_slow_interval, 0); + }else + { + // Advertising if there is still time left, otherwise stop it + if ( _left_timeout ) + { + // Call slow callback if available + if (_slow_cb) _slow_cb(); + + _start(_slow_interval, _left_timeout); + }else + { + // Stop advertising + Bluefruit._stopConnLed(); // stop blinking + + // invoke stop callback + if (_stop_cb) ada_callback(NULL, _stop_cb); + } + } + } + break; + + default: break; + } +} + diff --git a/arduino/libraries/Bluefruit52Lib/src/BLEAdvertising.h b/arduino/libraries/Bluefruit52Lib/src/BLEAdvertising.h new file mode 100755 index 0000000..f85c5e6 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/BLEAdvertising.h @@ -0,0 +1,172 @@ +/**************************************************************************/ +/*! + @file BLEAdvertising.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLEADVERTISING_H_ +#define BLEADVERTISING_H_ + +#include <Arduino.h> +#include "bluefruit_common.h" +#include "BLEClientService.h" + +#include "BLEUuid.h" +#include "BLEService.h" + +#include "services/BLEBeacon.h" +#include "services/EddyStone.h" + +/* Advertising Guideline from Apple + * https://developer.apple.com/library/content/qa/qa1931/_index.html + * + * The recommended advertising pattern and advertising intervals are: + * - First, advertise at 20 ms intervals for at least 30 seconds + * - If not discovered after 30 seconds, you may change to one of the following + * longer intervals: 152.5 ms, 211.25 ms, 318.75 ms, 417.5 ms, 546.25 ms, + * 760 ms, 852.5 ms, 1022.5 ms, 1285 ms + */ +#define BLE_ADV_INTERVAL_FAST_DFLT 32 // 20 ms (in 0.625 ms unit) +#define BLE_ADV_INTERVAL_SLOW_DFLT 244 // 152.5 ms (in 0.625 ms unit) +#define BLE_ADV_FAST_TIMEOUT_DFLT 30 // in seconds + +// forward declaration +class BLEAdvertisingData; + +// Abstract Class to set Adv Data +class Advertisable +{ + public: + virtual bool setAdv(BLEAdvertisingData& adv) = 0; +}; + +class BLEAdvertisingData +{ +protected: + uint8_t _data[BLE_GAP_ADV_SET_DATA_SIZE_MAX]; + uint8_t _count; + +public: + BLEAdvertisingData(void); + + /*------------- Adv Data -------------*/ + bool addData(uint8_t type, const void* data, uint8_t len); + bool addFlags(uint8_t flags); + bool addTxPower(void); + bool addName(void); + bool addAppearance(uint16_t appearance); + bool addManufacturerData(const void* data, uint8_t count); + + /*------------- UUID -------------*/ + bool addUuid(BLEUuid bleuuid); + bool addUuid(BLEUuid bleuuid1, BLEUuid bleuuid2); + bool addUuid(BLEUuid bleuuid1, BLEUuid bleuuid2, BLEUuid bleuuid3); + bool addUuid(BLEUuid bleuuid1, BLEUuid bleuuid2, BLEUuid bleuuid3, BLEUuid bleuuid4); + + bool addUuid(BLEUuid bleuuid[], uint8_t count); + + /*------------- Service -------------*/ + bool addService(BLEService& service); + bool addService(BLEService& service1, BLEService& service2); + bool addService(BLEService& service1, BLEService& service2, BLEService& service3); + bool addService(BLEService& service1, BLEService& service2, BLEService& service3, BLEService& service4); + + /*------------- Client Service -------------*/ + bool addService(BLEClientService& service); + + // Functions to work with the raw advertising packet + uint8_t count(void); + uint8_t* getData(void); + bool setData(const uint8_t* data, uint8_t count); + void clearData(void); + + bool setData(Advertisable& adv_able) { return adv_able.setAdv(*this); } +}; + +class BLEAdvertising : public BLEAdvertisingData +{ +public: + typedef void (*stop_callback_t) (void); + typedef void (*slow_callback_t) (void); + + BLEAdvertising(void); + + void setType(uint8_t adv_type); + void setFastTimeout(uint16_t sec); + + void setSlowCallback(slow_callback_t fp); + void setStopCallback(stop_callback_t fp); + + void setInterval (uint16_t fast, uint16_t slow); + void setIntervalMS(uint16_t fast, uint16_t slow); + + uint16_t getInterval(void); + + bool setBeacon(BLEBeacon& beacon); + bool setBeacon(EddyStoneUrl& eddy_url); + + bool isRunning(void); + + void restartOnDisconnect(bool enable); + bool start(uint16_t timeout = 0); + bool stop (void); + + /*------------------------------------------------------------------*/ + /* INTERNAL USAGE ONLY + * Although declare as public, it is meant to be invoked by internal + * code. User should not call these directly + *------------------------------------------------------------------*/ + void _eventHandler(ble_evt_t* evt); + +private: + uint8_t _hdl; + uint8_t _type; + bool _start_if_disconnect; + bool _runnning; + + uint16_t _fast_interval; // in 0.625 ms + uint16_t _slow_interval; // in 0.625 ms + uint16_t _active_interval; // in 0.625 ms + + uint16_t _fast_timeout; // in second + uint16_t _stop_timeout; // in second + uint16_t _left_timeout; + + stop_callback_t _stop_cb; + slow_callback_t _slow_cb; + + // Internal function + bool _start(uint16_t interval, uint16_t timeout); + +}; + +#endif /* BLEADVERTISING_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/BLECentral.cpp b/arduino/libraries/Bluefruit52Lib/src/BLECentral.cpp new file mode 100755 index 0000000..57bbe2a --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/BLECentral.cpp @@ -0,0 +1,185 @@ +/**************************************************************************/ +/*! + @file BLECentral.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" +#include "utility/bonding.h" + +/** + * Constructor + */ +BLECentral::BLECentral(void) +{ + _conn_param.min_conn_interval = _conn_param.max_conn_interval = BLE_GAP_CONN_MIN_INTERVAL_DFLT; + _conn_param.slave_latency = BLE_GAP_CONN_SLAVE_LATENCY; + _conn_param.conn_sup_timeout = BLE_GAP_CONN_SUPERVISION_TIMEOUT_MS/10; + + _connect_cb = NULL; + _disconnect_cb = NULL; +} + +void BLECentral::begin(void) +{ + // Central will very likely use Discovery + Bluefruit.Discovery.begin(); +} + +/*------------------------------------------------------------------*/ +/* + *------------------------------------------------------------------*/ +bool BLECentral::setConnInterval(uint16_t min, uint16_t max) +{ + _conn_param.min_conn_interval = min; + _conn_param.max_conn_interval = max; + + return true; +} + +bool BLECentral::setConnIntervalMS (uint16_t min_ms, uint16_t max_ms) +{ + return setConnInterval( MS100TO125(min_ms), MS100TO125(max_ms) ); +} + +bool BLECentral::connect(const ble_gap_addr_t* peer_addr) +{ + // Connect with default connection parameter + VERIFY_STATUS( sd_ble_gap_connect(peer_addr, Bluefruit.Scanner.getParams(), &_conn_param, CONN_CFG_CENTRAL), false ); + + return true; +} + +bool BLECentral::connect(const ble_gap_evt_adv_report_t* adv_report) +{ + return connect(&adv_report->peer_addr); +} + +bool BLECentral::disconnect(uint16_t conn_handle) +{ + return ERROR_NONE == sd_ble_gap_disconnect(conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); +} + +/** + * Check if connected to a specific peripheral + * @param conn_handle + * @return + */ +bool BLECentral::connected(uint16_t conn_handle) +{ + return Bluefruit.Gap.connected(conn_handle); +} + +/** + * Check if connected to ANY peripherals + * @param conn_handle + * @return + */ +bool BLECentral::connected(void) +{ + for (uint8_t conn=0; conn<BLE_MAX_CONN; conn++) + { + // skip Peripheral Role handle + if ( Bluefruit.Gap.connected(conn) && (Bluefruit.Gap.getRole(conn) == BLE_GAP_ROLE_CENTRAL) ) + { + return true; + } + } + + return false; +} + +void BLECentral::setConnectCallback( BLEGap::connect_callback_t fp) +{ + _connect_cb = fp; +} + +void BLECentral::setDisconnectCallback( BLEGap::disconnect_callback_t fp) +{ + _disconnect_cb = fp; +} + +void BLECentral::clearBonds(void) +{ + bond_clear_cntr(); +} + +/** + * Event is forwarded from Bluefruit Poll() method + * @param event + */ +void BLECentral::_event_handler(ble_evt_t* evt) +{ + // conn handle has fixed offset regardless of event type + const uint16_t conn_hdl = evt->evt.common_evt.conn_handle; + + /* PrPh handle connection is already filtered. Only handle Central events or + * connection handle is BLE_CONN_HANDLE_INVALID (e.g BLE_GAP_EVT_ADV_REPORT) */ + switch ( evt->header.evt_id ) + { + case BLE_GAP_EVT_CONNECTED: + if ( Bluefruit.Gap.getRole(conn_hdl) == BLE_GAP_ROLE_CENTRAL) + { + // Invoke callback + if ( _connect_cb) ada_callback(NULL, _connect_cb, conn_hdl); + } + break; + + case BLE_GAP_EVT_DISCONNECTED: + if ( Bluefruit.Gap.getRole(conn_hdl) == BLE_GAP_ROLE_CENTRAL) + { + // Invoke callback reason is BLE_HCI_STATUS code + if ( _disconnect_cb) ada_callback(NULL, _disconnect_cb, conn_hdl, evt->evt.gap_evt.params.disconnected.reason); + } + break; + + case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: + { + // Peripheral request to change connection parameter + ble_gap_conn_params_t* request_param = &evt->evt.gap_evt.params.conn_param_update_request.conn_params; + + LOG_LV2("GAP", "Conn Param Update Request: (min, max, latency, sup) = (%.2f, %.2f, %d, %d)", + request_param->min_conn_interval*1.25f, request_param->max_conn_interval*1.25f, request_param->slave_latency, request_param->conn_sup_timeout*10); + + // Central could perform checks to accept or reject request + // For now just accept parameter from prph + ble_gap_conn_params_t conn_param = *request_param; + conn_param.max_conn_interval = conn_param.min_conn_interval; + + sd_ble_gap_conn_param_update(conn_hdl, &conn_param); + } + break; + + default: break; + } +} diff --git a/arduino/libraries/Bluefruit52Lib/src/BLECentral.h b/arduino/libraries/Bluefruit52Lib/src/BLECentral.h new file mode 100755 index 0000000..79b7d63 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/BLECentral.h @@ -0,0 +1,93 @@ +/**************************************************************************/ +/*! + @file BLECentral.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLECENTRAL_H_ +#define BLECENTRAL_H_ + +#include <Arduino.h> +#include "bluefruit_common.h" + +#include "BLEGap.h" +#include "BLEGatt.h" + +#include "BLEUuid.h" +#include "BLECharacteristic.h" +#include "BLEClientCharacteristic.h" +#include "BLEService.h" + +#include "BLEClientService.h" + +class AdafruitBluefruit; + +class BLECentral +{ + public: + BLECentral(void); // Constructor + + void begin(void); + + /*------------------------------------------------------------------*/ + /* GAP + *------------------------------------------------------------------*/ + bool setConnInterval(uint16_t min, uint16_t max); + bool setConnIntervalMS (uint16_t min_ms, uint16_t max_ms); + + bool connect(const ble_gap_evt_adv_report_t* adv_report); + bool connect(const ble_gap_addr_t *peer_addr); + bool disconnect(uint16_t conn_handle); + + bool connected (uint16_t conn_handle); // If connected to a specific peripheral + bool connected (void); // If connected to any peripherals + + void clearBonds (void); + + /*------------- Callbacks -------------*/ + void setConnectCallback ( BLEGap::connect_callback_t fp); + void setDisconnectCallback( BLEGap::disconnect_callback_t fp); + + private: + ble_gap_conn_params_t _conn_param; + + BLEGap::connect_callback_t _connect_cb; + BLEGap::disconnect_callback_t _disconnect_cb; + + void _event_handler(ble_evt_t* evt); + + friend class AdafruitBluefruit; +}; + + + +#endif /* BLECENTRAL_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/BLECharacteristic.cpp b/arduino/libraries/Bluefruit52Lib/src/BLECharacteristic.cpp new file mode 100755 index 0000000..6474e39 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/BLECharacteristic.cpp @@ -0,0 +1,747 @@ +/**************************************************************************/ +/*! + @file BLECharacteristic.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" + +void BLECharacteristic::_init(void) +{ + _is_temp = false; + _max_len = BLE_GATT_ATT_MTU_DEFAULT-3; + _service = NULL; + + _usr_descriptor = NULL; + varclr(&_report_ref_desc); + varclr(&_format_desc); + + varclr(&_properties); + + varclr(&_attr_meta); + _attr_meta.read_perm = _attr_meta.write_perm = BLE_SECMODE_OPEN; + _attr_meta.vlen = 1; + _attr_meta.vloc = BLE_GATTS_VLOC_STACK; + + _handles.value_handle = BLE_GATT_HANDLE_INVALID; + _handles.user_desc_handle = BLE_GATT_HANDLE_INVALID; + _handles.sccd_handle = BLE_GATT_HANDLE_INVALID; + _handles.cccd_handle = BLE_GATT_HANDLE_INVALID; + + _long_wr.buffer = NULL; + _long_wr.bufsize = 0; + _long_wr.count = 0; + + _rd_authorize_cb = NULL; + _wr_authorize_cb = NULL; + _wr_cb = NULL; + _cccd_wr_cb = NULL; +} + +BLECharacteristic::BLECharacteristic(void) + : uuid() +{ + _init(); +} + +BLECharacteristic::BLECharacteristic(BLEUuid bleuuid) + : uuid(bleuuid) +{ + _init(); +} + +void BLECharacteristic::setUuid(BLEUuid bleuuid) +{ + uuid = bleuuid; +} + +BLEService& BLECharacteristic::parentService (void) +{ + return *_service; +} + +/** + * Destructor + */ +BLECharacteristic::~BLECharacteristic() +{ +// Bluefruit.Gatt._removeCharacteristic(this); +} + +/** + * Must be set when Charactertistic is declared locally (e.g insdie function) + * and is not last throughout programs. Useful for one-shot set-and-forget + * Characteristics such as read-only one. Where there is no need for interactions + * later on. This call will prevent the Characteristics to be hooked into + * managing chars list of AdafruitBluefruit + */ +void BLECharacteristic::setTempMemory(void) +{ + _is_temp = true; +} + +void BLECharacteristic::setProperties(uint8_t prop) +{ + memcpy(&_properties, &prop, 1); +} + +void BLECharacteristic::setMaxLen(uint16_t max_len) +{ + _max_len = max_len; +} + +void BLECharacteristic::setFixedLen(uint16_t fixed_len) +{ + if ( fixed_len ) + { + _max_len = fixed_len; + _attr_meta.vlen = 0; + }else + { + _attr_meta.vlen = 1; + } +} + +void BLECharacteristic::setPermission(BleSecurityMode read_perm, BleSecurityMode write_perm) +{ + memcpy(&_attr_meta.read_perm , &read_perm, 1); + memcpy(&_attr_meta.write_perm, &write_perm, 1); +} + +void BLECharacteristic::setWriteCallback(write_cb_t fp) +{ + _wr_cb = fp; +} + +void BLECharacteristic::setCccdWriteCallback(write_cccd_cb_t fp) +{ + _cccd_wr_cb = fp; +} + +void BLECharacteristic::setReadAuthorizeCallback(read_authorize_cb_t fp) +{ + _attr_meta.rd_auth = (fp ? 1 : 0); + _rd_authorize_cb = fp; +} + +void BLECharacteristic::setWriteAuthorizeCallback(write_authorize_cb_t fp) +{ + _attr_meta.wr_auth = (fp ? 1 : 0); + _wr_authorize_cb = fp; +} + +void BLECharacteristic::setUserDescriptor(const char* descriptor) +{ + _usr_descriptor = descriptor; +} + +void BLECharacteristic::setReportRefDescriptor(uint8_t id, uint8_t type) +{ + _report_ref_desc.id = id; + _report_ref_desc.type = type; +} + +/** + * https://developer.bluetooth.org/gatt/descriptors/Pages/DescriptorViewer.aspx?u=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml + * @param type BLE_GATT_CPF_FORMAT_x value, see ble_gatt.h + * @param exponent exponent + * @param unit UUID16_UNIT_x, see BLEUuid.h + */ +void BLECharacteristic::setPresentationFormatDescriptor(uint8_t type, int8_t exponent, uint16_t unit, uint8_t name_space, uint16_t descritpor) +{ + _format_desc.format = type; + _format_desc.exponent = exponent; + _format_desc.unit = unit; + _format_desc.name_space = name_space; + _format_desc.desc = descritpor; +} + +ble_gatts_char_handles_t BLECharacteristic::handles(void) +{ + return _handles; +} + +err_t BLECharacteristic::begin(void) +{ + _service = BLEService::lastService; + + // Add UUID128 if needed + (void) uuid.begin(); + + // Permission is OPEN if passkey is disabled. +// if (!nvm_data.core.passkey_enable) BLE_GAP_CONN_SEC_MODE_SET_OPEN(&p_char_def->permission); + + // Correct Read/Write permission according to properties + if ( !(_properties.read || _properties.notify || _properties.indicate ) ) + { + _attr_meta.read_perm = BLE_SECMODE_NO_ACCESS; + } + + if ( !(_properties.write || _properties.write_wo_resp ) ) + { + _attr_meta.write_perm = BLE_SECMODE_NO_ACCESS; + } + + /* CCCD attribute metadata */ + ble_gatts_attr_md_t cccd_md; + + if ( _properties.notify || _properties.indicate ) + { + /* Notification & Indication require CCCD */ + memclr( &cccd_md, sizeof(ble_gatts_attr_md_t) ); + cccd_md.vloc = BLE_GATTS_VLOC_STACK; + + cccd_md.read_perm = BLE_SECMODE_OPEN; + cccd_md.write_perm = _attr_meta.read_perm; + } + + /* Characteristic metadata */ + ble_gatts_char_md_t char_md; + varclr(&char_md); + + char_md.char_props = _properties; + char_md.p_cccd_md = (_properties.notify || _properties.indicate) ? &cccd_md : NULL; + + /* Characteristic extended properties (for user description) */ + ble_gatts_attr_md_t desc_md = + { + .read_perm = _attr_meta.read_perm, + .write_perm = BLE_SECMODE_NO_ACCESS, + .vlen = 0, + .vloc = BLE_GATTS_VLOC_STACK, + }; + + if (_usr_descriptor != NULL && _usr_descriptor[0] != 0) + { + char_md.p_char_user_desc = (uint8_t*) _usr_descriptor; + char_md.char_user_desc_size = char_md.char_user_desc_max_size = strlen(_usr_descriptor); + char_md.p_user_desc_md = &desc_md; + //char_md.char_ext_props = ext_props, + } + + /* Presentation Format Descriptor */ + if ( _format_desc.format != 0 ) + { + char_md.p_char_pf = &_format_desc; + } + + /* GATT attribute declaration */ + ble_gatts_attr_t attr_char_value = + { + .p_uuid = &uuid._uuid, + .p_attr_md = &_attr_meta, + .init_len = (_attr_meta.vlen == 1) ? (uint16_t) 0 : _max_len, + .init_offs = 0, + .max_len = _max_len, + .p_value = NULL + }; + + VERIFY_STATUS( sd_ble_gatts_characteristic_add(BLE_GATT_HANDLE_INVALID, &char_md, &attr_char_value, &_handles) ); + + // Report Reference Descriptor if any (required by HID) + if ( _report_ref_desc.type ) + { + // Reference Descriptor + ble_gatts_attr_md_t ref_md = + { + .read_perm = _attr_meta.read_perm, + .write_perm = BLE_SECMODE_NO_ACCESS, + .vlen = 0, + .vloc = BLE_GATTS_VLOC_STACK + }; + + ble_uuid_t ref_uuid = { .uuid = UUID16_REPORT_REF_DESCR, .type = BLE_UUID_TYPE_BLE }; + ble_gatts_attr_t ref_desc = + { + .p_uuid = &ref_uuid, + .p_attr_md = &ref_md, + .init_len = sizeof(_report_ref_desc), + .init_offs = 0, + .max_len = sizeof(_report_ref_desc), + .p_value = (uint8_t*) &_report_ref_desc + }; + + uint16_t ref_hdl; + VERIFY_STATUS ( sd_ble_gatts_descriptor_add(BLE_GATT_HANDLE_INVALID, &ref_desc, &ref_hdl) ); + + (void) ref_hdl; // not used + } + + // Currently Only register to Bluefruit if The Characteristic is not temporary memory i.e local variable + if ( !_is_temp ) + { + (void) Bluefruit.Gatt._addCharacteristic(this); + } + + return ERROR_NONE; +} + +err_t BLECharacteristic::addDescriptor(BLEUuid bleuuid, void const * content, uint16_t len, BleSecurityMode read_perm, BleSecurityMode write_perm) +{ + // Meta Data + ble_gatts_attr_md_t meta; + varclr(&meta); + + memcpy(&meta.read_perm , &read_perm , 1); + memcpy(&meta.write_perm, &write_perm, 1); + meta.vlen = 0; + meta.vloc = BLE_GATTS_VLOC_STACK; + + // Descriptor + (void) bleuuid.begin(); + + ble_gatts_attr_t desc = + { + .p_uuid = &bleuuid._uuid, + .p_attr_md = &meta, + .init_len = len, + .init_offs = 0, + .max_len = len, + .p_value = (uint8_t*) content + }; + + uint16_t hdl; + VERIFY_STATUS ( sd_ble_gatts_descriptor_add(BLE_GATT_HANDLE_INVALID, &desc, &hdl) ); + + return ERROR_NONE; +} + +/** + * @param event + */ +void BLECharacteristic::_eventHandler(ble_evt_t* event) +{ + uint16_t const conn_hdl = event->evt.common_evt.conn_handle; + + switch(event->header.evt_id) + { + case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST: + { + ble_gatts_evt_rw_authorize_request_t * request = &event->evt.gatts_evt.params.authorize_request; + + if (request->type == BLE_GATTS_AUTHORIZE_TYPE_WRITE) + { + ble_gatts_evt_write_t * wr_req = &request->request.write; + + switch(wr_req->op) + { + case BLE_GATTS_OP_PREP_WRITE_REQ: + { + // Prepare Long Write + if ( !_long_wr.buffer ) + { + // Allocate long write buffer if not previously + _long_wr.bufsize = 1024; // TODO bufsize is 10x MTU + _long_wr.buffer = (uint8_t*) rtos_malloc(_long_wr.bufsize); + _long_wr.count = 0; + } + + ble_gatts_rw_authorize_reply_params_t reply = { .type = BLE_GATTS_AUTHORIZE_TYPE_WRITE }; + + if ( wr_req->offset + wr_req->len > _long_wr.bufsize ) + { + reply.params.write.gatt_status = BLE_GATT_STATUS_ATTERR_PREPARE_QUEUE_FULL; + }else + { + reply.params.write = ((ble_gatts_authorize_params_t) { + .gatt_status = BLE_GATT_STATUS_SUCCESS, + .update = 1, + .offset = wr_req->offset, + .len = wr_req->len, + .p_data = wr_req->data + }); + + memcpy(_long_wr.buffer+wr_req->offset, wr_req->data, wr_req->len); + _long_wr.count = max16(_long_wr.count, wr_req->offset + wr_req->len); + } + + sd_ble_gatts_rw_authorize_reply(conn_hdl, &reply); + } + break; + + case BLE_GATTS_OP_EXEC_WRITE_REQ_NOW: + // Execute Long Write + if ( _long_wr.buffer ) + { + ble_gatts_rw_authorize_reply_params_t reply = { .type = BLE_GATTS_AUTHORIZE_TYPE_WRITE }; + reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS; + + sd_ble_gatts_rw_authorize_reply(conn_hdl, &reply); + + // Long write complete, call write callback if set + if (_wr_cb) _wr_cb(*this, _long_wr.buffer, _long_wr.count, 0); + + // free up memory + rtos_free(_long_wr.buffer); + _long_wr.buffer = NULL; + _long_wr.bufsize = _long_wr.count = 0; + } + break; + + case BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL: + // Cancel Long Write + break; + + case BLE_GATTS_OP_WRITE_REQ: + // Write Request with authorization + if (_wr_authorize_cb != NULL) _wr_authorize_cb(*this, &request->request.write); + break; + + default: break; + } + } + + if ( (request->type == BLE_GATTS_AUTHORIZE_TYPE_READ) && (_rd_authorize_cb != NULL)) + { + _rd_authorize_cb(*this, &request->request.read); + } + } + break; + + case BLE_GATTS_EVT_WRITE: + { + ble_gatts_evt_write_t* request = &event->evt.gatts_evt.params.write; + + LOG_LV2_BUFFER(NULL, request->data, request->len); + + // Value write + if (request->handle == _handles.value_handle) + { + LOG_LV2("GATTS", "attr's value, uuid = 0x%04X", request->uuid.uuid); + // TODO Ada callback + if (_wr_cb) + { +// uint8_t* data = (uint8_t*) rtos_malloc(request->len); +// +// if (data) +// { +// ada_callback(data, _wr_cb, *this, data, request->len, request->offset); +// }else + { + // invoke directly if cannot allocate memory for data + _wr_cb(*this, request->data, request->len, request->offset); + } + } + } + + // CCCD write + if ( request->handle == _handles.cccd_handle ) + { + LOG_LV2("GATTS", "attr's cccd"); + + // Invoke callback if set + if (_cccd_wr_cb) + { + uint16_t value; + memcpy(&value, request->data, 2); + _cccd_wr_cb(*this, value); + } + } + } + break; + + default: break; + } +} + +/*------------------------------------------------------------------*/ +/* WRITE + *------------------------------------------------------------------*/ +uint16_t BLECharacteristic::write(const char * str) +{ + return write((const uint8_t*) str, strlen(str)); +} + +uint16_t BLECharacteristic::write(const void* data, uint16_t len) +{ + ble_gatts_value_t value = + { + .len = min16(len, _max_len), // could not exceed max len + .offset = 0 , // TODO gatts long write + .p_value = (uint8_t*) data + }; + + // conn handle only needed for system attribute + VERIFY_STATUS( sd_ble_gatts_value_set(BLE_CONN_HANDLE_INVALID, _handles.value_handle, &value), 0 ); + + return value.len; +} + +uint16_t BLECharacteristic::write8(uint8_t num) +{ + return write( (uint8_t*) &num, sizeof(num)); +} + +uint16_t BLECharacteristic::write16(uint16_t num) +{ + return write( (uint8_t*) &num, sizeof(num)); +} + +uint16_t BLECharacteristic::write32(uint32_t num) +{ + return write( (uint8_t*) &num, sizeof(num)); +} + +uint16_t BLECharacteristic::write32(int num) +{ + return write32( (uint32_t) num ); +} + +/*------------------------------------------------------------------*/ +/* READ + *------------------------------------------------------------------*/ +/** + * Read Characteristic's value + * @param buffer memory to hold value + * @param len size of memory + * @param offset offset of value (dfeault is 0) + * @return number of read bytes + */ +uint16_t BLECharacteristic::read(void* buffer, uint16_t bufsize, uint16_t offset) +{ + ble_gatts_value_t value = + { + .len = bufsize, + .offset = offset, + .p_value = (uint8_t*) buffer + }; + + // conn handle only needed for system attribute + VERIFY_STATUS(sd_ble_gatts_value_get(BLE_CONN_HANDLE_INVALID, _handles.value_handle, &value), 0); + + return value.len; +} + +uint8_t BLECharacteristic::read8(void) +{ + uint8_t num; + return read(&num, sizeof(num)) ? num : 0; +} + +uint16_t BLECharacteristic::read16(void) +{ + uint16_t num; + return read(&num, sizeof(num)) ? num : 0; +} + +uint32_t BLECharacteristic::read32(void) +{ + uint32_t num; + return read(&num, sizeof(num)) ? num : 0; +} + + +uint16_t BLECharacteristic::getCccd(void) +{ + VERIFY( Bluefruit.connected() && (_handles.cccd_handle != BLE_GATT_HANDLE_INVALID), 0 ); + + uint16_t cccd; + ble_gatts_value_t value = + { + .len = 2, + .offset = 0, + .p_value = (uint8_t*) &cccd + }; + + err_t err = sd_ble_gatts_value_get(Bluefruit.connHandle(), _handles.cccd_handle, &value); + + // CCCD is not set, count as not enabled + if ( BLE_ERROR_GATTS_SYS_ATTR_MISSING == err ) + { + cccd = 0; + }else + { + VERIFY_STATUS(err); + } + + return cccd; +} + +/*------------------------------------------------------------------*/ +/* NOTIFY + *------------------------------------------------------------------*/ +bool BLECharacteristic::notifyEnabled(void) +{ + VERIFY( _properties.notify ); + return (getCccd() & BLE_GATT_HVX_NOTIFICATION); +} + +bool BLECharacteristic::notify(const void* data, uint16_t len) +{ + VERIFY( _properties.notify ); + + // could not exceed max len + uint16_t remaining = min16(len, _max_len); + + if ( notifyEnabled() ) + { + uint16_t const max_payload = Bluefruit.Gap.getMTU( Bluefruit.connHandle() ) - 3; + const uint8_t* u8data = (const uint8_t*) data; + + while ( remaining ) + { + // TODO multiple connection support + // Failed if there is no free buffer + if ( !Bluefruit.Gap.getHvnPacket( Bluefruit.connHandle() ) ) return false; + + uint16_t packet_len = min16(max_payload, remaining); + + ble_gatts_hvx_params_t hvx_params = + { + .handle = _handles.value_handle, + .type = BLE_GATT_HVX_NOTIFICATION, + .offset = 0, + .p_len = &packet_len, + .p_data = (uint8_t*) u8data, + }; + + LOG_LV2("CHR", "Notify %d bytes", packet_len); + VERIFY_STATUS( sd_ble_gatts_hvx(Bluefruit.connHandle(), &hvx_params), false ); + + remaining -= packet_len; + u8data += packet_len; + } + } + else + { + write(data, remaining); + return false; + } + + return true; +} + +bool BLECharacteristic::notify(const char * str) +{ + return notify( (const uint8_t*) str, strlen(str) ); +} + +bool BLECharacteristic::notify8(uint8_t num) +{ + return notify( (uint8_t*) &num, sizeof(num)); +} + +bool BLECharacteristic::notify16(uint16_t num) +{ + return notify( (uint8_t*) &num, sizeof(num)); +} + +bool BLECharacteristic::notify32(uint32_t num) +{ + return notify( (uint8_t*) &num, sizeof(num)); +} + +bool BLECharacteristic::notify32(int num) +{ + return notify32( (uint32_t) num); +} + +/*------------------------------------------------------------------*/ +/* INDICATE + *------------------------------------------------------------------*/ +bool BLECharacteristic::indicateEnabled(void) +{ + VERIFY( _properties.indicate ); + return (getCccd() & BLE_GATT_HVX_INDICATION); +} + +bool BLECharacteristic::indicate(const void* data, uint16_t len) +{ + VERIFY( _properties.indicate ); + + // could not exceed max len + uint16_t remaining = min16(len, _max_len); + + if ( indicateEnabled() ) + { + uint16_t conn_hdl = Bluefruit.connHandle(); + + uint16_t const max_payload = Bluefruit.Gap.getMTU( conn_hdl ) - 3; + const uint8_t* u8data = (const uint8_t*) data; + + while ( remaining ) + { + uint16_t packet_len = min16(max_payload, remaining); + + ble_gatts_hvx_params_t hvx_params = + { + .handle = _handles.value_handle, + .type = BLE_GATT_HVX_INDICATION, + .offset = 0, + .p_len = &packet_len, + .p_data = (uint8_t*) u8data, + }; + + LOG_LV2("CHR", "Indicate %d bytes", packet_len); + + // Blocking wait until receiving confirmation from peer + VERIFY_STATUS( sd_ble_gatts_hvx( conn_hdl, &hvx_params), false ); + VERIFY ( Bluefruit.Gatt.waitForIndicateConfirm(conn_hdl) ); + + remaining -= packet_len; + u8data += packet_len; + } + } + else + { + write(data, remaining); + return false; + } + + return true; +} + +bool BLECharacteristic::indicate(const char * str) +{ + return indicate( (const uint8_t*) str, strlen(str) ); +} + +bool BLECharacteristic::indicate8(uint8_t num) +{ + return indicate( (uint8_t*) &num, sizeof(num)); +} + +bool BLECharacteristic::indicate16(uint16_t num) +{ + return indicate( (uint8_t*) &num, sizeof(num)); +} + +bool BLECharacteristic::indicate32(uint32_t num) +{ + return indicate( (uint8_t*) &num, sizeof(num)); +} + +bool BLECharacteristic::indicate32(int num) +{ + return indicate32( (uint32_t) num); +} diff --git a/arduino/libraries/Bluefruit52Lib/src/BLECharacteristic.h b/arduino/libraries/Bluefruit52Lib/src/BLECharacteristic.h new file mode 100755 index 0000000..f0b1d9e --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/BLECharacteristic.h @@ -0,0 +1,198 @@ +/**************************************************************************/ +/*! + @file BLECharacteristic.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLECHARACTERISTIC_H_ +#define BLECHARACTERISTIC_H_ + +#include "bluefruit_common.h" +#include "BLEUuid.h" + +// Forward declaration +class AdafruitBluefruit; +class BLEService; + +enum BleSecurityMode +{ + SECMODE_NO_ACCESS = 0x00, + SECMODE_OPEN = 0x11, + SECMODE_ENC_NO_MITM = 0x21, + SECMODE_ENC_WITH_MITM = 0x31, + SECMODE_SIGNED_NO_MITM = 0x12, + SECMODE_SIGNED_WITH_MITM = 0x22 +}; + +#define BLE_SECMODE_NO_ACCESS ((ble_gap_conn_sec_mode_t) { .sm = 0, .lv = 0 }) +#define BLE_SECMODE_OPEN ((ble_gap_conn_sec_mode_t) { .sm = 1, .lv = 1 }) +#define BLE_SECMODE_ENC_NO_MITM ((ble_gap_conn_sec_mode_t) { .sm = 1, .lv = 2 }) +#define BLE_SECMODE_ENC_WITH_MITM ((ble_gap_conn_sec_mode_t) { .sm = 1, .lv = 3 }) +#define BLE_SECMODE_SIGNED_NO_MITM ((ble_gap_conn_sec_mode_t) { .sm = 2, .lv = 1 }) +#define BLE_SECMODE_SIGNED_WITH_MITM ((ble_gap_conn_sec_mode_t) { .sm = 2, .lv = 2 }) + +enum CharsProperties +{ + CHR_PROPS_BROADCAST = bit(0), + CHR_PROPS_READ = bit(1), + CHR_PROPS_WRITE_WO_RESP = bit(2), + CHR_PROPS_WRITE = bit(3), + CHR_PROPS_NOTIFY = bit(4), + CHR_PROPS_INDICATE = bit(5) +}; + +class BLECharacteristic +{ + public: + /*--------- Callback Signatures ----------*/ + typedef void (*read_authorize_cb_t) (BLECharacteristic& chr, ble_gatts_evt_read_t * request); + typedef void (*write_authorize_cb_t) (BLECharacteristic& chr, ble_gatts_evt_write_t* request); + typedef void (*write_cb_t) (BLECharacteristic& chr, uint8_t* data, uint16_t len, uint16_t offset); + typedef void (*write_cccd_cb_t) (BLECharacteristic& chr, uint16_t value); + + BLEUuid uuid; + + // Constructors + BLECharacteristic(void); + BLECharacteristic(BLEUuid bleuuid); + + // Destructor + virtual ~BLECharacteristic(); + + BLEService& parentService(void); + + void setTempMemory(void); + + /*------------- Configure -------------*/ + void setUuid(BLEUuid bleuuid); + void setProperties(uint8_t prop); + void setPermission(BleSecurityMode read_perm, BleSecurityMode write_perm); + void setMaxLen(uint16_t max_len); + void setFixedLen(uint16_t fixed_len); + + /*------------- Descriptors -------------*/ + void setUserDescriptor(const char* descriptor); // aka user descriptor + void setReportRefDescriptor(uint8_t id, uint8_t type); // TODO refactor to use addDescriptor() + void setPresentationFormatDescriptor(uint8_t type, int8_t exponent, uint16_t unit, uint8_t name_space = 1, uint16_t descritpor = 0); + + /*------------- Callbacks -------------*/ + void setWriteCallback (write_cb_t fp); + void setCccdWriteCallback (write_cccd_cb_t fp); + void setReadAuthorizeCallback(read_authorize_cb_t fp); + void setWriteAuthorizeCallback(write_authorize_cb_t fp); + + virtual err_t begin(void); + + // Add Descriptor function must be called right after begin() + err_t addDescriptor(BLEUuid bleuuid, void const * content, uint16_t len, BleSecurityMode read_perm = SECMODE_OPEN, BleSecurityMode write_perm = SECMODE_NO_ACCESS); + + ble_gatts_char_handles_t handles(void); + + /*------------- Write -------------*/ + uint16_t write(const void* data, uint16_t len); + uint16_t write(const char* str); + + uint16_t write8 (uint8_t num); + uint16_t write16 (uint16_t num); + uint16_t write32 (uint32_t num); + uint16_t write32 (int num); + + + /*------------- Read -------------*/ + uint16_t read(void* buffer, uint16_t bufsize, uint16_t offset = 0); + + uint8_t read8 (void); + uint16_t read16(void); + uint32_t read32(void); + + /*------------- Notify -------------*/ + uint16_t getCccd(void); + + bool notifyEnabled(void); + + bool notify(const void* data, uint16_t len); + bool notify(const char* str); + + bool notify8 (uint8_t num); + bool notify16 (uint16_t num); + bool notify32 (uint32_t num); + bool notify32 (int num); + + /*------------- Indicate -------------*/ + bool indicateEnabled(void); + + bool indicate(const void* data, uint16_t len); + bool indicate(const char* str); + + bool indicate8 (uint8_t num); + bool indicate16 (uint16_t num); + bool indicate32 (uint32_t num); + bool indicate32 (int num); + + /*------------- Internal Functions -------------*/ + virtual void _eventHandler(ble_evt_t* event); + + protected: + bool _is_temp; + uint16_t _max_len; + BLEService* _service; // pointer to parent's service + + /*------------- Descriptors -------------*/ + const char* _usr_descriptor; + struct ATTR_PACKED { + uint8_t id; + uint8_t type; + }_report_ref_desc; + + ble_gatts_char_pf_t _format_desc; + ble_gatt_char_props_t _properties; + ble_gatts_attr_md_t _attr_meta; + ble_gatts_char_handles_t _handles; + + struct { + uint8_t* buffer; + uint16_t bufsize; + uint16_t count; + }_long_wr; + + /*------------- Callback pointers -------------*/ + read_authorize_cb_t _rd_authorize_cb; + write_authorize_cb_t _wr_authorize_cb; + + write_cb_t _wr_cb; + write_cccd_cb_t _cccd_wr_cb; + + /*------------- Internal Functions -------------*/ + void _init(void); +}; + +#endif /* BLECHARACTERISTIC_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/BLEClientCharacteristic.cpp b/arduino/libraries/Bluefruit52Lib/src/BLEClientCharacteristic.cpp new file mode 100755 index 0000000..6e40266 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/BLEClientCharacteristic.cpp @@ -0,0 +1,543 @@ +/**************************************************************************/ +/*! + @file BLEClientCharacteristic.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" + +void BLEClientCharacteristic::_init(void) +{ + varclr(&_chr); + _chr.handle_value = BLE_GATT_HANDLE_INVALID; + _cccd_handle = BLE_GATT_HANDLE_INVALID; + + _notify_cb = NULL; + _indicate_cb = NULL; + + varclr(&_use_ada_cb); +} + +BLEClientCharacteristic::BLEClientCharacteristic(void) + : uuid(), _adamsg() +{ + _init(); +} + +BLEClientCharacteristic::BLEClientCharacteristic(BLEUuid bleuuid) + : uuid(bleuuid), _adamsg() +{ + _init(); +} + +/** + * Destructor + */ +BLEClientCharacteristic::~BLEClientCharacteristic() +{ + _adamsg.stop(); + Bluefruit.Gatt._removeCharacteristic(this); +} + +void BLEClientCharacteristic::begin(BLEClientService* parent_svc) +{ + // Add UUID128 if needed + (void) uuid.begin(); + + // Use the last (discovered) service as parent if not provided + _service = ( parent_svc == NULL ) ? BLEClientService::lastService : parent_svc; + + // Register to Bluefruit (required for callback and write response) + (void) Bluefruit.Gatt._addCharacteristic(this); + + _adamsg.begin(true); +} + +void BLEClientCharacteristic::_assign(ble_gattc_char_t* gattc_chr) +{ + _chr = *gattc_chr; +} + +void BLEClientCharacteristic::disconnect(void) +{ + _chr.handle_value = BLE_GATT_HANDLE_INVALID; +} + + +bool BLEClientCharacteristic::discover(void) +{ + ble_gattc_handle_range_t bck_range = Bluefruit.Discovery.getHandleRange(); + + // Set discovery handle to parent's service + Bluefruit.Discovery.setHandleRange( _service->getHandleRange() ); + + bool result = Bluefruit.Discovery.discoverCharacteristic( _service->connHandle(), *this) > 0; + + // Set back to previous + Bluefruit.Discovery.setHandleRange(bck_range); + + return result; +} + +bool BLEClientCharacteristic::discovered(void) +{ + return _chr.handle_value != BLE_GATT_HANDLE_INVALID; +} + +uint16_t BLEClientCharacteristic::connHandle(void) +{ + return _service->connHandle(); +} + +uint16_t BLEClientCharacteristic::valueHandle(void) +{ + return _chr.handle_value; +} + +uint8_t BLEClientCharacteristic::properties(void) +{ + uint8_t u8; + memcpy(&u8, &_chr.char_props, 1); + return u8; +} + +BLEClientService& BLEClientCharacteristic::parentService (void) +{ + return *_service; +} + +bool BLEClientCharacteristic::_discoverDescriptor(uint16_t conn_handle, ble_gattc_handle_range_t hdl_range) +{ + enum { MAX_DESCIRPTORS = 8 }; + + struct { + uint16_t count; + ble_gattc_desc_t descs[MAX_DESCIRPTORS]; + }disc_rsp; + + uint16_t count = Bluefruit.Discovery._discoverDescriptor(conn_handle, (ble_gattc_evt_desc_disc_rsp_t*) &disc_rsp, sizeof(disc_rsp), hdl_range); + + // only care CCCD for now + for(uint16_t i=0; i<count; i++) + { + if ( disc_rsp.descs[i].uuid.type == BLE_UUID_TYPE_BLE && + disc_rsp.descs[i].uuid.uuid == BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG ) + { + LOG_LV2("DISC", "Found CCDD: handle = %d", disc_rsp.descs[i].handle); + _cccd_handle = disc_rsp.descs[i].handle; + } + } + + return true; +} + +/*------------------------------------------------------------------*/ +/* READ + *------------------------------------------------------------------*/ +uint16_t BLEClientCharacteristic::read(void* buffer, uint16_t bufsize) +{ + VERIFY( _chr.char_props.read, 0 ); + + uint16_t const max_payload = Bluefruit.Gap.getMTU( _service->connHandle() ) - 3; + + _adamsg.prepare(buffer, bufsize); + VERIFY_STATUS( sd_ble_gattc_read(_service->connHandle(), _chr.handle_value, 0), 0); + int32_t rxlen = _adamsg.waitUntilComplete( (bufsize/(max_payload-2) + 1) * BLE_GENERIC_TIMEOUT ); + + return (rxlen < 0) ? 0 : rxlen; +} + +uint8_t BLEClientCharacteristic::read8 (void) +{ + uint8_t num; + return read(&num, sizeof(num)) ? num : 0; +} + +uint16_t BLEClientCharacteristic::read16(void) +{ + uint16_t num; + return read(&num, sizeof(num)) ? num : 0; +} + +uint32_t BLEClientCharacteristic::read32(void) +{ + uint32_t num; + return read(&num, sizeof(num)) ? num : 0; +} + +/*------------------------------------------------------------------*/ +/* WRITE + *------------------------------------------------------------------*/ +uint16_t BLEClientCharacteristic::write_resp(const void* data, uint16_t len) +{ + VERIFY( _chr.char_props.write, 0 ); + + uint16_t const max_payload = Bluefruit.Gap.getMTU( _service->connHandle() ) - 3; + + const bool long_write = (len > max_payload); + int32_t count = 0; + + // CMD WRITE_REQUEST for single transaction + if ( !long_write ) + { + ble_gattc_write_params_t param = + { + .write_op = BLE_GATT_OP_WRITE_REQ, + .flags = 0, + .handle = _chr.handle_value, + .offset = 0, + .len = len, + .p_value = (uint8_t*) data + }; + + _adamsg.prepare( (void*) data, len); + VERIFY_STATUS(sd_ble_gattc_write(_service->connHandle(), ¶m), 0); + + // len is always 0 in BLE_GATTC_EVT_WRITE_RSP for BLE_GATT_OP_WRITE_REQ + count = (_adamsg.waitUntilComplete(BLE_GENERIC_TIMEOUT) < 0 ? 0 : len); + } + else + { + /*------------- Long Write Sequence -------------*/ + // For BLE_GATT_OP_PREP_WRITE_REQ, 2 bytes are used for offset + ble_gattc_write_params_t param = + { + .write_op = BLE_GATT_OP_PREP_WRITE_REQ, + .flags = 0, + .handle = _chr.handle_value, + .offset = 0, + .len = min16(len, max_payload-2), + .p_value = (uint8_t*) data + }; + + _adamsg.prepare( (void*) data, len); + VERIFY_STATUS(sd_ble_gattc_write(_service->connHandle(), ¶m), 0); + count = _adamsg.waitUntilComplete( (len/(max_payload-2) + 1) * BLE_GENERIC_TIMEOUT ); + + // delay to swallow last WRITE RESPONSE + // delay(20); + } + + return (count < 0) ? 0 : count; +} + +uint16_t BLEClientCharacteristic::write8_resp(uint8_t value) +{ + return write_resp(&value, sizeof(value)); +} + +uint16_t BLEClientCharacteristic::write16_resp(uint16_t value) +{ + return write_resp(&value, sizeof(value)); +} + +uint16_t BLEClientCharacteristic::write32_resp(uint32_t value) +{ + return write_resp(&value, sizeof(value)); +} + +uint16_t BLEClientCharacteristic::write32_resp(int value) +{ + return write32_resp((uint32_t) value); +} + +uint16_t BLEClientCharacteristic::write(const void* data, uint16_t len) +{ +// VERIFY( _chr.char_props.write_wo_resp, 0 ); + + uint16_t const max_payload = Bluefruit.Gap.getMTU( _service->connHandle() ) - 3; + const uint8_t* u8data = (const uint8_t*) data; + + // Break into multiple packet if needed + uint16_t remaining = len; + while( remaining ) + { + // TODO only Write without response consume a TX buffer + if ( !Bluefruit.Gap.getWriteCmdPacket(_service->connHandle()) ) break; + + uint16_t packet_len = min16(max_payload, remaining); + + ble_gattc_write_params_t param = + { + .write_op = BLE_GATT_OP_WRITE_CMD , + .flags = 0 , // not used with BLE_GATT_OP_WRITE_CMD + .handle = _chr.handle_value , + .offset = 0 , // not used with BLE_GATT_OP_WRITE_CMD + .len = packet_len , + .p_value = (uint8_t* ) u8data + }; + + VERIFY_STATUS( sd_ble_gattc_write(_service->connHandle(), ¶m), len-remaining); + + remaining -= packet_len; + u8data += packet_len; + } + + return len-remaining; +} + +uint16_t BLEClientCharacteristic::write8(uint8_t value) +{ + return write(&value, sizeof(value)); +} + +uint16_t BLEClientCharacteristic::write16(uint16_t value) +{ + return write(&value, sizeof(value)); +} + +uint16_t BLEClientCharacteristic::write32(uint32_t value) +{ + return write(&value, sizeof(value)); +} + +uint16_t BLEClientCharacteristic::write32(int value) +{ + return write32( (uint32_t) value); +} + + +void BLEClientCharacteristic::setNotifyCallback(notify_cb_t fp, bool useAdaCallback) +{ + _notify_cb = fp; + _use_ada_cb.notify = useAdaCallback; +} + +void BLEClientCharacteristic::setIndicateCallback(indicate_cb_t fp, bool useAdaCallback) +{ + _indicate_cb = fp; + _use_ada_cb.indicate = useAdaCallback; +} + +bool BLEClientCharacteristic::writeCCCD(uint16_t value) +{ + const uint16_t conn_handle = _service->connHandle(); + + ble_gattc_write_params_t param = + { + .write_op = BLE_GATT_OP_WRITE_CMD, + .flags = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_WRITE, + .handle = _cccd_handle, + .offset = 0, + .len = 2, + .p_value = (uint8_t*) &value + }; + + // TODO only Write without response consume a TX buffer + if ( !Bluefruit.Gap.getWriteCmdPacket(conn_handle) ) return NRF_ERROR_RESOURCES; //BLE_ERROR_NO_TX_PACKETS; + + VERIFY_STATUS( sd_ble_gattc_write(conn_handle, ¶m), false ); + + return true; +} + +bool BLEClientCharacteristic::enableNotify(void) +{ + VERIFY( _chr.char_props.notify ); + return writeCCCD(0x0001); +} + +bool BLEClientCharacteristic::disableNotify(void) +{ + VERIFY( _chr.char_props.notify ); + return writeCCCD(0x0000); +} + +bool BLEClientCharacteristic::enableIndicate (void) +{ + VERIFY( _chr.char_props.indicate ); + return writeCCCD(0x0002); +} + +bool BLEClientCharacteristic::disableIndicate (void) +{ + VERIFY( _chr.char_props.indicate ); + return writeCCCD(0x0000); +} + +void BLEClientCharacteristic::_eventHandler(ble_evt_t* evt) +{ + const uint16_t evt_conn_hdl = evt->evt.common_evt.conn_handle; + uint16_t gatt_status = evt->evt.gattc_evt.gatt_status; + + switch(evt->header.evt_id) + { + case BLE_GATTC_EVT_HVX: + { + ble_gattc_evt_hvx_t* hvx = &evt->evt.gattc_evt.params.hvx; + + switch ( hvx->type ) + { + case BLE_GATT_HVX_NOTIFICATION: + if (_notify_cb) + { + // use AdaCallback or invoke directly + if (_use_ada_cb.notify) + { + uint8_t* data = (uint8_t*) rtos_malloc(hvx->len); + if (!data) return; + memcpy(data, hvx->data, hvx->len); + + // data is free by callback + ada_callback(data, _notify_cb, this, data, hvx->len); + }else + { + _notify_cb(this, hvx->data, hvx->len); + } + } + break; + + case BLE_GATT_HVX_INDICATION: + if (_indicate_cb) + { + // use AdaCallback or invoke directly + if (_use_ada_cb.indicate) + { + uint8_t* data = (uint8_t*) rtos_malloc(hvx->len); + if (!data) return; + memcpy(data, hvx->data, hvx->len); + + // data is free by callback + ada_callback(data, _indicate_cb, this, data, hvx->len); + }else + { + _indicate_cb(this, hvx->data, hvx->len); + } + + // Send confirmation to server + VERIFY_STATUS( sd_ble_gattc_hv_confirm(evt_conn_hdl, hvx->handle), ); + } + break; + + default : break; + } + } + break; + + case BLE_GATTC_EVT_WRITE_RSP: + { + ble_gattc_evt_write_rsp_t* wr_rsp = (ble_gattc_evt_write_rsp_t*) &evt->evt.gattc_evt.params.write_rsp; + + // Give up if failed + if ( gatt_status != BLE_GATT_STATUS_SUCCESS ) + { + _adamsg.complete(); + break; + } + + _adamsg.feed(NULL, wr_rsp->len); + + if ( wr_rsp->write_op == BLE_GATT_OP_WRITE_REQ) + { + // len is known to be zero for WRITE_REQ + _adamsg.complete(); + } + else if ( wr_rsp->write_op == BLE_GATT_OP_PREP_WRITE_REQ) + { + uint16_t const max_payload = Bluefruit.Gap.getMTU( _service->connHandle() ) - 3; + uint16_t packet_len = min16(_adamsg.remaining, max_payload-2); + + if ( packet_len ) + { + // still has data, continue to prepare Long Write sequence + ble_gattc_write_params_t param = + { + .write_op = BLE_GATT_OP_PREP_WRITE_REQ, + .flags = 0, + .handle = _chr.handle_value, + .offset = (uint16_t) (wr_rsp->offset + wr_rsp->len), + .len = packet_len, + .p_value = (uint8_t*) _adamsg.buffer + }; + + // give up if cannot write + if ( ERROR_NONE != sd_ble_gattc_write(_service->connHandle(), ¶m) ) + { + _adamsg.complete(); + } + }else + { + // All data is prepared, execute Long Write + ble_gattc_write_params_t param = + { + .write_op = BLE_GATT_OP_EXEC_WRITE_REQ, + .flags = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_WRITE, + .handle = _chr.handle_value + }; + + sd_ble_gattc_write(_service->connHandle(), ¶m); + + // Last BLE_GATTC_EVT_WRITE_RSP for BLE_GATT_OP_EXEC_WRITE_REQ does not + // contain characteristic's handle. Therefore BLEGatt couldn't forward the + // event to us. Just skip the wait for now + _adamsg.complete(); + } + }else + { + // BLE_GATT_OP_EXEC_WRITE_REQ wont reach here due to the handle = 0 issue + } + } + break; + + case BLE_GATTC_EVT_READ_RSP: + { + uint16_t const max_payload = Bluefruit.Gap.getMTU( _service->connHandle() ) - 3; + ble_gattc_evt_read_rsp_t* rd_rsp = (ble_gattc_evt_read_rsp_t*) &evt->evt.gattc_evt.params.read_rsp; + + // Give up if failed (BLE_GATT_STATUS_ATTERR_INVALID_OFFSET usually) + if ( gatt_status != BLE_GATT_STATUS_SUCCESS ) + { + _adamsg.complete(); + break; + } + + _adamsg.feed(rd_rsp->data, rd_rsp->len); + + /* Complete condition is one of follows + * - Running out of buffer + * - Receive data less than MPS - 1 + * - Couldn't perform GATTC Read + */ + if (( _adamsg.remaining == 0) || + (rd_rsp->len < (max_payload-1) ) || + (ERROR_NONE != sd_ble_gattc_read(_service->connHandle(), _chr.handle_value, _adamsg.xferlen)) ) + { + _adamsg.complete(); + } + } + break; + + default: break; + } +} + diff --git a/arduino/libraries/Bluefruit52Lib/src/BLEClientCharacteristic.h b/arduino/libraries/Bluefruit52Lib/src/BLEClientCharacteristic.h new file mode 100755 index 0000000..17c6907 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/BLEClientCharacteristic.h @@ -0,0 +1,135 @@ +/**************************************************************************/ +/*! + @file BLEClientCharacteristic.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLECLIENTCHARACTERISTIC_H_ +#define BLECLIENTCHARACTERISTIC_H_ + +#include "bluefruit_common.h" +#include "BLEUuid.h" +#include "BLECharacteristic.h" + +// Forward declaration +class BLEClientService; + +class BLEClientCharacteristic +{ + public: + /*--------- Callback Signatures ----------*/ + typedef void (*notify_cb_t ) (BLEClientCharacteristic* chr, uint8_t* data, uint16_t len); + typedef void (*indicate_cb_t) (BLEClientCharacteristic* chr, uint8_t* data, uint16_t len); + + BLEUuid uuid; + + // Constructors + BLEClientCharacteristic(void); + BLEClientCharacteristic(BLEUuid bleuuid); + + // Destructor + virtual ~BLEClientCharacteristic(); + + void begin(BLEClientService* parent_svc = NULL); + + bool discover(void); + bool discovered(void); + + uint16_t connHandle(void); + uint16_t valueHandle(void); + uint8_t properties(void); + + BLEClientService& parentService(void); + + /*------------- Read -------------*/ + uint16_t read(void* buffer, uint16_t bufsize); + uint8_t read8 (void); + uint16_t read16(void); + uint32_t read32(void); + + /*------------- Write without Response-------------*/ + uint16_t write (const void* data, uint16_t len); + uint16_t write8 (uint8_t value); + uint16_t write16 (uint16_t value); + uint16_t write32 (uint32_t value); + uint16_t write32 (int value); + + /*------------- Write with Response-------------*/ + uint16_t write_resp(const void* data, uint16_t len); + uint16_t write8_resp (uint8_t value); + uint16_t write16_resp (uint16_t value); + uint16_t write32_resp (uint32_t value); + uint16_t write32_resp (int value); + + /*------------- Notify -------------*/ + bool writeCCCD (uint16_t value); + + bool enableNotify (void); + bool disableNotify (void); + + bool enableIndicate (void); + bool disableIndicate (void); + + /*------------- Callbacks -------------*/ + void setNotifyCallback(notify_cb_t fp, bool useAdaCallback = true); + void setIndicateCallback(indicate_cb_t fp, bool useAdaCallback = true); + + /*------------- Internal usage -------------*/ + void _assign(ble_gattc_char_t* gattc_chr); + bool _discoverDescriptor(uint16_t conn_handle, ble_gattc_handle_range_t hdl_range); + + private: + ble_gattc_char_t _chr; + uint16_t _cccd_handle; + + BLEClientService* _service; + AdaMsg _adamsg; + + /*------------- Callbacks -------------*/ + notify_cb_t _notify_cb; + indicate_cb_t _indicate_cb; + + struct ATTR_PACKED { + uint8_t notify : 1; + uint8_t indicate : 1; + } _use_ada_cb; + + + void _init (void); + void _eventHandler (ble_evt_t* event); + + void disconnect(void); + + friend class BLEGatt; +}; + +#endif /* BLECLIENTCHARACTERISTIC_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/BLEClientService.cpp b/arduino/libraries/Bluefruit52Lib/src/BLEClientService.cpp new file mode 100755 index 0000000..a4850ea --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/BLEClientService.cpp @@ -0,0 +1,108 @@ +/**************************************************************************/ +/*! + @file BLEClientService.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" + +// Last service that is discovered +BLEClientService* BLEClientService::lastService = NULL; + +void BLEClientService::_init(void) +{ + _conn_hdl = BLE_CONN_HANDLE_INVALID; + + _hdl_range.start_handle = 1; + _hdl_range.end_handle = 0xffff; +} + +BLEClientService::BLEClientService(void) + : uuid() +{ + _init(); +} + +BLEClientService::BLEClientService(BLEUuid bleuuid) + : uuid(bleuuid) +{ + _init(); +} + + +bool BLEClientService::begin(void) +{ + // Add UUID128 if needed + (void) uuid.begin(); + + lastService = this; + (void) Bluefruit.Gatt._addService(this); + + return true; +} + +bool BLEClientService::discover(uint16_t conn_handle) +{ + // Initialize Discovery module if needed + if ( !Bluefruit.Discovery.begun() ) Bluefruit.Discovery.begin(); + + VERIFY( Bluefruit.Discovery._discoverService(conn_handle, *this) ); + _conn_hdl = conn_handle; + return true; +} + +bool BLEClientService::discovered(void) +{ + return _conn_hdl != BLE_CONN_HANDLE_INVALID; +} + +uint16_t BLEClientService::connHandle(void) +{ + return _conn_hdl; +} + +void BLEClientService::setHandleRange(ble_gattc_handle_range_t handle_range) +{ + _hdl_range = handle_range; +} + +ble_gattc_handle_range_t BLEClientService::getHandleRange(void) +{ + return _hdl_range; +} + +void BLEClientService::disconnect(void) +{ + _conn_hdl = BLE_CONN_HANDLE_INVALID; + // inherited service may want to clean up its own characteristic +} diff --git a/arduino/libraries/Bluefruit52Lib/src/BLEClientService.h b/arduino/libraries/Bluefruit52Lib/src/BLEClientService.h new file mode 100755 index 0000000..63b4312 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/BLEClientService.h @@ -0,0 +1,75 @@ +/**************************************************************************/ +/*! + @file BLEClientService.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLECLIENTSERVICE_H_ +#define BLECLIENTSERVICE_H_ + +#include "bluefruit_common.h" +#include "BLEUuid.h" + +class BLEClientService +{ + protected: + uint16_t _conn_hdl; + ble_gattc_handle_range_t _hdl_range; + + void _init(void); + virtual void disconnect(void); + + public: + static BLEClientService* lastService; + + BLEUuid uuid; + + // Constructors + BLEClientService(void); + BLEClientService(BLEUuid bleuuid); + + virtual bool begin(void); + + virtual bool discover (uint16_t conn_handle); + bool discovered(void); + + uint16_t connHandle(void); + + void setHandleRange(ble_gattc_handle_range_t handle_range); + ble_gattc_handle_range_t getHandleRange(void); + + friend class BLEGatt; +}; + + + +#endif /* BLECLIENTSERVICE_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/BLEDiscovery.cpp b/arduino/libraries/Bluefruit52Lib/src/BLEDiscovery.cpp new file mode 100755 index 0000000..682ac02 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/BLEDiscovery.cpp @@ -0,0 +1,275 @@ +/**************************************************************************/ +/*! + @file BLEDiscovery.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" + +BLEDiscovery::BLEDiscovery(void) + : _adamsg() +{ + _hdl_range.start_handle = 1; + _hdl_range.end_handle = 0xffff; + + _begun = false; +} + +void BLEDiscovery::begin(void) +{ + if ( !_begun ) + { + _adamsg.begin(false); + _begun = true; + } +} + +bool BLEDiscovery::begun(void) +{ + return _begun; +} + +void BLEDiscovery::setHandleRange(ble_gattc_handle_range_t handle_range) +{ + _hdl_range = handle_range; +} + +ble_gattc_handle_range_t BLEDiscovery::getHandleRange(void) +{ + return _hdl_range; +} + +bool BLEDiscovery::_discoverService(uint16_t conn_handle, BLEClientService& svc, uint16_t start_handle) +{ + ble_gattc_evt_prim_srvc_disc_rsp_t disc_svc; + + LOG_LV2("DISC", "[SVC] Handle start = %d", start_handle); + + _adamsg.prepare(&disc_svc, sizeof(disc_svc)); + VERIFY_STATUS( sd_ble_gattc_primary_services_discover(conn_handle, start_handle, &svc.uuid._uuid), false ); + + // wait for discovery event + int32_t bytecount = _adamsg.waitUntilComplete(BLE_DISCOVERY_TIMEOUT); + + // timeout or has no data (due to GATT Error) + if ( bytecount <= 0 ) + { + LOG_LV1("DISC", "[SVC] timeout or error", start_handle); + return false; + } + + // Check the discovered UUID with input one + if ( (disc_svc.count) && (svc.uuid == disc_svc.services[0].uuid) ) + { + _hdl_range = disc_svc.services[0].handle_range; + svc.setHandleRange(_hdl_range); + + LOG_LV2("DISC", "[SVC] Found 0x%04X, Handle start = %d, end = %d\n-----------------", disc_svc.services[0].uuid.uuid, _hdl_range.start_handle, _hdl_range.end_handle); + + // increase for next discovery + _hdl_range.start_handle++; + return true; + } + + return false; +} + +uint8_t BLEDiscovery::discoverCharacteristic(uint16_t conn_handle, BLEClientCharacteristic* chr[], uint8_t count) +{ + // We could found more characteristic than we looking for. Buffer must be large enough + enum { MAX_DISC_CHARS = 4 }; + + uint16_t bufsize = sizeof(ble_gattc_evt_char_disc_rsp_t) + (MAX_DISC_CHARS-1)*sizeof(ble_gattc_char_t); + ble_gattc_evt_char_disc_rsp_t* disc_chr = (ble_gattc_evt_char_disc_rsp_t*) rtos_malloc( bufsize ); + + uint8_t found = 0; + + while( found < count ) + { + LOG_LV2("DISC", "[CHR] Handle start = %d, end = %d", _hdl_range.start_handle, _hdl_range.end_handle); + + memclr(disc_chr, bufsize); + _adamsg.prepare(disc_chr, bufsize); + + if( ERROR_NONE != sd_ble_gattc_characteristics_discover(conn_handle, &_hdl_range) ) break; + + // wait for discovery event + int32_t bytecount = _adamsg.waitUntilComplete(BLE_DISCOVERY_TIMEOUT); + + // timeout or has no data (due to GATT Error) + if ( bytecount <= 0 ) break; + + // Look for matched uuid in the discovered list + for(uint8_t d=0 ; d<disc_chr->count; d++) + { + for (uint8_t i=0; i<count; i++) + { + if ( chr[i]->uuid == disc_chr->chars[d].uuid ) + { + LOG_LV2("DISC", "[CHR] Found 0x%04X, handle = %d\n-----------------", disc_chr->chars[d].uuid.uuid, disc_chr->chars[d].handle_value); + + // characteristic assign overload + chr[i]->_assign(&disc_chr->chars[d]); + + // only discover CCCD descriptor + if (disc_chr->chars[d].char_props.notify || disc_chr->chars[d].char_props.indicate ) + { + ble_gattc_handle_range_t range = { disc_chr->chars[d].handle_value + 1, _hdl_range.end_handle }; + + if ( range.start_handle <= range.end_handle ) + { + // skip if reaching end of range (last char has no descriptor)p + chr[i]->_discoverDescriptor(conn_handle, range); + } + } + + found++; + + break; + } + } + } + + // increase handle range for next discovery + // should be last descriptor +1, but that will cause missing on the next Characteristic !!!!! + // Reason is descriptor also include BLE_UUID_CHARACTERISTIC 0x2803 (Char declaration) in the result + // + // To be safe we use last chars + 1 + _hdl_range.start_handle = disc_chr->chars[ disc_chr->count-1 ].handle_value + 1; + } + + rtos_free(disc_chr); + + return found; +} + +uint16_t BLEDiscovery::_discoverDescriptor(uint16_t conn_handle, ble_gattc_evt_desc_disc_rsp_t* disc_desc, uint16_t bufsize, ble_gattc_handle_range_t hdl_range) +{ + LOG_LV2("DISC", "[DESC] Handle start = %d, end = %d", hdl_range.start_handle, hdl_range.end_handle); + + _adamsg.prepare(disc_desc, bufsize); + + VERIFY_STATUS( sd_ble_gattc_descriptors_discover(conn_handle, &hdl_range), 0 ); + + // wait for discovery event + int32_t bytecount = _adamsg.waitUntilComplete(BLE_DISCOVERY_TIMEOUT); + + // timeout or has no data (due to GATT Error) + if ( bytecount <= 0 ) return 0; + + for(uint16_t i=0; i<disc_desc->count; i++) + { + LOG_LV2("DISC", "[DESC] Descriptor %d: uuid = 0x%04X, handle = %d", i, disc_desc->descs[i].uuid.uuid, disc_desc->descs[i].handle); + } + + return disc_desc->count; +} + + +void BLEDiscovery::_event_handler(ble_evt_t* evt) +{ + ble_gattc_evt_t* gattc = &evt->evt.gattc_evt; + + switch ( evt->header.evt_id ) + { + case BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP: + { + ble_gattc_evt_prim_srvc_disc_rsp_t* svc_rsp = &gattc->params.prim_srvc_disc_rsp; + + LOG_LV2("DISC", "[SVC] Service Count: %d", svc_rsp->count); + + if (gattc->gatt_status == BLE_GATT_STATUS_SUCCESS) + { + // Only 1 service at a time + if (svc_rsp->count) + { + _adamsg.feed(svc_rsp, sizeof(ble_gattc_evt_prim_srvc_disc_rsp_t)); + } + }else + { + LOG_LV1("DISC", "[SVC] Gatt Status = 0x%04X", gattc->gatt_status); + } + + _adamsg.complete(); + } + break; + + case BLE_GATTC_EVT_CHAR_DISC_RSP: + { + ble_gattc_evt_char_disc_rsp_t* chr_rsp = &gattc->params.char_disc_rsp; + + LOG_LV2("DISC", "[CHR] Characteristic Count: %d", chr_rsp->count); + + if (gattc->gatt_status == BLE_GATT_STATUS_SUCCESS) + { + if ( chr_rsp->count ) + { + uint16_t len = sizeof(ble_gattc_evt_char_disc_rsp_t) + (chr_rsp->count-1)*sizeof(ble_gattc_char_t); + _adamsg.feed(chr_rsp, len); + } + }else + { + LOG_LV1("DISC", "[CHR] Gatt Status = 0x%04X", gattc->gatt_status); + } + + _adamsg.complete(); + } + break; + + case BLE_GATTC_EVT_DESC_DISC_RSP: + { + ble_gattc_evt_desc_disc_rsp_t* desc_rsp = &gattc->params.desc_disc_rsp; + + LOG_LV2("DISC", "[DESC] Descriptor Count: %d", desc_rsp->count); + + if (gattc->gatt_status == BLE_GATT_STATUS_SUCCESS) + { + if ( desc_rsp->count ) + { + uint16_t len = sizeof(ble_gattc_evt_desc_disc_rsp_t) + (desc_rsp->count-1)*sizeof(ble_gattc_desc_t); + _adamsg.feed(desc_rsp, len); + } + }else + { + LOG_LV1("DISC", "[DESC] Gatt Status = 0x%04X", gattc->gatt_status); + } + + _adamsg.complete(); + } + break; + + default: break; + } +} + + diff --git a/arduino/libraries/Bluefruit52Lib/src/BLEDiscovery.h b/arduino/libraries/Bluefruit52Lib/src/BLEDiscovery.h new file mode 100755 index 0000000..07d43c1 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/BLEDiscovery.h @@ -0,0 +1,115 @@ +/**************************************************************************/ +/*! + @file BLEDiscovery.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLEDISCOVERY_H_ +#define BLEDISCOVERY_H_ + +#include <Arduino.h> +#include "bluefruit_common.h" +#include "BLEClientCharacteristic.h" + +#include "BLEUuid.h" +#include "BLEClientService.h" + +#define BLE_DISCOVERY_TIMEOUT 1000 + +class BLEDiscovery +{ + private: + ble_gattc_handle_range_t _hdl_range; + AdaMsg _adamsg; + bool _begun; + + void _event_handler(ble_evt_t* evt); + + public: + BLEDiscovery(void); + + void begin(void); + bool begun(void); + + void setHandleRange(ble_gattc_handle_range_t handle_range); + ble_gattc_handle_range_t getHandleRange(void); + + uint8_t discoverCharacteristic(uint16_t conn_handle, BLEClientCharacteristic* chr[], uint8_t count); + + uint8_t discoverCharacteristic(uint16_t conn_handle, BLEClientCharacteristic& chr1) + { + BLEClientCharacteristic* chr_arr[] = {&chr1}; + return discoverCharacteristic(conn_handle, chr_arr, arrcount(chr_arr)); + } + + uint8_t discoverCharacteristic(uint16_t conn_handle, BLEClientCharacteristic& chr1, BLEClientCharacteristic& chr2) + { + BLEClientCharacteristic* chr_arr[] = {&chr1, &chr2}; + return discoverCharacteristic(conn_handle, chr_arr, arrcount(chr_arr)); + } + + uint8_t discoverCharacteristic(uint16_t conn_handle, BLEClientCharacteristic& chr1, BLEClientCharacteristic& chr2, BLEClientCharacteristic& chr3) + { + BLEClientCharacteristic* chr_arr[] = {&chr1, &chr2, &chr3}; + return discoverCharacteristic(conn_handle, chr_arr, arrcount(chr_arr)); + } + + uint8_t discoverCharacteristic(uint16_t conn_handle, BLEClientCharacteristic& chr1, BLEClientCharacteristic& chr2, BLEClientCharacteristic& chr3, BLEClientCharacteristic& chr4) + { + BLEClientCharacteristic* chr_arr[] = {&chr1, &chr2, &chr3, &chr4}; + return discoverCharacteristic(conn_handle, chr_arr, arrcount(chr_arr)); + } + + uint8_t discoverCharacteristic(uint16_t conn_handle, BLEClientCharacteristic& chr1, BLEClientCharacteristic& chr2, BLEClientCharacteristic& chr3, BLEClientCharacteristic& chr4, BLEClientCharacteristic& chr5) + { + BLEClientCharacteristic* chr_arr[] = {&chr1, &chr2, &chr3, &chr4, &chr5}; + return discoverCharacteristic(conn_handle, chr_arr, arrcount(chr_arr)); + } + + uint8_t discoverCharacteristic(uint16_t conn_handle, BLEClientCharacteristic& chr1, BLEClientCharacteristic& chr2, BLEClientCharacteristic& chr3, BLEClientCharacteristic& chr4, BLEClientCharacteristic& chr5, BLEClientCharacteristic& chr6) + { + BLEClientCharacteristic* chr_arr[] = {&chr1, &chr2, &chr3, &chr4, &chr5, &chr6}; + return discoverCharacteristic(conn_handle, chr_arr, arrcount(chr_arr)); + } + + /*------------------------------------------------------------------*/ + /* INTERNAL USAGE ONLY + * Although declare as public, it is meant to be invoked by internal + * code. User should not call these directly + *------------------------------------------------------------------*/ + bool _discoverService(uint16_t conn_handle, BLEClientService& svc, uint16_t start_handle = 1); + uint16_t _discoverDescriptor(uint16_t conn_handle, ble_gattc_evt_desc_disc_rsp_t* disc_desc, uint16_t bufsize, ble_gattc_handle_range_t hdl_range); + + friend class AdafruitBluefruit; +}; + +#endif /* BLEDISCOVERY_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/BLEGap.cpp b/arduino/libraries/Bluefruit52Lib/src/BLEGap.cpp new file mode 100755 index 0000000..61aa83d --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/BLEGap.cpp @@ -0,0 +1,501 @@ +/**************************************************************************/ +/*! + @file BLEGap.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" + +BLEGap::BLEGap(void) +{ + memclr(_peers, sizeof(_peers)); + + _cfg_prph.mtu_max = BLE_GATT_ATT_MTU_DEFAULT; + _cfg_central.mtu_max = BLE_GATT_ATT_MTU_DEFAULT; + + _cfg_prph.event_len = BLE_GAP_EVENT_LENGTH_DEFAULT; + _cfg_prph.hvn_tx_qsize = BLE_GATTS_HVN_TX_QUEUE_SIZE_DEFAULT; + _cfg_prph.wr_cmd_qsize = BLE_GATTC_WRITE_CMD_TX_QUEUE_SIZE_DEFAULT; + + _cfg_central.event_len = BLE_GAP_EVENT_LENGTH_DEFAULT; + _cfg_central.hvn_tx_qsize = BLE_GATTS_HVN_TX_QUEUE_SIZE_DEFAULT; + _cfg_central.wr_cmd_qsize = BLE_GATTC_WRITE_CMD_TX_QUEUE_SIZE_DEFAULT; + + _sec_param = (ble_gap_sec_params_t) + { + .bond = 1, + .mitm = 0, + .lesc = 0, + .keypress = 0, + .io_caps = BLE_GAP_IO_CAPS_NONE, + .oob = 0, + .min_key_size = 7, + .max_key_size = 16, + .kdist_own = { .enc = 1, .id = 1}, + .kdist_peer = { .enc = 1, .id = 1}, + }; +} + + +void BLEGap::configPrphConn(uint16_t mtu_max, uint8_t event_len, uint8_t hvn_qsize, uint8_t wrcmd_qsize) +{ + _cfg_prph.mtu_max = maxof(mtu_max, BLE_GATT_ATT_MTU_DEFAULT); + _cfg_prph.event_len = maxof(event_len, BLE_GAP_EVENT_LENGTH_MIN); + _cfg_prph.hvn_tx_qsize = hvn_qsize; + _cfg_prph.wr_cmd_qsize = wrcmd_qsize; +} + +void BLEGap::configCentralConn(uint16_t mtu_max, uint8_t event_len, uint8_t hvn_qsize, uint8_t wrcmd_qsize) +{ + _cfg_central.mtu_max = maxof(mtu_max, BLE_GATT_ATT_MTU_DEFAULT); + _cfg_central.event_len = maxof(event_len, BLE_GAP_EVENT_LENGTH_MIN); + _cfg_central.hvn_tx_qsize = hvn_qsize; + _cfg_central.wr_cmd_qsize = wrcmd_qsize; +} + + +uint16_t BLEGap::getMaxMtuByConnCfg(uint8_t conn_cfg) +{ + return (conn_cfg == CONN_CFG_PERIPHERAL) ? _cfg_prph.mtu_max : _cfg_central.mtu_max; +} + +uint16_t BLEGap::getMaxMtu (uint8_t conn_hdl) +{ + return (getRole(conn_hdl) == BLE_GAP_ROLE_PERIPH) ? _cfg_prph.mtu_max : _cfg_central.mtu_max; +} + +uint8_t BLEGap::getHvnQueueSize (uint8_t conn_hdl) +{ + return (getRole(conn_hdl) == BLE_GAP_ROLE_PERIPH) ? _cfg_prph.hvn_tx_qsize : _cfg_central.hvn_tx_qsize; +} + +uint8_t BLEGap::getWriteCmdQueueSize (uint8_t conn_hdl) +{ + return (getRole(conn_hdl) == BLE_GAP_ROLE_PERIPH) ? _cfg_prph.wr_cmd_qsize : _cfg_central.wr_cmd_qsize; +} + + +/** + * Get current Mac address and its type + * @param mac address + * @return Address type e.g BLE_GAP_ADDR_TYPE_RANDOM_STATIC + */ +uint8_t BLEGap::getAddr(uint8_t mac[6]) +{ + ble_gap_addr_t addr; + + sd_ble_gap_addr_get(&addr); + memcpy(mac, addr.addr, 6); + + return addr.addr_type; +} + +/** + * Set the MAC address + * @param mac Bluetooth MAC Address + * @param type Must be either BLE_GAP_ADDR_TYPE_PUBLIC or BLE_GAP_ADDR_TYPE_RANDOM_STATIC + * @return true if success + */ +bool BLEGap::setAddr(uint8_t mac[6], uint8_t type) +{ + ble_gap_addr_t addr; + addr.addr_type = type; + + memcpy(addr.addr, mac, 6); + VERIFY_STATUS( sd_ble_gap_addr_set(&addr), false ); + + return true; +} + +bool BLEGap::connected(uint16_t conn_hdl) +{ + return _peers[conn_hdl].connected; +} + +bool BLEGap::paired(uint16_t conn_hdl) +{ + return _peers[conn_hdl].paired; +} + +bool BLEGap::requestPairing(uint16_t conn_hdl) +{ + gap_peer_t* peer = &_peers[conn_hdl]; + + // skip if already paired + if ( peer->paired ) return true; + + uint16_t cntr_ediv = 0xFFFF; + + if ( peer->role == BLE_GAP_ROLE_CENTRAL ) + { + // Check to see if we did bonded with current prph previously + bond_keys_t bkeys; + + if ( bond_find_cntr(&peer->addr, &bkeys) ) + { + cntr_ediv = bkeys.peer_enc.master_id.ediv; + LOG_LV2("BOND", "Load Keys from file " BOND_FNAME_CNTR, cntr_ediv); + VERIFY_STATUS( sd_ble_gap_encrypt(conn_hdl, &bkeys.peer_enc.master_id, &bkeys.peer_enc.enc_info), false); + + }else + { + VERIFY_STATUS( sd_ble_gap_authenticate(conn_hdl, &_sec_param ), false); + } + }else + { + VERIFY_STATUS( sd_ble_gap_authenticate(conn_hdl, &_sec_param ), false); + } + + // Wait for pairing process using on-the-fly semaphore + peer->pair_sem = xSemaphoreCreateBinary(); + + xSemaphoreTake(peer->pair_sem, portMAX_DELAY); + + // Failed to pair using central stored keys, this happens when + // Prph delete bonds while we did not --> let's remove the obsolete keyfile and move on + if ( !peer->paired && (cntr_ediv != 0xffff) ) + { + bond_remove_key(BLE_GAP_ROLE_CENTRAL, cntr_ediv); + + // Re-try with a fresh session + VERIFY_STATUS( sd_ble_gap_authenticate(conn_hdl, &_sec_param ), false); + + xSemaphoreTake(peer->pair_sem, portMAX_DELAY); + } + + vSemaphoreDelete(peer->pair_sem); + peer->pair_sem = NULL; + + return peer->paired; +} + +uint8_t BLEGap::getRole(uint16_t conn_hdl) +{ + return _peers[conn_hdl].role; +} + +uint8_t BLEGap::getPeerAddr(uint16_t conn_hdl, uint8_t addr[6]) +{ + memcpy(addr, _peers[conn_hdl].addr.addr, BLE_GAP_ADDR_LEN); + return _peers[conn_hdl].addr.addr_type; +} + +ble_gap_addr_t BLEGap::getPeerAddr(uint16_t conn_hdl) +{ + return _peers[conn_hdl].addr; +} + +bool BLEGap::getHvnPacket(uint16_t conn_hdl) +{ + VERIFY( (conn_hdl < BLE_MAX_CONN) && (_peers[conn_hdl].hvn_tx_sem != NULL) ); + + return xSemaphoreTake(_peers[conn_hdl].hvn_tx_sem, ms2tick(BLE_GENERIC_TIMEOUT)); +} + +bool BLEGap::getWriteCmdPacket(uint16_t conn_hdl) +{ + VERIFY( (conn_hdl < BLE_MAX_CONN) && (_peers[conn_hdl].wrcmd_tx_sem != NULL) ); + return xSemaphoreTake(_peers[conn_hdl].wrcmd_tx_sem, ms2tick(BLE_GENERIC_TIMEOUT)); +} + +uint16_t BLEGap::getMTU (uint16_t conn_hdl) +{ + return _peers[conn_hdl].att_mtu; +} + +uint16_t BLEGap::getPeerName(uint16_t conn_hdl, char* buf, uint16_t bufsize) +{ + return Bluefruit.Gatt.readCharByUuid(conn_hdl, BLEUuid(BLE_UUID_GAP_CHARACTERISTIC_DEVICE_NAME), buf, bufsize); +} + +/** + * Event handler + * @param evt + */ +void BLEGap::_eventHandler(ble_evt_t* evt) +{ + // conn handle has fixed offset regardless of event type + const uint16_t conn_hdl = evt->evt.common_evt.conn_handle; + + gap_peer_t* peer = (conn_hdl == BLE_CONN_HANDLE_INVALID) ? NULL : &_peers[conn_hdl]; + + switch(evt->header.evt_id) + { + case BLE_GAP_EVT_CONNECTED: + { + ble_gap_evt_connected_t const * para = &evt->evt.gap_evt.params.connected; + + peer->connected = true; + peer->role = para->role; + peer->addr = para->peer_addr; + peer->att_mtu = BLE_GATT_ATT_MTU_DEFAULT; + + // Init transmission buffer for notification + peer->hvn_tx_sem = xSemaphoreCreateCounting(getHvnQueueSize(conn_hdl), getHvnQueueSize(conn_hdl)); + peer->wrcmd_tx_sem = xSemaphoreCreateCounting(getWriteCmdQueueSize(conn_hdl), getWriteCmdQueueSize(conn_hdl)); + + LOG_LV2("GAP", "Conn Interval= %f", para->conn_params.min_conn_interval*1.25f); + } + break; + + case BLE_GAP_EVT_DISCONNECTED: + { + ble_gap_evt_disconnected_t const* para = &evt->evt.gap_evt.params.disconnected; + + // mark as disconnected, but keep the role for sub sequence event handler + peer->connected = peer->paired = false; + + vSemaphoreDelete( peer->hvn_tx_sem ); + peer->hvn_tx_sem = NULL; + + vSemaphoreDelete( peer->wrcmd_tx_sem ); + peer->wrcmd_tx_sem = NULL; + } + break; + + case BLE_GAP_EVT_CONN_PARAM_UPDATE: + { + ble_gap_conn_params_t* param = &evt->evt.gap_evt.params.conn_param_update.conn_params; + LOG_LV2("GAP", "Conn Interval= %f", param->min_conn_interval*1.25f); + } + break; + + case BLE_GAP_EVT_SEC_PARAMS_REQUEST: + { + // Pairing in progress, Peer asking for our info + peer->bond_keys = (bond_keys_t*) rtos_malloc( sizeof(bond_keys_t)); + VERIFY(peer->bond_keys, ); + + bond_keys_t* bkeys = peer->bond_keys; + memclr(bkeys, sizeof(bond_keys_t)); + + peer->ediv = 0xFFFF; // invalid value for ediv + + /* Step 1: Pairing/Bonding + * - Central supplies its parameters + * - We replies with our security parameters + */ + // ble_gap_sec_params_t* peer = &evt->evt.gap_evt.params.sec_params_request.peer_params; + COMMENT_OUT( + // Change security parameter according to authentication type + if ( _auth_type == BLE_GAP_AUTH_KEY_TYPE_PASSKEY) + { + sec_para.mitm = 1; + sec_para.io_caps = BLE_GAP_IO_CAPS_DISPLAY_ONLY; + } + ) + + ble_gap_sec_keyset_t keyset = + { + .keys_own = { + .p_enc_key = &bkeys->own_enc, + .p_id_key = NULL, + .p_sign_key = NULL, + .p_pk = NULL + }, + + .keys_peer = { + .p_enc_key = &bkeys->peer_enc, + .p_id_key = &bkeys->peer_id, + .p_sign_key = NULL, + .p_pk = NULL + } + }; + + VERIFY_STATUS(sd_ble_gap_sec_params_reply(conn_hdl, + BLE_GAP_SEC_STATUS_SUCCESS, + peer->role == BLE_GAP_ROLE_PERIPH ? &_sec_param : NULL, + &keyset), + ); + } + break; + + case BLE_GAP_EVT_AUTH_STATUS: + { + // Pairing process completed + ble_gap_evt_auth_status_t* status = &evt->evt.gap_evt.params.auth_status; + + // Pairing succeeded --> save encryption keys ( Bonding ) + if (BLE_GAP_SEC_STATUS_SUCCESS == status->auth_status) + { + peer->paired = true; + peer->ediv = peer->bond_keys->own_enc.master_id.ediv; + + bond_save_keys(peer->role, conn_hdl, peer->bond_keys); + }else + { + PRINT_HEX(status->auth_status); + } + + rtos_free(peer->bond_keys); + peer->bond_keys = NULL; + } + break; + + case BLE_GAP_EVT_SEC_INFO_REQUEST: + { + // Central ask for the stored keys. + // - load key and return if bonded previously. + // - Else return NULL --> Initiate key exchange + ble_gap_evt_sec_info_request_t* sec_req = (ble_gap_evt_sec_info_request_t*) &evt->evt.gap_evt.params.sec_info_request; + + bond_keys_t bkeys; + varclr(&bkeys); + + if ( bond_load_keys(peer->role, sec_req->master_id.ediv, &bkeys) ) + { + sd_ble_gap_sec_info_reply(evt->evt.gap_evt.conn_handle, &bkeys.own_enc.enc_info, &bkeys.peer_id.id_info, NULL); + + peer->ediv = bkeys.own_enc.master_id.ediv; + } else + { + sd_ble_gap_sec_info_reply(evt->evt.gap_evt.conn_handle, NULL, NULL, NULL); + } + } + break; + + case BLE_GAP_EVT_CONN_SEC_UPDATE: + { + const ble_gap_conn_sec_t* conn_sec = &evt->evt.gap_evt.params.conn_sec_update.conn_sec; + + // Connection is secured (paired) + // Occurs if bonded + reconnection, or we initiate the pairing process + if ( !( conn_sec->sec_mode.sm == 1 && conn_sec->sec_mode.lv == 1) ) + { + // Previously bonded --> secure by re-connection process --> Load & Set SysAttr (Apply Service Context) + // Else Init SysAttr (first bonded) + if ( !bond_load_cccd(peer->role, conn_hdl, peer->ediv) ) + { + sd_ble_gatts_sys_attr_set(conn_hdl, NULL, 0, 0); + } + + peer->paired = true; + } + + if (peer->pair_sem) xSemaphoreGive(peer->pair_sem); + } + break; + + case BLE_GAP_EVT_PASSKEY_DISPLAY: + { + // ble_gap_evt_passkey_display_t const* passkey_display = &evt->evt.gap_evt.params.passkey_display; + // PRINT_INT(passkey_display->match_request); + // PRINT_BUFFER(passkey_display->passkey, 6); + + // sd_ble_gap_auth_key_reply + } + break; + + case BLE_GATTS_EVT_HVN_TX_COMPLETE: + if ( peer->hvn_tx_sem ) + { + for(uint8_t i=0; i<evt->evt.gatts_evt.params.hvn_tx_complete.count; i++) + { + xSemaphoreGive(peer->hvn_tx_sem); + } + } + break; + + case BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE: + if ( peer->wrcmd_tx_sem ) + { + for(uint8_t i=0; i<evt->evt.gattc_evt.params.write_cmd_tx_complete.count; i++) + { + xSemaphoreGive(peer->wrcmd_tx_sem); + } + } + break; + + case BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST: + { + ble_gap_data_length_params_t* param = &evt->evt.gap_evt.params.data_length_update_request.peer_params; + LOG_LV2("GAP", "Data Length Req is (tx, rx) octets = (%d, %d), (tx, rx) time = (%d, %d) us", + param->max_tx_octets, param->max_rx_octets, param->max_tx_time_us, param->max_rx_time_us); + + // Let Softdevice decide the data length + VERIFY_STATUS( sd_ble_gap_data_length_update(conn_hdl, NULL, NULL), ); + } + break; + + case BLE_GAP_EVT_DATA_LENGTH_UPDATE: + { + ble_gap_data_length_params_t* datalen = &evt->evt.gap_evt.params.data_length_update.effective_params; + LOG_LV2("GAP", "Data Length is (tx, rx) octets = (%d, %d), (tx, rx) time = (%d, %d) us", + datalen->max_tx_octets, datalen->max_rx_octets, datalen->max_tx_time_us, datalen->max_rx_time_us); + } + break; + + case BLE_GAP_EVT_PHY_UPDATE_REQUEST: + { + ble_gap_phys_t* req_phy = &evt->evt.gap_evt.params.phy_update_request.peer_preferred_phys; + + #if CFG_DEBUG >= 1 + char const *phy_str[] = { "Auto", "1 Mbps", "2 Mbps", "Coded" }; + LOG_LV1("GAP", "PHY request tx: %s, rx: %s", phy_str[req_phy->tx_phys], phy_str[req_phy->rx_phys]); + #endif + + // Tell SoftDevice to choose PHY automatically + ble_gap_phys_t phy = { BLE_GAP_PHY_AUTO, BLE_GAP_PHY_AUTO }; + (void) sd_ble_gap_phy_update(conn_hdl, &phy); + } + break; + + case BLE_GAP_EVT_PHY_UPDATE: + { + ble_gap_evt_phy_update_t* active_phy = &evt->evt.gap_evt.params.phy_update; + + #if CFG_DEBUG >= 1 + if ( active_phy->status != BLE_HCI_STATUS_CODE_SUCCESS ) + { + LOG_LV1("GAP", "Failed HCI status = 0x%02X", active_phy->status); + }else + { + char const *phy_str[] = { "Auto", "1 Mbps", "2 Mbps", "Coded" }; + LOG_LV1("GAP", "PHY active tx: %s, rx: %s", phy_str[active_phy->tx_phy], phy_str[active_phy->rx_phy]); + } + #endif + } + break; + + case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST: + { + peer->att_mtu = minof(evt->evt.gatts_evt.params.exchange_mtu_request.client_rx_mtu, getMaxMtu(conn_hdl)); + VERIFY_STATUS( sd_ble_gatts_exchange_mtu_reply(conn_hdl, peer->att_mtu), ); + + LOG_LV1("GAP", "ATT MTU is changed to %d", peer->att_mtu); + } + break; + + default: break; + } +} diff --git a/arduino/libraries/Bluefruit52Lib/src/BLEGap.h b/arduino/libraries/Bluefruit52Lib/src/BLEGap.h new file mode 100755 index 0000000..3b6fa8a --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/BLEGap.h @@ -0,0 +1,132 @@ +/**************************************************************************/ +/*! + @file BLEGap.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLEGAP_H_ +#define BLEGAP_H_ + +#include <Arduino.h> +#include "bluefruit_common.h" +#include "BLEUuid.h" +#include "utility/bonding.h" + +enum +{ + CONN_CFG_PERIPHERAL = 1, + CONN_CFG_CENTRAL = 2, +}; + +class BLEGap +{ + public: + typedef void (*connect_callback_t ) (uint16_t conn_hdl); + typedef void (*disconnect_callback_t ) (uint16_t conn_hdl, uint8_t reason); + + BLEGap(void); + + uint8_t getAddr (uint8_t mac[6]); + bool setAddr (uint8_t mac[6], uint8_t type); +// bool setPrivacy (); sd_ble_gap_privacy_set() + + bool connected (uint16_t conn_hdl); + bool paired (uint16_t conn_hdl); + bool requestPairing (uint16_t conn_hdl); + + uint8_t getRole (uint16_t conn_hdl); + + uint8_t getPeerAddr (uint16_t conn_hdl, uint8_t addr[6]); + ble_gap_addr_t getPeerAddr (uint16_t conn_hdl); + uint16_t getPeerName (uint16_t conn_hdl, char* buf, uint16_t bufsize); + + uint16_t getMTU (uint16_t conn_hdl); + uint16_t getMaxMtuByConnCfg (uint8_t conn_cfg); + uint16_t getMaxMtu (uint8_t conn_hdl); + + uint8_t getHvnQueueSize (uint8_t conn_hdl); + uint8_t getWriteCmdQueueSize (uint8_t conn_hdl); + + bool getHvnPacket (uint16_t conn_hdl); + bool getWriteCmdPacket (uint16_t conn_hdl); + + void configPrphConn (uint16_t mtu_max, uint8_t event_len, uint8_t hvn_qsize, uint8_t wrcmd_qsize); + void configCentralConn (uint16_t mtu_max, uint8_t event_len, uint8_t hvn_qsize, uint8_t wrcmd_qsize); + + /*------------------------------------------------------------------*/ + /* INTERNAL USAGE ONLY + * Although declare as public, it is meant to be invoked by internal + * code. User should not call these directly + *------------------------------------------------------------------*/ + void _eventHandler(ble_evt_t* evt); + + // Array of TX Packet semaphore, indexed by connection handle + // Peer info where conn_hdl serves as index + typedef struct { + bool connected; + bool paired; + uint8_t role; + uint16_t att_mtu; + + ble_gap_addr_t addr; + + uint16_t ediv; + bond_keys_t* bond_keys; // Shared keys with bonded device, size ~ 80 bytes + + SemaphoreHandle_t hvn_tx_sem; + SemaphoreHandle_t wrcmd_tx_sem; + + bool hvc_received; + + // On-demand semaphore that are created on the fly + SemaphoreHandle_t hvc_sem; + SemaphoreHandle_t pair_sem; + } gap_peer_t; + + gap_peer_t* _get_peer(uint16_t conn_hdl) { return &_peers[conn_hdl]; } + + private: + struct { + uint16_t mtu_max; + uint8_t event_len; + uint8_t hvn_tx_qsize; + uint8_t wr_cmd_qsize; + } _cfg_prph, _cfg_central; + + gap_peer_t _peers[BLE_MAX_CONN]; + + ble_gap_sec_params_t _sec_param; + + friend class AdafruitBluefruit; +}; + +#endif /* BLEGAP_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/BLEGatt.cpp b/arduino/libraries/Bluefruit52Lib/src/BLEGatt.cpp new file mode 100755 index 0000000..0617713 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/BLEGatt.cpp @@ -0,0 +1,329 @@ +/**************************************************************************/ +/*! + @file BLEGatt.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" +#include "utility/bonding.h" + + +BLEGatt::BLEGatt(void) + : _adamsg() +{ + varclr(&_server); + varclr(&_client); +} + +uint16_t BLEGatt::readCharByUuid(uint16_t conn_hdl, BLEUuid bleuuid, void* buffer, uint16_t bufsize, uint16_t start_hdl, uint16_t end_hdl) +{ + int32_t count = 0; + ble_gattc_handle_range_t hdl_range = { .start_handle = start_hdl, .end_handle = end_hdl }; + + while( _adamsg.isWaiting() ) + { + // TODO multiple peripherals + delay( Bluefruit.connInterval() ); + } + + _adamsg.begin(true); + _adamsg.prepare(buffer, bufsize); + + err_t err = sd_ble_gattc_char_value_by_uuid_read(conn_hdl, &bleuuid._uuid, &hdl_range); + + if( NRF_SUCCESS == err ) + { + // Read by uuid could take long if the uuid handle is far from start handle + count = _adamsg.waitUntilComplete(5*BLE_GENERIC_TIMEOUT); + }else + { + VERIFY_MESS(err, dbg_err_str); + } + _adamsg.stop(); + + + return (count < 0) ? 0 : count; +} + +bool BLEGatt::waitForIndicateConfirm(uint16_t conn_hdl) +{ + BLEGap::gap_peer_t* peer = Bluefruit.Gap._get_peer(conn_hdl); + + // hvi confirm semaphore is created on the fly + peer->hvc_sem = xSemaphoreCreateBinary(); + + xSemaphoreTake(peer->hvc_sem, portMAX_DELAY); + + vSemaphoreDelete(peer->hvc_sem); + peer->hvc_sem = NULL; + + return peer->hvc_received; +} + +void BLEGatt::_eventHandler(ble_evt_t* evt) +{ + // conn handle has fixed offset regardless of event type + const uint16_t evt_conn_hdl = evt->evt.common_evt.conn_handle; + const uint16_t evt_id = evt->header.evt_id; + + /*------------- Server service -------------*/ + // TODO multiple peripherals +// if ( evt_conn_hdl == Bluefruit.connHandle() ) + { + if ( evt_id == BLE_GAP_EVT_DISCONNECTED || evt_id == BLE_GAP_EVT_CONNECTED ) + { + for(uint8_t i=0; i<_server.svc_count; i++) + { + if ( evt_id == BLE_GAP_EVT_DISCONNECTED ) + { + _server.svc_list[i]->_disconnect_cb(); + }else + { + _server.svc_list[i]->_connect_cb(); + } + } + } + } + + /*------------- Server Characteristics -------------*/ + // TODO multiple prph connection + if ( evt_conn_hdl == Bluefruit.connHandle() ) + { + for(uint8_t i=0; i<_server.chr_count; i++) + { + BLECharacteristic* chr = _server.chr_list[i]; + uint16_t req_handle = BLE_GATT_HANDLE_INVALID; + + // BLE_GATTS_OP_EXEC_WRITE_REQ_NOW has no handle, uuid only command op + bool exec_write_now = false; + + switch (evt_id) + { + case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST: + { + // Handle has the same offset for read & write request + req_handle = evt->evt.gatts_evt.params.authorize_request.request.read.handle; + + if ( evt->evt.gatts_evt.params.authorize_request.type == BLE_GATTS_AUTHORIZE_TYPE_WRITE ) + { + ble_gatts_evt_write_t * wr_req = &evt->evt.gatts_evt.params.authorize_request.request.write; + if ( wr_req->op == BLE_GATTS_OP_EXEC_WRITE_REQ_NOW ) + { + exec_write_now = true; + } + } + } + break; + + case BLE_GATTS_EVT_WRITE: + req_handle = evt->evt.gatts_evt.params.write.handle; + break; + + default: break; + } + + // invoke characteristic handler if matched + if ( exec_write_now || + ((req_handle != BLE_GATT_HANDLE_INVALID) && (req_handle == chr->handles().value_handle || req_handle == chr->handles().cccd_handle)) ) + { + chr->_eventHandler(evt); + + // Save CCCD if paired + if ( Bluefruit.connPaired() && (evt_id == BLE_GATTS_EVT_WRITE) && (req_handle == chr->handles().cccd_handle) ) + { + BLEGap::gap_peer_t* peer = Bluefruit.Gap._get_peer(evt_conn_hdl); + bond_save_cccd( peer->role, evt_conn_hdl, peer->ediv); + } + } + } + } + + /*------------- Client Characteristics -------------*/ + for(int i=0; i<_client.chr_count; i++) + { + if ( evt_conn_hdl == _client.chr_list[i]->connHandle() ) + { + BLEClientCharacteristic* chr = _client.chr_list[i]; + uint16_t req_handle = BLE_GATT_HANDLE_INVALID; + + switch(evt_id) + { + case BLE_GATTC_EVT_HVX: + req_handle = evt->evt.gattc_evt.params.hvx.handle; + break; + + case BLE_GATTC_EVT_WRITE_RSP: + req_handle = evt->evt.gattc_evt.params.write_rsp.handle; + break; + + case BLE_GATTC_EVT_READ_RSP: + req_handle = evt->evt.gattc_evt.params.read_rsp.handle; + break; + + default: break; + } + + // invoke characteristic handler if matched + if ( (req_handle != BLE_GATT_HANDLE_INVALID) && (chr->valueHandle() == req_handle) ) + { + chr->_eventHandler(evt); + } + } + } + + // disconnect Client Services & Characteristics of the disconnected handle + if ( evt_id == BLE_GAP_EVT_DISCONNECTED ) + { + // Client + for(uint8_t i=0; i<_client.svc_count; i++) + { + if ( evt_conn_hdl == _client.svc_list[i]->_conn_hdl) + { + _client.svc_list[i]->disconnect(); + } + } + + // TODO merge to above loop + for(uint8_t i=0; i<_client.chr_count; i++) + { + if ( evt_conn_hdl == _client.chr_list[i]->connHandle() ) + { + _client.chr_list[i]->disconnect(); + } + } + } + + // GATTC Read Characteristic by UUID procedure + switch ( evt_id ) + { + case BLE_GATTC_EVT_CHAR_VAL_BY_UUID_READ_RSP: + { + ble_gattc_evt_char_val_by_uuid_read_rsp_t* rd_rsp = &evt->evt.gattc_evt.params.char_val_by_uuid_read_rsp; + + if (rd_rsp->count) + { + ble_gattc_handle_value_t hdl_value; + + if ( ERROR_NONE == sd_ble_gattc_evt_char_val_by_uuid_read_rsp_iter(&evt->evt.gattc_evt, &hdl_value) ) + { + _adamsg.feed(hdl_value.p_value, rd_rsp->value_len); + } + + _adamsg.complete(); + } + } + break; + + case BLE_GATTS_EVT_HVC: + { + LOG_LV2("GATTS", "Confirm received handle = 0x%04X", evt->evt.gatts_evt.params.hvc.handle); + BLEGap::gap_peer_t* peer = Bluefruit.Gap._get_peer(evt_conn_hdl); + + if ( peer->hvc_sem ) xSemaphoreGive(peer->hvc_sem); + peer->hvc_received = true; + } + break; + + case BLE_GATTS_EVT_TIMEOUT: + { + LOG_LV2("GATTS", "Timeout Source = %d", evt->evt.gatts_evt.params.timeout.src); + + BLEGap::gap_peer_t* peer = Bluefruit.Gap._get_peer(evt_conn_hdl); + + if ( peer->hvc_sem ) xSemaphoreGive(peer->hvc_sem); + peer->hvc_received = false; + } + break; + + default: break; + } +} + +/*------------------------------------------------------------------*/ +/* Server + *------------------------------------------------------------------*/ +bool BLEGatt::_addCharacteristic(BLECharacteristic* chr) +{ + if ( _server.chr_count == CFG_GATT_MAX_SERVER_CHARS ) return false; + _server.chr_list[ _server.chr_count++ ] = chr; + + return true; +} + +bool BLEGatt::_addService(BLEService* svc) +{ + VERIFY( _server.svc_count < CFG_GATT_MAX_SERVER_SERVICE ); + _server.svc_list[ _server.svc_count++ ] = svc; + + return true; +} + +/*------------------------------------------------------------------*/ +/* Client + *------------------------------------------------------------------*/ +void BLEGatt::_removeCharacteristic(BLEClientCharacteristic* chr) +{ + for(int i=0; i<_client.chr_count; i++) + { + // found the char, swap with the last one + if ( _client.chr_list[i] == chr ) + { + vTaskSuspendAll(); + + _client.chr_count--; + + _client.chr_list[i] = _client.chr_list[ _client.chr_count ]; + _client.chr_list[_client.chr_count] = NULL; + + ( void ) xTaskResumeAll(); + + break; + } + } +} + +bool BLEGatt::_addCharacteristic(BLEClientCharacteristic* chr) +{ + VERIFY( _client.chr_count < CFG_GATT_MAX_CLIENT_CHARS ); + _client.chr_list[ _client.chr_count++ ] = chr; + + return true; +} + +bool BLEGatt::_addService(BLEClientService* svc) +{ + VERIFY( _client.svc_count < CFG_GATT_MAX_CLIENT_SERVICE ); + _client.svc_list[ _client.svc_count++ ] = svc; + + return true; +} diff --git a/arduino/libraries/Bluefruit52Lib/src/BLEGatt.h b/arduino/libraries/Bluefruit52Lib/src/BLEGatt.h new file mode 100755 index 0000000..23df636 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/BLEGatt.h @@ -0,0 +1,101 @@ +/**************************************************************************/ +/*! + @file BLEGatt.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLEGATT_H_ +#define BLEGATT_H_ + +#include <Arduino.h> +#include "bluefruit_common.h" + +#include "BLEUuid.h" +#include "BLECharacteristic.h" +#include "BLEClientCharacteristic.h" +#include "BLEService.h" + +#include "BLEClientService.h" + +#define CFG_GATT_MAX_SERVER_SERVICE 20 +#define CFG_GATT_MAX_SERVER_CHARS 40 + +#define CFG_GATT_MAX_CLIENT_SERVICE 20 +#define CFG_GATT_MAX_CLIENT_CHARS 40 + + +class BLEGatt +{ + public: + BLEGatt(void); + + uint16_t readCharByUuid(uint16_t conn_hdl, BLEUuid bleuuid, void* buffer, uint16_t bufsize, uint16_t start_hdl = 1, uint16_t end_hdl = 0xffff); + + bool waitForIndicateConfirm(uint16_t conn_hdl); + + /*------------------------------------------------------------------*/ + /* INTERNAL USAGE ONLY + * Although declare as public, it is meant to be invoked by internal + * code. User should not call these directly + *------------------------------------------------------------------*/ + // Server + bool _addCharacteristic(BLECharacteristic* chr); + bool _addService(BLEService* svc); + + // Client + bool _addCharacteristic(BLEClientCharacteristic* chr); + void _removeCharacteristic(BLEClientCharacteristic* chr); + bool _addService(BLEClientService* svc); + + void _eventHandler(ble_evt_t* evt); + + private: + struct { + uint8_t svc_count; + BLEService* svc_list[CFG_GATT_MAX_SERVER_SERVICE]; + + uint8_t chr_count; + BLECharacteristic* chr_list[CFG_GATT_MAX_SERVER_CHARS]; + } _server; + + struct { + uint8_t svc_count; + BLEClientService* svc_list[CFG_GATT_MAX_CLIENT_SERVICE]; + + uint8_t chr_count; + BLEClientCharacteristic* chr_list[CFG_GATT_MAX_CLIENT_CHARS]; + }_client; + + AdaMsg _adamsg; +}; + +#endif /* BLEGATT_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/BLEScanner.cpp b/arduino/libraries/Bluefruit52Lib/src/BLEScanner.cpp new file mode 100755 index 0000000..1ac7366 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/BLEScanner.cpp @@ -0,0 +1,427 @@ +/**************************************************************************/ +/*! + @file BLEScanner.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" + +BLEScanner::BLEScanner(void) +{ + _report_data.p_data = _scan_data; + _report_data.len = BLE_GAP_SCAN_BUFFER_MAX; + + _runnning = false; + _start_if_disconnect = true; + + _filter_rssi = INT8_MIN; + + _filter_msd_en = false; + _filter_msd_id = 0; // Irrelevant + + _filter_uuid_count = 0; + _filter_uuid = NULL; + + _rx_cb = NULL; + _stop_cb = NULL; + + _param = ((ble_gap_scan_params_t) { + // TODO Extended Adv on secondary channels + .extended = 0, + .report_incomplete_evts = 0, + + .active = 0, + .filter_policy = BLE_GAP_SCAN_FP_ACCEPT_ALL, + .scan_phys = BLE_GAP_PHY_AUTO, + + .interval = BLE_SCAN_INTERVAL_DFLT, + .window = BLE_SCAN_WINDOW_DFLT, + .timeout = 0, // no timeout, in 10 ms units + .channel_mask = { 0, 0, 0, 0, 0 } + }); +} + +void BLEScanner::useActiveScan(bool enable) +{ + _param.active = enable; +} + +void BLEScanner::setInterval(uint16_t interval, uint16_t window) +{ + _param.interval = interval; + _param.window = window; +} + +void BLEScanner::setIntervalMS(uint16_t interval, uint16_t window) +{ + _param.interval = MS1000TO625(interval); + _param.window = MS1000TO625(window); +} + +bool BLEScanner::isRunning(void) +{ + return _runnning; +} + +void BLEScanner::setRxCallback(rx_callback_t fp) +{ + _rx_cb = fp; +} + +void BLEScanner::setStopCallback(stop_callback_t fp) +{ + _stop_cb = fp; +} + +void BLEScanner::restartOnDisconnect(bool enable) +{ + _start_if_disconnect = enable; +} + +ble_gap_scan_params_t* BLEScanner::getParams(void) +{ + return &_param; +} + +bool BLEScanner::start(uint16_t timeout) +{ + _report_data.p_data = _scan_data; + _report_data.len = BLE_GAP_SCAN_BUFFER_MAX; + + _param.timeout = timeout; + + VERIFY_STATUS( sd_ble_gap_scan_start(&_param, &_report_data), false ); + + Bluefruit._startConnLed(); // start blinking + _runnning = true; + + return true; +} + +bool BLEScanner::resume(void) +{ + // resume scanning after received an report + VERIFY_STATUS( sd_ble_gap_scan_start(NULL, &_report_data), false ); + return true; +} + +bool BLEScanner::stop(void) +{ + VERIFY_STATUS( sd_ble_gap_scan_stop(), false ); + + Bluefruit._stopConnLed(); // stop blinking + _runnning = false; + + return true; +} + +/*------------------------------------------------------------------*/ +/* Paser helper + *------------------------------------------------------------------*/ + + /** + * @param scandata + * @param scanlen + * @param type + * @param buf Output buffer + * @param bufsize If bufsize is skipped (zero), len check will be skipped + * @return number of written bytes + */ +uint8_t BLEScanner::parseReportByType(const uint8_t* scandata, uint8_t scanlen, uint8_t type, uint8_t* buf, uint8_t bufsize) +{ + uint8_t len = 0; + uint8_t const* ptr = NULL; + + if ( scanlen < 2 ) return 0; + + // len (1+data), type, data + while ( scanlen ) + { + if ( scandata[1] == type ) + { + len = (scandata[0]-1); + ptr = (scandata + 2); + break; + } + else + { + scanlen -= (scandata[0] + 1); + scandata += (scandata[0] + 1); + } + } + + // not found return 0 + if (ptr == NULL) return 0; + + // Only check len if bufsize is input + if (bufsize > 0) len = min8(bufsize, len); + + memcpy(buf, ptr, len); + return len; +} + +uint8_t BLEScanner::parseReportByType(const ble_gap_evt_adv_report_t* report, uint8_t type, uint8_t* buf, uint8_t bufsize) +{ + return parseReportByType(report->data.p_data, report->data.len, type, buf, bufsize); +} + +bool BLEScanner::checkReportForUuid(const ble_gap_evt_adv_report_t* report, BLEUuid ble_uuid) +{ + const uint8_t* uuid; + uint8_t uuid_len = ble_uuid.size(); + + uint8_t type_arr[2]; + + // Check both more available and complete list + if ( uuid_len == 16) + { + type_arr[0] = BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE; + type_arr[1] = BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE; + + uuid = (uint8_t*) &ble_uuid._uuid.uuid; + }else + { + type_arr[0] = BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE; + type_arr[1] = BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE; + + uuid = ble_uuid._uuid128; + } + + uuid_len /= 8; // convert to number of bytes + + for (int i=0; i<2; i++) + { + uint8_t buffer[BLE_GAP_ADV_SET_DATA_SIZE_MAX] = { 0 }; + uint8_t len = parseReportByType(report, type_arr[i], buffer); + + uint8_t* ptr = buffer; + + // look for uuid in the uuid list + while( len ) + { + // found matched + if ( !memcmp(ptr, uuid, uuid_len) ) + { + return true; + }else + { + ptr += uuid_len; + len -= uuid_len; + } + } + } + + return false; +} + +bool BLEScanner::checkReportForService(const ble_gap_evt_adv_report_t* report, BLEClientService& svc) +{ + return checkReportForUuid(report, svc.uuid); +} + +bool BLEScanner::checkReportForService(const ble_gap_evt_adv_report_t* report, BLEService& svc) +{ + return checkReportForUuid(report, svc.uuid); +} + +void BLEScanner::filterRssi(int8_t min_rssi) +{ + _filter_rssi = min_rssi; +} + +void BLEScanner::filterUuid(BLEUuid ble_uuid) +{ + filterUuid(&ble_uuid, 1); +} + +void BLEScanner::filterUuid(BLEUuid ble_uuid1, BLEUuid ble_uuid2) +{ + BLEUuid bleuuid[] = {ble_uuid1, ble_uuid2}; + filterUuid( bleuuid , 2); +} + +void BLEScanner::filterUuid(BLEUuid ble_uuid1, BLEUuid ble_uuid2, BLEUuid ble_uuid3) +{ + BLEUuid bleuuid[] = {ble_uuid1, ble_uuid2, ble_uuid3}; + filterUuid( bleuuid , 3); +} + +void BLEScanner::filterUuid(BLEUuid ble_uuid1, BLEUuid ble_uuid2, BLEUuid ble_uuid3, BLEUuid ble_uuid4) +{ + BLEUuid bleuuid[] = {ble_uuid1, ble_uuid2, ble_uuid3, ble_uuid4}; + filterUuid( bleuuid , 4); +} + +void BLEScanner::filterUuid(BLEUuid ble_uuid[], uint8_t count) +{ + if (_filter_uuid_count) delete[] _filter_uuid; + + _filter_uuid_count = count; + if (count == 0) return; + + _filter_uuid = new BLEUuid[count]; + + for(uint8_t i=0; i<count; i++) _filter_uuid[i] = ble_uuid[i]; +} + +void BLEScanner::filterService(BLEService& svc) +{ + filterUuid(svc.uuid); +} + +void BLEScanner::filterService(BLEClientService& cli) +{ + filterUuid(cli.uuid); +} + +void BLEScanner::filterMSD(uint16_t manuf_id) +{ + _filter_msd_en = true; + _filter_msd_id = manuf_id; +} + +void BLEScanner::clearFilters(void) +{ + _filter_rssi = INT8_MIN; + _filter_msd_en = false; + + if ( _filter_uuid_count ) + { + delete[] _filter_uuid; + _filter_uuid = NULL; + + _filter_uuid_count = 0; + } + +} + +/** + * Event Handler + * @param evt + */ +void BLEScanner::_eventHandler(ble_evt_t* evt) +{ + switch ( evt->header.evt_id ) + { + case BLE_GAP_EVT_ADV_REPORT: + { + ble_gap_evt_adv_report_t const* evt_report = &evt->evt.gap_evt.params.adv_report; + bool invoke_cb = false; + + do + { + // filter by rssi + if ( _filter_rssi > evt_report->rssi ) break; + + // filter by uuid + if ( _filter_uuid_count ) + { + uint8_t i; + for(i=0; i<_filter_uuid_count; i++) + { + if ( checkReportForUuid(evt_report, _filter_uuid[i]) ) break; + } + + // If there is no matched UUID in the list --> filter failed + if ( i == _filter_uuid_count ) break; + } + + // filter by MSD if present + if ( _filter_msd_en ) + { + uint16_t id; + if ( !(parseReportByType(evt_report, BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA, (uint8_t*)&id, 2) && (id == _filter_msd_id)) ) break; + } + + invoke_cb = true; + } while(0); // for quick break + + if ( invoke_cb ) + { + ble_gap_evt_adv_report_t* adv_report = (ble_gap_evt_adv_report_t*) rtos_malloc( sizeof(ble_gap_evt_adv_report_t) ); + VERIFY(adv_report,); + + (*adv_report) = (*evt_report); + if (_rx_cb) ada_callback(adv_report, _rx_cb, adv_report); + }else + { + // continue scanning since report is filtered and callback is not invoked + this->resume(); + } + } + break; + + case BLE_GAP_EVT_CONNECTED: + { + ble_gap_evt_connected_t const * para = &evt->evt.gap_evt.params.connected; + + if ( para->role == BLE_GAP_ROLE_CENTRAL) + { + _runnning = false; + + // Turn on Conn LED + Bluefruit._stopConnLed(); + Bluefruit._setConnLed(true); + } + } + break; + + case BLE_GAP_EVT_DISCONNECTED: + if ( BLE_GAP_ROLE_CENTRAL == Bluefruit.Gap.getRole(evt->evt.common_evt.conn_handle) ) + { + // skip if already running + if ( !_runnning ) + { + // Turn off Conn LED + Bluefruit._setConnLed(false); + + // Auto start if enabled + if ( _start_if_disconnect ) + { + start(_param.timeout); + } + } + } + break; + + case BLE_GAP_EVT_TIMEOUT: + if (evt->evt.gap_evt.params.timeout.src == BLE_GAP_TIMEOUT_SRC_SCAN) + { + _runnning = false; + if (_stop_cb) ada_callback(NULL, _stop_cb); + } + break; + + default: break; + } +} diff --git a/arduino/libraries/Bluefruit52Lib/src/BLEScanner.h b/arduino/libraries/Bluefruit52Lib/src/BLEScanner.h new file mode 100755 index 0000000..8e06eb0 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/BLEScanner.h @@ -0,0 +1,121 @@ +/**************************************************************************/ +/*! + @file BLEScanner.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLESCANNER_H_ +#define BLESCANNER_H_ + +#include <Arduino.h> +#include "bluefruit_common.h" + +#define BLE_SCAN_INTERVAL_DFLT 160 // 100 ms (in 0.625 ms) +#define BLE_SCAN_WINDOW_DFLT 80 // 50 ms (in 0.625 ms) + +class BLEScanner +{ +public: + typedef void (*rx_callback_t ) (ble_gap_evt_adv_report_t*); + typedef void (*stop_callback_t) (void); + + BLEScanner(void); + + ble_gap_scan_params_t* getParams(void); + bool isRunning(void); + + void useActiveScan(bool enable); + void setInterval(uint16_t interval, uint16_t window); + void setIntervalMS(uint16_t interval, uint16_t window); + void restartOnDisconnect(bool enable); + + void filterRssi(int8_t min_rssi); + void filterMSD(uint16_t manuf_id); + + + void filterUuid(BLEUuid ble_uuid); + void filterUuid(BLEUuid ble_uuid1, BLEUuid ble_uuid2); + void filterUuid(BLEUuid ble_uuid1, BLEUuid ble_uuid2, BLEUuid ble_uuid3); + void filterUuid(BLEUuid ble_uuid1, BLEUuid ble_uuid2, BLEUuid ble_uuid3, BLEUuid ble_uuid4); + void filterUuid(BLEUuid ble_uuid[], uint8_t count); + + void filterService(BLEService& svc); + void filterService(BLEClientService& cli); + + void clearFilters(void); + + bool start(uint16_t timeout = 0); + bool resume(void); + bool stop(void); + + /*------------- Callbacks -------------*/ + void setRxCallback(rx_callback_t fp); + void setStopCallback(stop_callback_t fp); + + /*------------- Data Parser -------------*/ + uint8_t parseReportByType(const uint8_t* scandata, uint8_t scanlen, uint8_t type, uint8_t* buf, uint8_t bufsize = 0); + uint8_t parseReportByType(const ble_gap_evt_adv_report_t* report, uint8_t type, uint8_t* buf, uint8_t bufsize = 0); + + bool checkReportForUuid(const ble_gap_evt_adv_report_t* report, BLEUuid ble_uuid); + bool checkReportForService(const ble_gap_evt_adv_report_t* report, BLEClientService& svc); + bool checkReportForService(const ble_gap_evt_adv_report_t* report, BLEService& svc); + + /*------------------------------------------------------------------*/ + /* INTERNAL USAGE ONLY + * Although declare as public, it is meant to be invoked by internal + * code. User should not call these directly + *------------------------------------------------------------------*/ + void _eventHandler(ble_evt_t* evt); + +private: + uint8_t _scan_data[BLE_GAP_SCAN_BUFFER_MAX]; + ble_data_t _report_data; + + bool _runnning; + bool _start_if_disconnect; + + int8_t _filter_rssi; + bool _filter_msd_en; // since all value of manufacturer id is valid (0-FFFF) + uint16_t _filter_msd_id; + + BLEUuid* _filter_uuid; + uint8_t _filter_uuid_count; + + rx_callback_t _rx_cb; + stop_callback_t _stop_cb; + + ble_gap_scan_params_t _param; +}; + + + +#endif /* BLESCANNER_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/BLEService.cpp b/arduino/libraries/Bluefruit52Lib/src/BLEService.cpp new file mode 100755 index 0000000..ef83f06 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/BLEService.cpp @@ -0,0 +1,86 @@ +/**************************************************************************/ +/*! + @file BLEService.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" + +BLEService* BLEService::lastService = NULL; + +void BLEService::_init(void) +{ + _handle = BLE_GATT_HANDLE_INVALID; +} + +BLEService::BLEService(void) + : uuid() +{ + _init(); +} + +BLEService::BLEService(BLEUuid bleuuid) + : uuid(bleuuid) +{ + _init(); +} + +void BLEService::setUuid(BLEUuid bleuuid) +{ + uuid = bleuuid; +} + +err_t BLEService::begin(void) +{ + // Add UUID128 if needed + (void) uuid.begin(); + + uint16_t handle; + VERIFY_STATUS( sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &uuid._uuid, &handle) ); + + lastService = this; + (void) Bluefruit.Gatt._addService(this); + + return ERROR_NONE; +} + +void BLEService::_disconnect_cb(void) +{ + // Template for inherited class +} + +void BLEService::_connect_cb(void) +{ + // Template for inherited class +} + diff --git a/arduino/libraries/Bluefruit52Lib/src/BLEService.h b/arduino/libraries/Bluefruit52Lib/src/BLEService.h new file mode 100755 index 0000000..895f599 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/BLEService.h @@ -0,0 +1,67 @@ +/**************************************************************************/ +/*! + @file BLEService.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLESERVICE_H_ +#define BLESERVICE_H_ + +#include "bluefruit_common.h" +#include "BLEUuid.h" + +class BLEService +{ + protected: + uint16_t _handle; + + void _init(void); + + virtual void _disconnect_cb(void); + virtual void _connect_cb(void); + + public: + static BLEService* lastService; + + BLEUuid uuid; + + BLEService(void); + BLEService(BLEUuid bleuuid); + + void setUuid(BLEUuid bleuuid); + + virtual err_t begin(void); + + friend class BLEGatt; +}; + +#endif /* BLESERVICE_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/BLEUuid.cpp b/arduino/libraries/Bluefruit52Lib/src/BLEUuid.cpp new file mode 100755 index 0000000..6922afc --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/BLEUuid.cpp @@ -0,0 +1,152 @@ +/**************************************************************************/ +/*! + @file BLEUuid.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "BLEUuid.h" + +//void reverse_uuid128(uint8_t const in[16], uint8_t out[16]) +//{ +// for(uint8_t i=0; i<16; i++) +// { +// out[i] = in[15-i]; +// } +//} + +void BLEUuid::set(uint16_t uuid16) +{ + _uuid.type = BLE_UUID_TYPE_BLE; + _uuid.uuid = uuid16; + + _uuid128 = NULL; +} + +void BLEUuid::set(uint8_t const uuid128[16]) +{ + _uuid.type = BLE_UUID_TYPE_UNKNOWN; + _uuid.uuid = 0; + + _uuid128 = uuid128; +} + +bool BLEUuid::get(uint16_t* uuid16 ) const +{ + *uuid16 = _uuid.uuid; + + return true; +} + +bool BLEUuid::get(uint8_t uuid128[16]) +{ + if (_uuid.type < BLE_UUID_TYPE_VENDOR_BEGIN ) return false; + + if ( _uuid128 ) + { + memcpy(uuid128, _uuid128, 16); + }else + { + uint8_t len = 16; + VERIFY_STATUS( sd_ble_uuid_encode(&_uuid, &len, uuid128), false); + VERIFY(len == 16); + } + + return true; +} + +/** + * Get size of uuid in BIT + * @return 16, 32 or 128 + */ +size_t BLEUuid::size (void) const +{ + // uuid 16 + if (_uuid.type == BLE_UUID_TYPE_BLE ) return 16; + if (_uuid128 != NULL || _uuid.type >= BLE_UUID_TYPE_VENDOR_BEGIN) return 128; + + // unknown + return 0; +} + +err_t BLEUuid::begin(void) +{ + /* Add base uuid and decode to get uuid16 + * This should cover the already added base uuid128 previously + */ + if (_uuid.type == BLE_UUID_TYPE_UNKNOWN && _uuid128 != NULL ) + { + (void) sd_ble_uuid_vs_add( (ble_uuid128_t const*) _uuid128, &_uuid.type ); + VERIFY_STATUS( sd_ble_uuid_decode(16, _uuid128, &_uuid) ); + } + + return ERROR_NONE; +} + +bool BLEUuid::operator== (const BLEUuid& uuid) const +{ + return (this->_uuid.type == uuid._uuid.type) && (this->_uuid.uuid == uuid._uuid.uuid); +} + +bool BLEUuid::operator!= (const BLEUuid& uuid) const +{ + return !(*this == uuid); +} + +bool BLEUuid::operator== (const ble_uuid_t uuid) const +{ + return (this->_uuid.type == uuid.type) && (this->_uuid.uuid == uuid.uuid); +} + +bool BLEUuid::operator!= (const ble_uuid_t uuid) const +{ + return !(*this == uuid); +} + +// Overload copy operator to allow initialization from other type +BLEUuid& BLEUuid::operator=(const uint16_t uuid) +{ + set(uuid); + return *this; +} + +BLEUuid& BLEUuid::operator=(uint8_t const uuid128[16]) +{ + set(uuid128); + return *this; +} + +BLEUuid& BLEUuid::operator=(ble_uuid_t uuid) +{ + _uuid = uuid; + return *this; +} diff --git a/arduino/libraries/Bluefruit52Lib/src/BLEUuid.h b/arduino/libraries/Bluefruit52Lib/src/BLEUuid.h new file mode 100755 index 0000000..6cdc101 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/BLEUuid.h @@ -0,0 +1,320 @@ +/**************************************************************************/ +/*! + @file BLEUuid.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLEUUID_H_ +#define BLEUUID_H_ + +#include "bluefruit_common.h" + +class BLEUuid +{ + public: + ble_uuid_t _uuid; + uint8_t const* _uuid128; + + // Constructors + BLEUuid(void ) { _uuid.type = BLE_UUID_TYPE_UNKNOWN; _uuid.uuid = 0; _uuid128 = NULL; } + BLEUuid(uint16_t uuid16 ) { set(uuid16 ); } + BLEUuid(uint8_t const uuid128[16] ) { set(uuid128); } + BLEUuid(ble_uuid_t uuid ) { _uuid = uuid; _uuid128 = NULL; } + + void set(uint16_t uuid16); + void set(uint8_t const uuid128[16]); + + bool get(uint16_t* uuid16) const; + bool get(uint8_t uuid128[16]); + + size_t size (void) const; + + // Add UUID128 if needed, in case of UUID16, no actions is required + err_t begin(void); + + bool operator==(const BLEUuid& uuid) const; + bool operator!=(const BLEUuid& uuid) const; + bool operator==(const ble_uuid_t uuid) const; + bool operator!=(const ble_uuid_t uuid) const; + + // Overload copy operator to allow initialization from other type + BLEUuid& operator=(const uint16_t uuid); + BLEUuid& operator=(uint8_t const uuid128[16]); + BLEUuid& operator=(ble_uuid_t uuid); +}; + +/*------------------------------------------------------------------*/ +/* Service UUID + * https://www.bluetooth.com/specifications/gatt/services + *------------------------------------------------------------------*/ +#define UUID16_SVC_ALERT_NOTIFICATION 0x1811 +#define UUID16_SVC_BATTERY 0x180F +#define UUID16_SVC_BLOOD_PRESSURE 0x1810 +#define UUID16_SVC_CURRENT_TIME 0x1805 +#define UUID16_SVC_CYCLING_SPEED_AND_CADENCE 0x1816 +#define UUID16_SVC_CYCLING_POWER 0x1818 +#define UUID16_SVC_LOCATION_AND_NAVIGATION 0x1819 +#define UUID16_SVC_DEVICE_INFORMATION 0x180A +#define UUID16_SVC_GLUCOSE 0x1808 +#define UUID16_SVC_HEALTH_THERMOMETER 0x1809 +#define UUID16_SVC_HEART_RATE 0x180D +#define UUID16_SVC_HUMAN_INTERFACE_DEVICE 0x1812 +#define UUID16_SVC_IMMEDIATE_ALERT 0x1802 +#define UUID16_SVC_LINK_LOSS 0x1803 +#define UUID16_SVC_NEXT_DST_CHANGE 0x1807 +#define UUID16_SVC_PHONE_ALERT_STATUS 0x180E +#define UUID16_SVC_REFERENCE_TIME_UPDATE 0x1806 +#define UUID16_SVC_RUNNING_SPEED_AND_CADENCE 0x1814 +#define UUID16_SVC_SCAN_PARAMETERS 0x1813 +#define UUID16_SVC_TX_POWER 0x1804 +#define UUID16_SVC_IPSP 0x1820 +#define UUID16_SVC_BMS 0x181E +#define UUID16_SVC_CGM 0x181F +#define UUID16_SVC_PLX 0x1822 + +#define UUID16_SVC_EDDYSTONE 0xFEAA + +/*------------------------------------------------------------------*/ +/* Characteristic UUID + * https://www.bluetooth.com/specifications/gatt/characteristics + *------------------------------------------------------------------*/ +#define UUID16_CHR_REMOVABLE 0x2A3A +#define UUID16_CHR_SERVICE_REQUIRED 0x2A3B +#define UUID16_CHR_ALERT_CATEGORY_ID 0x2A43 +#define UUID16_CHR_ALERT_CATEGORY_ID_BIT_MASK 0x2A42 +#define UUID16_CHR_ALERT_LEVEL 0x2A06 +#define UUID16_CHR_ALERT_NOTIFICATION_CONTROL_POINT 0x2A44 +#define UUID16_CHR_ALERT_STATUS 0x2A3F +#define UUID16_CHR_BATTERY_LEVEL 0x2A19 +#define UUID16_CHR_BLOOD_PRESSURE_FEATURE 0x2A49 +#define UUID16_CHR_BLOOD_PRESSURE_MEASUREMENT 0x2A35 +#define UUID16_CHR_BODY_SENSOR_LOCATION 0x2A38 +#define UUID16_CHR_BOOT_KEYBOARD_INPUT_REPORT 0x2A22 +#define UUID16_CHR_BOOT_KEYBOARD_OUTPUT_REPORT 0x2A32 +#define UUID16_CHR_BOOT_MOUSE_INPUT_REPORT 0x2A33 +#define UUID16_CHR_CURRENT_TIME 0x2A2B +#define UUID16_CHR_CYCLING_POWER_MEASUREMENT 0x2A63 +#define UUID16_CHR_CYCLING_POWER_FEATURE 0x2A65 +#define UUID16_CHR_DATE_TIME 0x2A08 +#define UUID16_CHR_DAY_DATE_TIME 0x2A0A +#define UUID16_CHR_DAY_OF_WEEK 0x2A09 +#define UUID16_CHR_DST_OFFSET 0x2A0D +#define UUID16_CHR_EXACT_TIME_256 0x2A0C +#define UUID16_CHR_FIRMWARE_REVISION_STRING 0x2A26 +#define UUID16_CHR_GLUCOSE_FEATURE 0x2A51 +#define UUID16_CHR_GLUCOSE_MEASUREMENT 0x2A18 +#define UUID16_CHR_GLUCOSE_MEASUREMENT_CONTEXT 0x2A34 +#define UUID16_CHR_HARDWARE_REVISION_STRING 0x2A27 +#define UUID16_CHR_HEART_RATE_CONTROL_POINT 0x2A39 +#define UUID16_CHR_HEART_RATE_MEASUREMENT 0x2A37 +#define UUID16_CHR_HID_CONTROL_POINT 0x2A4C +#define UUID16_CHR_HID_INFORMATION 0x2A4A +#define UUID16_CHR_IEEE_REGULATORY_CERTIFICATION_DATA_LIST 0x2A2A +#define UUID16_CHR_INTERMEDIATE_CUFF_PRESSURE 0x2A36 +#define UUID16_CHR_INTERMEDIATE_TEMPERATURE 0x2A1E +#define UUID16_CHR_LOCAL_TIME_INFORMATION 0x2A0F +#define UUID16_CHR_MANUFACTURER_NAME_STRING 0x2A29 +#define UUID16_CHR_MEASUREMENT_INTERVAL 0x2A21 +#define UUID16_CHR_MODEL_NUMBER_STRING 0x2A24 +#define UUID16_CHR_UNREAD_ALERT 0x2A45 +#define UUID16_CHR_NEW_ALERT 0x2A46 +#define UUID16_CHR_PNP_ID 0x2A50 +#define UUID16_CHR_PROTOCOL_MODE 0x2A4E +#define UUID16_CHR_RECORD_ACCESS_CONTROL_POINT 0x2A52 +#define UUID16_CHR_REFERENCE_TIME_INFORMATION 0x2A14 +#define UUID16_CHR_REPORT 0x2A4D +#define UUID16_CHR_REPORT_MAP 0x2A4B +#define UUID16_CHR_RINGER_CONTROL_POINT 0x2A40 +#define UUID16_CHR_RINGER_SETTING 0x2A41 +#define UUID16_CHR_SCAN_INTERVAL_WINDOW 0x2A4F +#define UUID16_CHR_SCAN_REFRESH 0x2A31 +#define UUID16_CHR_SERIAL_NUMBER_STRING 0x2A25 +#define UUID16_CHR_SOFTWARE_REVISION_STRING 0x2A28 +#define UUID16_CHR_SUPPORTED_NEW_ALERT_CATEGORY 0x2A47 +#define UUID16_CHR_SUPPORTED_UNREAD_ALERT_CATEGORY 0x2A48 +#define UUID16_CHR_SYSTEM_ID 0x2A23 +#define UUID16_CHR_TEMPERATURE_MEASUREMENT 0x2A1C +#define UUID16_CHR_TEMPERATURE_TYPE 0x2A1D +#define UUID16_CHR_TIME_ACCURACY 0x2A12 +#define UUID16_CHR_TIME_SOURCE 0x2A13 +#define UUID16_CHR_TIME_UPDATE_CONTROL_POINT 0x2A16 +#define UUID16_CHR_TIME_UPDATE_STATE 0x2A17 +#define UUID16_CHR_TIME_WITH_DST 0x2A11 +#define UUID16_CHR_TIME_ZONE 0x2A0E +#define UUID16_CHR_TX_POWER_LEVEL 0x2A07 +#define UUID16_CHR_CSC_FEATURE 0x2A5C +#define UUID16_CHR_CSC_MEASUREMENT 0x2A5B +#define UUID16_CHR_RSC_FEATURE 0x2A54 +#define UUID16_CHR_SC_CTRLPT 0x2A55 +#define UUID16_CHR_RSC_MEASUREMENT 0x2A53 +#define UUID16_CHR_SENSOR_LOCATION 0x2A5D +#define UUID16_EXTERNAL_REPORT_REF_DESCR 0x2907 +#define UUID16_REPORT_REF_DESCR 0x2908 +#define UUID16_CHR_LN_FEATURE 0x2A6A +#define UUID16_CHR_LN_POSITION_QUALITY 0x2A69 +#define UUID16_CHR_LN_LOCATION_AND_SPEED 0x2A67 +#define UUID16_CHR_LN_NAVIGATION 0x2A68 +#define UUID16_CHR_LN_CONTROL_POINT 0x2A6B +#define UUID16_BMS_CTRLPT 0x2AA4 +#define UUID16_BMS_FEATURE 0x2AA5 +#define UUID16_CGM_MEASUREMENT 0x2AA7 +#define UUID16_CGM_FEATURE 0x2AA8 +#define UUID16_CGM_STATUS 0x2AA9 +#define UUID16_CGM_SESSION_START_TIME 0x2AAA +#define UUID16_CGM_SESSION_RUN_TIME 0x2AAB +#define UUID16_CGM_SPECIFIC_OPS_CTRLPT 0x2AAC +#define UUID16_PLX_SPOT_CHECK_MEAS 0x2A5E +#define UUID16_PLX_CONTINUOUS_MEAS 0x2A5F +#define UUID16_PLX_FEATURES 0x2A60 + +/*------------------------------------------------------------------*/ +/* Company UUID + * https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers + *------------------------------------------------------------------*/ +#define UUID16_COMPANY_ID_APPLE 0x004C + + +/*------------------------------------------------------------------*/ +/* Unit values ( used in Characteristic Presentation Format ) + * https://developer.bluetooth.org/gatt/units/Pages/default.aspx + *------------------------------------------------------------------*/ +#define UUID16_UNIT_UNITLESS 0x2700 +#define UUID16_UNIT_LENGTH_METRE 0x2701 +#define UUID16_UNIT_MASS_KILOGRAM 0x2702 +#define UUID16_UNIT_TIME_SECOND 0x2703 +#define UUID16_UNIT_ELECTRIC_CURRENT_AMPERE 0x2704 +#define UUID16_UNIT_THERMODYNAMIC_TEMPERATURE_KELVIN 0x2705 +#define UUID16_UNIT_AMOUNT_OF_SUBSTANCE_MOLE 0x2706 +#define UUID16_UNIT_LUMINOUS_INTENSITY_CANDELA 0x2707 +#define UUID16_UNIT_AREA_SQUARE_METRES 0x2710 +#define UUID16_UNIT_VOLUME_CUBIC_METRES 0x2711 +#define UUID16_UNIT_VELOCITY_METRES_PER_SECOND 0x2712 +#define UUID16_UNIT_ACCELERATION_METRES_PER_SECOND_SQUARED 0x2713 +#define UUID16_UNIT_WAVENUMBER_RECIPROCAL_METRE 0x2714 +#define UUID16_UNIT_DENSITY_KILOGRAM_PER_CUBIC_METRE 0x2715 +#define UUID16_UNIT_SURFACE_DENSITY_KILOGRAM_PER_SQUARE_METRE 0x2716 +#define UUID16_UNIT_SPECIFIC_VOLUME_CUBIC_METRE_PER_KILOGRAM 0x2717 +#define UUID16_UNIT_CURRENT_DENSITY_AMPERE_PER_SQUARE_METRE 0x2718 +#define UUID16_UNIT_MAGNETIC_FIELD_STRENGTH_AMPERE_PER_METRE 0x2719 +#define UUID16_UNIT_AMOUNT_CONCENTRATION_MOLE_PER_CUBIC_METRE 0x271A +#define UUID16_UNIT_MASS_CONCENTRATION_KILOGRAM_PER_CUBIC_METRE 0x271B +#define UUID16_UNIT_LUMINANCE_CANDELA_PER_SQUARE_METRE 0x271C +#define UUID16_UNIT_REFRACTIVE_INDEX 0x271D +#define UUID16_UNIT_RELATIVE_PERMEABILITY 0x271E +#define UUID16_UNIT_PLANE_ANGLE_RADIAN 0x2720 +#define UUID16_UNIT_SOLID_ANGLE_STERADIAN 0x2721 +#define UUID16_UNIT_FREQUENCY_HERTZ 0x2722 +#define UUID16_UNIT_FORCE_NEWTON 0x2723 +#define UUID16_UNIT_PRESSURE_PASCAL 0x2724 +#define UUID16_UNIT_ENERGY_JOULE 0x2725 +#define UUID16_UNIT_POWER_WATT 0x2726 +#define UUID16_UNIT_ELECTRIC_CHARGE_COULOMB 0x2727 +#define UUID16_UNIT_ELECTRIC_POTENTIAL_DIFFERENCE_VOLT 0x2728 +#define UUID16_UNIT_CAPACITANCE_FARAD 0x2729 +#define UUID16_UNIT_ELECTRIC_RESISTANCE_OHM 0x272A +#define UUID16_UNIT_ELECTRIC_CONDUCTANCE_SIEMENS 0x272B +#define UUID16_UNIT_MAGNETIC_FLEX_WEBER 0x272C +#define UUID16_UNIT_MAGNETIC_FLEX_DENSITY_TESLA 0x272D +#define UUID16_UNIT_INDUCTANCE_HENRY 0x272E +#define UUID16_UNIT_THERMODYNAMIC_TEMPERATURE_DEGREE_CELSIUS 0x272F +#define UUID16_UNIT_LUMINOUS_FLUX_LUMEN 0x2730 +#define UUID16_UNIT_ILLUMINANCE_LUX 0x2731 +#define UUID16_UNIT_ACTIVITY_REFERRED_TO_A_RADIONUCLIDE_BECQUEREL 0x2732 +#define UUID16_UNIT_ABSORBED_DOSE_GRAY 0x2733 +#define UUID16_UNIT_DOSE_EQUIVALENT_SIEVERT 0x2734 +#define UUID16_UNIT_CATALYTIC_ACTIVITY_KATAL 0x2735 +#define UUID16_UNIT_DYNAMIC_VISCOSITY_PASCAL_SECOND 0x2740 +#define UUID16_UNIT_MOMENT_OF_FORCE_NEWTON_METRE 0x2741 +#define UUID16_UNIT_SURFACE_TENSION_NEWTON_PER_METRE 0x2742 +#define UUID16_UNIT_ANGULAR_VELOCITY_RADIAN_PER_SECOND 0x2743 +#define UUID16_UNIT_ANGULAR_ACCELERATION_RADIAN_PER_SECOND_SQUARED 0x2744 +#define UUID16_UNIT_HEAT_FLUX_DENSITY_WATT_PER_SQUARE_METRE 0x2745 +#define UUID16_UNIT_HEAT_CAPACITY_JOULE_PER_KELVIN 0x2746 +#define UUID16_UNIT_SPECIFIC_HEAT_CAPACITY_JOULE_PER_KILOGRAM_KELVIN 0x2747 +#define UUID16_UNIT_SPECIFIC_ENERGY_JOULE_PER_KILOGRAM 0x2748 +#define UUID16_UNIT_THERMAL_CONDUCTIVITY_WATT_PER_METRE_KELVIN 0x2749 +#define UUID16_UNIT_ENERGY_DENSITY_JOULE_PER_CUBIC_METRE 0x274A +#define UUID16_UNIT_ELECTRIC_FIELD_STRENGTH_VOLT_PER_METRE 0x274B +#define UUID16_UNIT_ELECTRIC_CHARGE_DENSITY_COULOMB_PER_CUBIC_METRE 0x274C +#define UUID16_UNIT_SURFACE_CHARGE_DENSITY_COULOMB_PER_SQUARE_METRE 0x274D +#define UUID16_UNIT_ELECTRIC_FLUX_DENSITY_COULOMB_PER_SQUARE_METRE 0x274E +#define UUID16_UNIT_PERMITTIVITY_FARAD_PER_METRE 0x274F +#define UUID16_UNIT_PERMEABILITY_HENRY_PER_METRE 0x2750 +#define UUID16_UNIT_MOLAR_ENERGY_JOULE_PER_MOLE 0x2751 +#define UUID16_UNIT_MOLAR_ENTROPY_JOULE_PER_MOLE_KELVIN 0x2752 +#define UUID16_UNIT_EXPOSURE_COULOMB_PER_KILOGRAM 0x2753 +#define UUID16_UNIT_ABSORBED_DOSE_RATE_GRAY_PER_SECOND 0x2754 +#define UUID16_UNIT_RADIANT_INTENSITY_WATT_PER_STERADIAN 0x2755 +#define UUID16_UNIT_RADIANCE_WATT_PER_SQUARE_METRE_STERADIAN 0x2756 +#define UUID16_UNIT_CATALYTIC_ACTIVITY_CONCENTRATION_KATAL_PER_CUBIC_METRE 0x2757 +#define UUID16_UNIT_TIME_MINUTE 0x2760 +#define UUID16_UNIT_TIME_HOUR 0x2761 +#define UUID16_UNIT_TIME_DAY 0x2762 +#define UUID16_UNIT_PLANE_ANGLE_DEGREE 0x2763 +#define UUID16_UNIT_PLANE_ANGLE_MINUTE 0x2764 +#define UUID16_UNIT_PLANE_ANGLE_SECOND 0x2765 +#define UUID16_UNIT_AREA_HECTARE 0x2766 +#define UUID16_UNIT_VOLUME_LITRE 0x2767 +#define UUID16_UNIT_MASS_TONNE 0x2768 +#define UUID16_UNIT_PRESSURE_BAR 0x2780 +#define UUID16_UNIT_PRESSURE_MILLIMETRE_OF_MERCURY 0x2781 +#define UUID16_UNIT_LENGTH_ANGSTROM 0x2782 +#define UUID16_UNIT_LENGTH_NAUTICAL_MILE 0x2783 +#define UUID16_UNIT_AREA_BARN 0x2784 +#define UUID16_UNIT_VELOCITY_KNOT 0x2785 +#define UUID16_UNIT_LOGARITHMIC_RADIO_QUANTITY_NEPER 0x2786 +#define UUID16_UNIT_LOGARITHMIC_RADIO_QUANTITY_BEL 0x2787 +#define UUID16_UNIT_LENGTH_YARD 0x27A0 +#define UUID16_UNIT_LENGTH_PARSEC 0x27A1 +#define UUID16_UNIT_LENGTH_INCH 0x27A2 +#define UUID16_UNIT_LENGTH_FOOT 0x27A3 +#define UUID16_UNIT_LENGTH_MILE 0x27A4 +#define UUID16_UNIT_PRESSURE_POUND_FORCE_PER_SQUARE_INCH 0x27A5 +#define UUID16_UNIT_VELOCITY_KILOMETRE_PER_HOUR 0x27A6 +#define UUID16_UNIT_VELOCITY_MILE_PER_HOUR 0x27A7 +#define UUID16_UNIT_ANGULAR_VELOCITY_REVOLUTION_PER_MINUTE 0x27A8 +#define UUID16_UNIT_ENERGY_GRAM_CALORIE 0x27A9 +#define UUID16_UNIT_ENERGY_KILOGRAM_CALORIE 0x27AA +#define UUID16_UNIT_ENERGY_KILOWATT_HOUR 0x27AB +#define UUID16_UNIT_THERMODYNAMIC_TEMPERATURE_DEGREE_FAHRENHEIT 0x27AC +#define UUID16_UNIT_PERCENTAGE 0x27AD +#define UUID16_UNIT_PER_MILLE 0x27AE +#define UUID16_UNIT_PERIOD_BEATS_PER_MINUTE 0x27AF +#define UUID16_UNIT_ELECTRIC_CHARGE_AMPERE_HOURS 0x27B0 +#define UUID16_UNIT_MASS_DENSITY_MILLIGRAM_PER_DECILITRE 0x27B1 +#define UUID16_UNIT_MASS_DENSITY_MILLIMOLE_PER_LITRE 0x27B2 +#define UUID16_UNIT_TIME_YEAR 0x27B3 +#define UUID16_UNIT_TIME_MONTH 0x27B4 +#define UUID16_UNIT_CONCENTRATION_COUNT_PER_CUBIC_METRE 0x27B5 +#define UUID16_UNIT_IRRADIANCE_WATT_PER_SQUARE_METRE 0x27B6 + +#endif /* BLEUUID_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/bluefruit.cpp b/arduino/libraries/Bluefruit52Lib/src/bluefruit.cpp new file mode 100755 index 0000000..7e7848e --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/bluefruit.cpp @@ -0,0 +1,1025 @@ +/**************************************************************************/ +/*! + @file bluefruit.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" +#include "utility/bonding.h" + +#ifdef NRF52840_XXAA +#include "nrfx_power.h" +#include "usb/usb.h" +#endif + +#define CFG_BLE_TX_POWER_LEVEL 0 +#define CFG_DEFAULT_NAME "Bluefruit52" + +#define CFG_BLE_TASK_STACKSIZE (512*3) +#define CFG_SOC_TASK_STACKSIZE (200) + +AdafruitBluefruit Bluefruit; + +/*------------------------------------------------------------------*/ +/* PROTOTYPTES + *------------------------------------------------------------------*/ +extern "C" +{ +void flash_nrf5x_event_cb (uint32_t event) ATTR_WEAK; +} + +void adafruit_ble_task(void* arg); +void adafruit_soc_task(void* arg); + +/*------------------------------------------------------------------*/ +/* INTERNAL FUNCTION + *------------------------------------------------------------------*/ +static void bluefruit_blinky_cb( TimerHandle_t xTimer ) +{ + (void) xTimer; + digitalToggle(LED_BLUE); +} + + +static void nrf_error_cb(uint32_t id, uint32_t pc, uint32_t info) +{ + PRINT_INT(id); + PRINT_HEX(pc); + PRINT_HEX(info); + + if ( id == NRF_FAULT_ID_SD_ASSERT && info != 0) + { + typedef struct + { + uint16_t line_num; /**< The line number where the error occurred. */ + uint8_t const * p_file_name; /**< The file in which the error occurred. */ + } assert_info_t; + + assert_info_t* assert_info = (assert_info_t*) info; + + LOG_LV1("SD Err", "assert at %s : %d", assert_info->p_file_name, assert_info->line_num); + } + +#if CFG_DEBUG + while(1) + { + + } +#endif +} + +/** + * Constructor + */ +AdafruitBluefruit::AdafruitBluefruit(void) + : Central(), Gap() +{ + /*------------------------------------------------------------------*/ + /* SoftDevice Default Configuration + * Most config use Nordic default value, except the follows: + * - Max MTU : up to 247 for maximum throughput + * + * Attr Table Size, HVN queue size, Write Command queue size is + * determined later in begin() depending on number of peripherals + * and central connections for optimum SRAM usage. + */ + /*------------------------------------------------------------------*/ + varclr(&_sd_cfg); + + _sd_cfg.attr_table_size = 0x800; + _sd_cfg.uuid128_max = BLE_UUID_VS_COUNT_DEFAULT; + _sd_cfg.service_changed = 1; + + _prph_count = 0; + _central_count = 0; + + _ble_event_sem = NULL; + _soc_event_sem = NULL; + + _led_blink_th = NULL; + _led_conn = true; + + _tx_power = CFG_BLE_TX_POWER_LEVEL; + + _conn_hdl = BLE_CONN_HANDLE_INVALID; + + _ppcp.min_conn_interval = BLE_GAP_CONN_MIN_INTERVAL_DFLT; + _ppcp.max_conn_interval = BLE_GAP_CONN_MAX_INTERVAL_DFLT; + _ppcp.slave_latency = 0; + _ppcp.conn_sup_timeout = BLE_GAP_CONN_SUPERVISION_TIMEOUT_MS / 10; // in 10ms unit + + _conn_interval = 0; + + _connect_cb = NULL; + _disconnect_cb = NULL; + _event_cb = NULL; + +COMMENT_OUT( + _auth_type = BLE_GAP_AUTH_KEY_TYPE_NONE; + varclr(_pin); +) +} + +void AdafruitBluefruit::configServiceChanged(bool changed) +{ + _sd_cfg.service_changed = (changed ? 1 : 0); +} + +void AdafruitBluefruit::configUuid128Count(uint8_t uuid128_max) +{ + _sd_cfg.uuid128_max = uuid128_max; +} + +void AdafruitBluefruit::configAttrTableSize(uint32_t attr_table_size) +{ + _sd_cfg.attr_table_size = align4( maxof(attr_table_size, BLE_GATTS_ATTR_TAB_SIZE_MIN) ); +} + +void AdafruitBluefruit::configPrphConn(uint16_t mtu_max, uint8_t event_len, uint8_t hvn_qsize, uint8_t wrcmd_qsize) +{ + Gap.configPrphConn(mtu_max, event_len, hvn_qsize, wrcmd_qsize); +} + +void AdafruitBluefruit::configCentralConn(uint16_t mtu_max, uint8_t event_len, uint8_t hvn_qsize, uint8_t wrcmd_qsize) +{ + Gap.configCentralConn(mtu_max, event_len, hvn_qsize, wrcmd_qsize); +} + +void AdafruitBluefruit::configPrphBandwidth(uint8_t bw) +{ + /* Note default value from SoftDevice are + * MTU = 23, Event Len = 3, HVN QSize = 1, WrCMD QSize =1 + */ + switch (bw) + { + case BANDWIDTH_LOW: + configPrphConn(BLE_GATT_ATT_MTU_DEFAULT, BLE_GAP_EVENT_LENGTH_MIN, BLE_GATTS_HVN_TX_QUEUE_SIZE_DEFAULT, BLE_GATTC_WRITE_CMD_TX_QUEUE_SIZE_DEFAULT); + break; + + // TODO Bandwidth auto + case BANDWIDTH_AUTO: + case BANDWIDTH_NORMAL: + configPrphConn(BLE_GATT_ATT_MTU_DEFAULT, BLE_GAP_EVENT_LENGTH_DEFAULT, BLE_GATTS_HVN_TX_QUEUE_SIZE_DEFAULT, BLE_GATTC_WRITE_CMD_TX_QUEUE_SIZE_DEFAULT); + break; + + case BANDWIDTH_HIGH: + configPrphConn(128, 6, 2, BLE_GATTC_WRITE_CMD_TX_QUEUE_SIZE_DEFAULT); + break; + + case BANDWIDTH_MAX: + configPrphConn(247, 6, 3, BLE_GATTC_WRITE_CMD_TX_QUEUE_SIZE_DEFAULT); + break; + + default: break; + } +} + +void AdafruitBluefruit::configCentralBandwidth(uint8_t bw) +{ + /* Note default value from SoftDevice are + * MTU = 23, Event Len = 3, HVN QSize = 1, WrCMD QSize =1 + */ + switch (bw) + { + case BANDWIDTH_LOW: + configCentralConn(BLE_GATT_ATT_MTU_DEFAULT, BLE_GAP_EVENT_LENGTH_MIN, BLE_GATTS_HVN_TX_QUEUE_SIZE_DEFAULT, BLE_GATTC_WRITE_CMD_TX_QUEUE_SIZE_DEFAULT); + break; + + // TODO Bandwidth auto + case BANDWIDTH_AUTO: + case BANDWIDTH_NORMAL: + configCentralConn(BLE_GATT_ATT_MTU_DEFAULT, BLE_GAP_EVENT_LENGTH_DEFAULT, BLE_GATTS_HVN_TX_QUEUE_SIZE_DEFAULT, BLE_GATTC_WRITE_CMD_TX_QUEUE_SIZE_DEFAULT); + break; + + case BANDWIDTH_HIGH: + configCentralConn(128, 6, 2, BLE_GATTC_WRITE_CMD_TX_QUEUE_SIZE_DEFAULT); + break; + + case BANDWIDTH_MAX: + configCentralConn(247, 6, 3, BLE_GATTC_WRITE_CMD_TX_QUEUE_SIZE_DEFAULT); + break; + + default: break; + } +} + + +err_t AdafruitBluefruit::begin(uint8_t prph_count, uint8_t central_count) +{ + _prph_count = prph_count; + _central_count = central_count; + +#ifdef NRF52840_XXAA + usb_softdevice_pre_enable(); +#endif + + // Configure Clock +#if defined( USE_LFXO ) + nrf_clock_lf_cfg_t clock_cfg = + { + // LFXO + .source = NRF_CLOCK_LF_SRC_XTAL, + .rc_ctiv = 0, + .rc_temp_ctiv = 0, + .accuracy = NRF_CLOCK_LF_ACCURACY_20_PPM + }; +#elif defined( USE_LFRC ) + nrf_clock_lf_cfg_t clock_cfg = + { + // LXRC + .source = NRF_CLOCK_LF_SRC_RC, + .rc_ctiv = 16, + .rc_temp_ctiv = 2, + .accuracy = NRF_CLOCK_LF_ACCURACY_250_PPM + }; +#else + #error Clock Source is not configured, define USE_LFXO or USE_LFRC according to your board in variant.h +#endif + + VERIFY_STATUS( sd_softdevice_enable(&clock_cfg, nrf_error_cb) ); + +#ifdef NRF52840_XXAA + usb_softdevice_post_enable(); +#endif + + /*------------------------------------------------------------------*/ + /* SoftDevice Default Configuration depending on the number of + * prph and central connections for optimal SRAM usage. + * + * - If Peripheral mode is enabled + * - ATTR Table Size = 0x800. + * - HVN TX Queue Size = 3 + * + * - If Central mode is enabled + * - Write Command Queue Size = 3 + * + * Otherwise value will have default as follows: + * - ATTR Table Size = BLE_GATTS_ATTR_TAB_SIZE_DEFAULT (0x580) + * - HVN TX Queue Size = 1 + * - Write Command Queue Size = 1 + * + * Note: Value is left as it is if already configured by user. + */ + /*------------------------------------------------------------------*/ +// if ( _prph_count ) +// { +// // If not configured by user, set Attr Table Size large enough for +// // most peripheral applications +// if ( _sd_cfg.attr_table_size == 0 ) _sd_cfg.attr_table_size = 0x800; +// } +// +// if ( _central_count) +// { +// +// } +// +// // Not configure, default value are used +// if ( _sd_cfg.attr_table_size == 0 ) _sd_cfg.attr_table_size = BLE_GATTS_ATTR_TAB_SIZE_DEFAULT; + + /*------------- Configure BLE params -------------*/ + extern uint32_t __data_start__[]; // defined in linker + uint32_t ram_start = (uint32_t) __data_start__; + + ble_cfg_t blecfg; + + // Vendor UUID count + varclr(&blecfg); + blecfg.common_cfg.vs_uuid_cfg.vs_uuid_count = _sd_cfg.uuid128_max; + VERIFY_STATUS ( sd_ble_cfg_set(BLE_COMMON_CFG_VS_UUID, &blecfg, ram_start) ); + + // Roles + varclr(&blecfg); + blecfg.gap_cfg.role_count_cfg.periph_role_count = _prph_count; + blecfg.gap_cfg.role_count_cfg.central_role_count = _central_count; // ? BLE_CENTRAL_MAX_CONN : 0); + blecfg.gap_cfg.role_count_cfg.central_sec_count = (_central_count ? 1 : 0); // should be enough + VERIFY_STATUS( sd_ble_cfg_set(BLE_GAP_CFG_ROLE_COUNT, &blecfg, ram_start) ); + + // Device Name +// varclr(&blecfg); +// blecfg.gap_cfg.device_name_cfg = +// VERIFY_STATUS( sd_ble_cfg_set(BLE_GAP_CFG_DEVICE_NAME, &blecfg, ram_start) ); + + varclr(&blecfg); + blecfg.gatts_cfg.service_changed.service_changed = _sd_cfg.service_changed; + VERIFY_STATUS ( sd_ble_cfg_set(BLE_GATTS_CFG_SERVICE_CHANGED, &blecfg, ram_start) ); + + // ATTR Table Size + varclr(&blecfg); + blecfg.gatts_cfg.attr_tab_size.attr_tab_size = _sd_cfg.attr_table_size; + VERIFY_STATUS ( sd_ble_cfg_set(BLE_GATTS_CFG_ATTR_TAB_SIZE, &blecfg, ram_start) ); + + /*------------- Event Length + MTU + HVN queue + WRITE CMD queue setting affecting bandwidth -------------*/ + if ( _prph_count ) + { + // ATT MTU + varclr(&blecfg); + blecfg.conn_cfg.conn_cfg_tag = CONN_CFG_PERIPHERAL; + blecfg.conn_cfg.params.gatt_conn_cfg.att_mtu = Gap._cfg_prph.mtu_max; + VERIFY_STATUS ( sd_ble_cfg_set(BLE_CONN_CFG_GATT, &blecfg, ram_start) ); + + // Event length and max connection for this config + varclr(&blecfg); + blecfg.conn_cfg.conn_cfg_tag = CONN_CFG_PERIPHERAL; + blecfg.conn_cfg.params.gap_conn_cfg.conn_count = _prph_count; + blecfg.conn_cfg.params.gap_conn_cfg.event_length = Gap._cfg_prph.event_len; + VERIFY_STATUS ( sd_ble_cfg_set(BLE_CONN_CFG_GAP, &blecfg, ram_start) ); + + // HVN queue size + varclr(&blecfg); + blecfg.conn_cfg.conn_cfg_tag = CONN_CFG_PERIPHERAL; + blecfg.conn_cfg.params.gatts_conn_cfg.hvn_tx_queue_size = Gap._cfg_prph.hvn_tx_qsize; + VERIFY_STATUS ( sd_ble_cfg_set(BLE_CONN_CFG_GATTS, &blecfg, ram_start) ); + + // WRITE COMMAND queue size + varclr(&blecfg); + blecfg.conn_cfg.conn_cfg_tag = CONN_CFG_PERIPHERAL; + blecfg.conn_cfg.params.gattc_conn_cfg.write_cmd_tx_queue_size = Gap._cfg_prph.wr_cmd_qsize; + VERIFY_STATUS ( sd_ble_cfg_set(BLE_CONN_CFG_GATTC, &blecfg, ram_start) ); + } + + if ( _central_count) + { + // ATT MTU + varclr(&blecfg); + blecfg.conn_cfg.conn_cfg_tag = CONN_CFG_CENTRAL; + blecfg.conn_cfg.params.gatt_conn_cfg.att_mtu = Gap._cfg_central.mtu_max; + VERIFY_STATUS ( sd_ble_cfg_set(BLE_CONN_CFG_GATT, &blecfg, ram_start) ); + + // Event length and max connection for this config + varclr(&blecfg); + blecfg.conn_cfg.conn_cfg_tag = CONN_CFG_CENTRAL; + blecfg.conn_cfg.params.gap_conn_cfg.conn_count = _central_count; + blecfg.conn_cfg.params.gap_conn_cfg.event_length = Gap._cfg_central.event_len; + VERIFY_STATUS ( sd_ble_cfg_set(BLE_CONN_CFG_GAP, &blecfg, ram_start) ); + + // HVN queue size + varclr(&blecfg); + blecfg.conn_cfg.conn_cfg_tag = CONN_CFG_CENTRAL; + blecfg.conn_cfg.params.gatts_conn_cfg.hvn_tx_queue_size = Gap._cfg_central.hvn_tx_qsize; + VERIFY_STATUS ( sd_ble_cfg_set(BLE_CONN_CFG_GATTS, &blecfg, ram_start) ); + + // WRITE COMMAND queue size + varclr(&blecfg); + blecfg.conn_cfg.conn_cfg_tag = CONN_CFG_CENTRAL; + blecfg.conn_cfg.params.gattc_conn_cfg.write_cmd_tx_queue_size = Gap._cfg_central.wr_cmd_qsize; + VERIFY_STATUS ( sd_ble_cfg_set(BLE_CONN_CFG_GATTC, &blecfg, ram_start) ); + } + + // Enable BLE stack + // The memory requirement for a specific configuration will not increase + // between SoftDevices with the same major version number + uint32_t err = sd_ble_enable(&ram_start); + if ( err ) + { + LOG_LV1("CFG", "SoftDevice config require more SRAM than provided by linker.\n" + "App Ram Start must be at least 0x%08X (provided 0x%08X)\n" + "Please update linker file or re-config SoftDevice", ram_start, (uint32_t) __data_start__); + } + + LOG_LV1("CFG", "SoftDevice's RAM requires: 0x%08X", ram_start); + VERIFY_STATUS(err); + + /*------------- Configure BLE Option -------------*/ + ble_opt_t opt; + varclr(&opt); + + opt.common_opt.conn_evt_ext.enable = 1; // enable Data Length Extension + VERIFY_STATUS( sd_ble_opt_set(BLE_COMMON_OPT_CONN_EVT_EXT, &opt) ); + + /*------------- Configure GAP -------------*/ + + // Peripheral Preferred Connection Parameters + VERIFY_STATUS( sd_ble_gap_ppcp_set(&_ppcp) ); + + // Default device name + ble_gap_conn_sec_mode_t sec_mode = BLE_SECMODE_OPEN; + VERIFY_STATUS(sd_ble_gap_device_name_set(&sec_mode, (uint8_t const *) CFG_DEFAULT_NAME, strlen(CFG_DEFAULT_NAME))); + + VERIFY_STATUS( sd_ble_gap_appearance_set(BLE_APPEARANCE_UNKNOWN) ); + + //------------- USB -------------// +#if NRF52840_XXAA + sd_power_usbdetected_enable(true); + sd_power_usbpwrrdy_enable(true); + sd_power_usbremoved_enable(true); +#endif + + /*------------- DFU OTA as built-in service -------------*/ + _dfu_svc.begin(); + + if (_central_count) Central.begin(); // Init Central + + // Create RTOS Semaphore & Task for BLE Event + _ble_event_sem = xSemaphoreCreateBinary(); + VERIFY(_ble_event_sem, NRF_ERROR_NO_MEM); + + TaskHandle_t ble_task_hdl; + xTaskCreate( adafruit_ble_task, "BLE", CFG_BLE_TASK_STACKSIZE, NULL, TASK_PRIO_HIGH, &ble_task_hdl); + + // Create RTOS Semaphore & Task for SOC Event + _soc_event_sem = xSemaphoreCreateBinary(); + VERIFY(_soc_event_sem, NRF_ERROR_NO_MEM); + + TaskHandle_t soc_task_hdl; + xTaskCreate( adafruit_soc_task, "SOC", CFG_SOC_TASK_STACKSIZE, NULL, TASK_PRIO_HIGH, &soc_task_hdl); + + // Interrupt priority has already been set by the stack. +// NVIC_SetPriority(SD_EVT_IRQn, 6); + NVIC_EnableIRQ(SD_EVT_IRQn); + + // Create Timer for led advertising blinky + _led_blink_th = xTimerCreate(NULL, ms2tick(CFG_ADV_BLINKY_INTERVAL/2), true, NULL, bluefruit_blinky_cb); + + // Initialize bonding + bond_init(); + + return ERROR_NONE; +} + +/*------------------------------------------------------------------*/ +/* General Functions + *------------------------------------------------------------------*/ +void AdafruitBluefruit::setName (char const * str) +{ + ble_gap_conn_sec_mode_t sec_mode = BLE_SECMODE_OPEN; +sd_ble_gap_device_name_set(&sec_mode, (uint8_t const *) str, strlen(str)); +} + +uint8_t AdafruitBluefruit::getName(char* name, uint16_t bufsize) +{ + VERIFY_STATUS( sd_ble_gap_device_name_get((uint8_t*) name, &bufsize), 0); + return bufsize; +} + +bool AdafruitBluefruit::setTxPower(int8_t power) +{ +#if defined(NRF52832_XXAA) +int8_t const accepted[] = { -40, -20, -16, -12, -8, -4, 0, 3, 4 }; +#elif defined( NRF52840_XXAA) +int8_t const accepted[] = { -40, -20, -16, -12, -8, -4, 0, 2, 3, 4, 5, 6, 7, 8 }; +#endif + + // Check if TX Power is valid value + uint32_t i; + for (i=0; i<sizeof(accepted); i++) + { + if (accepted[i] == power) break; + } + VERIFY(i < sizeof(accepted)); + + // Apply if connected + if ( _conn_hdl != BLE_CONN_HANDLE_INVALID ) + { + VERIFY_STATUS( sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_CONN, _conn_hdl, power), false ); + } + _tx_power = power; + + return true; +} + +int8_t AdafruitBluefruit::getTxPower(void) +{ + return _tx_power; +} + +void AdafruitBluefruit::autoConnLed(bool enabled) +{ + _led_conn = enabled; +} + +void AdafruitBluefruit::setConnLedInterval(uint32_t ms) +{ + BaseType_t active = xTimerIsTimerActive(_led_blink_th); + xTimerChangePeriod(_led_blink_th, ms2tick(ms), 0); + + // Change period of inactive timer will also start it !! + if ( !active ) xTimerStop(_led_blink_th, 0); +} + +bool AdafruitBluefruit::setApperance(uint16_t appear) +{ + return ERROR_NONE == sd_ble_gap_appearance_set(appear); +} + +uint16_t AdafruitBluefruit::getApperance(void) +{ + uint16_t appear = 0; + (void) sd_ble_gap_appearance_get(&appear); + return appear; +} + +/*------------------------------------------------------------------*/ +/* GAP, Connections and Bonding + *------------------------------------------------------------------*/ + +bool AdafruitBluefruit::connected(void) +{ + return ( _conn_hdl != BLE_CONN_HANDLE_INVALID ); +} + +bool AdafruitBluefruit::disconnect(void) +{ + // disconnect if connected + if ( connected() ) + { + return ERROR_NONE == sd_ble_gap_disconnect(_conn_hdl, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); + } + + return true; // not connected still return true +} + +bool AdafruitBluefruit::setConnInterval(uint16_t min, uint16_t max) +{ + _ppcp.min_conn_interval = min; + _ppcp.max_conn_interval = max; + + VERIFY_STATUS( sd_ble_gap_ppcp_set(&_ppcp), false); + + return true; +} + +bool AdafruitBluefruit::setConnIntervalMS(uint16_t min_ms, uint16_t max_ms) +{ + return setConnInterval( MS100TO125(min_ms), MS100TO125(max_ms) ); +} + +bool AdafruitBluefruit::setConnSupervisionTimeout(uint16_t timeout) +{ + _ppcp.conn_sup_timeout = timeout; + + VERIFY_STATUS( sd_ble_gap_ppcp_set(&_ppcp), false); + + return true; +} + +bool AdafruitBluefruit::setConnSupervisionTimeoutMS(uint16_t timeout_ms) +{ + return setConnSupervisionTimeout(timeout_ms / 10); // 10ms unit +} + +void AdafruitBluefruit::setConnectCallback( BLEGap::connect_callback_t fp ) +{ + _connect_cb = fp; +} + +void AdafruitBluefruit::setDisconnectCallback( BLEGap::disconnect_callback_t fp ) +{ + _disconnect_cb = fp; +} + +void AdafruitBluefruit::setEventCallback ( void (*fp) (ble_evt_t*) ) +{ + _event_cb = fp; +} + +uint16_t AdafruitBluefruit::connHandle(void) +{ + return _conn_hdl; +} + +bool AdafruitBluefruit::connPaired(void) +{ + return ( _conn_hdl != BLE_CONN_HANDLE_INVALID ) && Gap.paired(_conn_hdl); +} + +uint16_t AdafruitBluefruit::connInterval(void) +{ + return _conn_interval; +} + +ble_gap_addr_t AdafruitBluefruit::getPeerAddr(void) +{ + return Gap.getPeerAddr(_conn_hdl); +} + +uint8_t AdafruitBluefruit::getPeerAddr(uint8_t addr[6]) +{ + return Gap.getPeerAddr(_conn_hdl, addr); +} + +COMMENT_OUT ( +bool AdafruitBluefruit::setPIN(const char* pin) +{ + VERIFY ( strlen(pin) == BLE_GAP_PASSKEY_LEN ); + + _auth_type = BLE_GAP_AUTH_KEY_TYPE_PASSKEY; + memcpy(_pin, pin, BLE_GAP_PASSKEY_LEN); + +// Config Static Passkey +// ble_opt_t opt +// uint8_t passkey[] = STATIC_PASSKEY; +// m_static_pin_option.gap.passkey.p_passkey = passkey; +//err_code = sd_ble_opt_set(BLE_GAP_OPT_PASSKEY, &m_static_pin_option); + + return true; +} +) + +/*------------------------------------------------------------------*/ +/* Thread & SoftDevice Event handler + *------------------------------------------------------------------*/ +void SD_EVT_IRQHandler(void) +{ + // Notify both BLE & SOC Task + xSemaphoreGiveFromISR(Bluefruit._soc_event_sem, NULL); + xSemaphoreGiveFromISR(Bluefruit._ble_event_sem, NULL); +} + +/** + * Handle SOC event such as FLASH operation + */ +void adafruit_soc_task(void* arg) +{ + (void) arg; + + while (1) + { + if ( xSemaphoreTake(Bluefruit._soc_event_sem, portMAX_DELAY) ) + { + uint32_t soc_evt; + uint32_t err = ERROR_NONE; + + // until no more pending events + while ( NRF_ERROR_NOT_FOUND != (err = sd_evt_get(&soc_evt)) ) + { + if (ERROR_NONE == err) + { + switch (soc_evt) + { + // Flash + case NRF_EVT_FLASH_OPERATION_SUCCESS: + case NRF_EVT_FLASH_OPERATION_ERROR: + LOG_LV1("SOC", "NRF_EVT_FLASH_OPERATION_%s", soc_evt == NRF_EVT_FLASH_OPERATION_SUCCESS ? "SUCCESS" : "ERROR"); + if ( flash_nrf5x_event_cb ) flash_nrf5x_event_cb(soc_evt); + break; + + #ifdef NRF52840_XXAA + /*------------- usb power event handler -------------*/ + case NRF_EVT_POWER_USB_DETECTED: + case NRF_EVT_POWER_USB_POWER_READY: + case NRF_EVT_POWER_USB_REMOVED: + { + int32_t usbevt = (soc_evt == NRF_EVT_POWER_USB_DETECTED ) ? NRFX_POWER_USB_EVT_DETECTED: + (soc_evt == NRF_EVT_POWER_USB_POWER_READY) ? NRFX_POWER_USB_EVT_READY : + (soc_evt == NRF_EVT_POWER_USB_REMOVED ) ? NRFX_POWER_USB_EVT_REMOVED : -1; + + if ( usbevt >= 0) tusb_hal_nrf_power_event(usbevt); + } + break; + #endif + + default: break; + } + } + } + } + } +} + +/*------------------------------------------------------------------*/ +/* BLE Event handler + *------------------------------------------------------------------*/ +void adafruit_ble_task(void* arg) +{ + (void) arg; + + uint8_t * ev_buf = (uint8_t*) rtos_malloc(BLE_EVT_LEN_MAX(BLE_GATT_ATT_MTU_MAX)); + + while (1) + { + if ( xSemaphoreTake(Bluefruit._ble_event_sem, portMAX_DELAY) ) + { + uint32_t err = ERROR_NONE; + + // Until no pending events + while( NRF_ERROR_NOT_FOUND != err ) + { + uint16_t ev_len = BLE_EVT_LEN_MAX(BLE_GATT_ATT_MTU_MAX); + + // Get BLE Event + err = sd_ble_evt_get(ev_buf, &ev_len); + + // Handle valid event, ignore error + if( ERROR_NONE == err) + { + Bluefruit._ble_handler( (ble_evt_t*) ev_buf ); + } + } + } + } +} + +/** + * BLE event handler + * @param evt event + */ +void AdafruitBluefruit::_ble_handler(ble_evt_t* evt) +{ + // conn handle has fixed offset regardless of event type + uint16_t const evt_conn_hdl = evt->evt.common_evt.conn_handle; + + LOG_LV1("BLE", "%s : Conn Handle = %d", dbg_ble_event_str(evt->header.evt_id), evt_conn_hdl); + + // GAP handler + Gap._eventHandler(evt); + Advertising._eventHandler(evt); + Scanner._eventHandler(evt); + + /*------------- BLE Peripheral Events -------------*/ + /* Only handle Peripheral events with matched connection handle + * or a few special one + * - Connected event + * - Advertising timeout (could be connected and advertising at the same time) + * + * Pairing procedure + * - Connect -> SEC_PARAMS_REQUEST -> CONN_SEC_UPDATE -> AUTH_STATUS + * + * Reconnect to a paired device + * - Connect -> SEC_INFO_REQUEST -> CONN_SEC_UPDATE + */ + if ( evt_conn_hdl == _conn_hdl || + evt->header.evt_id == BLE_GAP_EVT_CONNECTED || + evt->header.evt_id == BLE_GAP_EVT_TIMEOUT ) + { + switch ( evt->header.evt_id ) + { + case BLE_GAP_EVT_CONNECTED: + { // Note callback is invoked by BLEGap + ble_gap_evt_connected_t* para = &evt->evt.gap_evt.params.connected; + + if (para->role == BLE_GAP_ROLE_PERIPH) + { + _conn_hdl = evt->evt.gap_evt.conn_handle; + _conn_interval = para->conn_params.min_conn_interval; + + // Connection interval set by Central is out of preferred range + // Try to negotiate with Central using our preferred values + if ( !is_within(_ppcp.min_conn_interval, para->conn_params.min_conn_interval, _ppcp.max_conn_interval) ) + { + // Null, value is set by sd_ble_gap_ppcp_set will be used + VERIFY_STATUS( sd_ble_gap_conn_param_update(_conn_hdl, NULL), ); + } + + if (_connect_cb) ada_callback(NULL, _connect_cb, _conn_hdl); + } + } + break; + + case BLE_GAP_EVT_CONN_PARAM_UPDATE: + { + // Connection Parameter after negotiating with Central + // min conn = max conn = actual used interval + ble_gap_conn_params_t* param = &evt->evt.gap_evt.params.conn_param_update.conn_params; + _conn_interval = param->min_conn_interval; + } + break; + + case BLE_GAP_EVT_DISCONNECTED: + if (_disconnect_cb) ada_callback(NULL, _disconnect_cb, _conn_hdl, evt->evt.gap_evt.params.disconnected.reason); + + LOG_LV2("GAP", "Disconnect Reason 0x%02X", evt->evt.gap_evt.params.disconnected.reason); + + _conn_hdl = BLE_CONN_HANDLE_INVALID; + break; + + case BLE_GATTS_EVT_SYS_ATTR_MISSING: + sd_ble_gatts_sys_attr_set(_conn_hdl, NULL, 0, 0); + break; + + case BLE_EVT_USER_MEM_REQUEST: + // We will handle Long Write sequence (RW Authorize PREP_WRITE_REQ) + sd_ble_user_mem_reply(evt_conn_hdl, NULL); + break; + + case BLE_EVT_USER_MEM_RELEASE: + // nothing to do + break; + + default: break; + } + } + + // Central Event Handler + if (_central_count) + { + // Skip if not central connection + if (evt_conn_hdl != _conn_hdl || + evt_conn_hdl == BLE_CONN_HANDLE_INVALID) + { + Central._event_handler(evt); + } + } + + // Discovery Event Handler + if ( Discovery.begun() ) Discovery._event_handler(evt); + + // GATTs characteristics event handler + Gatt._eventHandler(evt); + + // User callback if set + if (_event_cb) _event_cb(evt); +} + +/*------------------------------------------------------------------*/ +/* Internal Connection LED + *------------------------------------------------------------------*/ +void AdafruitBluefruit::_startConnLed(void) +{ + if (_led_conn) xTimerStart(_led_blink_th, 0); +} + +void AdafruitBluefruit::_stopConnLed(void) +{ + xTimerStop(_led_blink_th, 0); +} + +void AdafruitBluefruit::_setConnLed (bool on_off) +{ + if (_led_conn) + { + digitalWrite(LED_BLUE, on_off ? LED_STATE_ON : (1-LED_STATE_ON) ); + } +} + +/*------------------------------------------------------------------*/ +/* Bonds + *------------------------------------------------------------------*/ +bool AdafruitBluefruit::requestPairing(void) +{ + return Gap.requestPairing(_conn_hdl); +} + +void AdafruitBluefruit::clearBonds(void) +{ + bond_clear_prph(); +} + +//--------------------------------------------------------------------+ +// +//--------------------------------------------------------------------+ + +void Bluefruit_printInfo(void) +{ + Bluefruit.printInfo(); +} + +void AdafruitBluefruit::printInfo(void) +{ + // Skip if Serial is not initialised + if ( !Serial ) return; + + // Skip if Bluefruit.begin() is not called + if ( _ble_event_sem == NULL ) return; + + Serial.println("--------- SoftDevice Config ---------"); + + char const * title_fmt = "%-16s: "; + + /*------------- SoftDevice Config -------------*/ + // Max uuid128 + Serial.printf(title_fmt, "Max UUID128"); + Serial.println(_sd_cfg.uuid128_max); + + // ATTR Table Size + Serial.printf(title_fmt, "ATTR Table Size"); + Serial.println(_sd_cfg.attr_table_size); + + // Service Changed + Serial.printf(title_fmt, "Service Changed"); + Serial.println(_sd_cfg.service_changed); + + if ( _prph_count ) + { + Serial.println("Peripheral Connect Setting"); + + Serial.print(" - "); + Serial.printf(title_fmt, "Max MTU"); + Serial.println(Gap._cfg_prph.mtu_max); + + Serial.print(" - "); + Serial.printf(title_fmt, "Event Length"); + Serial.println(Gap._cfg_prph.event_len); + + Serial.print(" - "); + Serial.printf(title_fmt, "HVN Queue Size"); + Serial.println(Gap._cfg_prph.hvn_tx_qsize); + + Serial.print(" - "); + Serial.printf(title_fmt, "WrCmd Queue Size"); + Serial.println(Gap._cfg_prph.wr_cmd_qsize); + } + + if ( _central_count ) + { + Serial.println("Central Connect Setting"); + + Serial.print(" - "); + Serial.printf(title_fmt, "Max MTU"); + Serial.println(Gap._cfg_central.mtu_max); + + Serial.print(" - "); + Serial.printf(title_fmt, "Event Length"); + Serial.println(Gap._cfg_central.event_len); + + Serial.print(" - "); + Serial.printf(title_fmt, "HVN Queue Size"); + Serial.println(Gap._cfg_central.hvn_tx_qsize); + + Serial.print(" - "); + Serial.printf(title_fmt, "WrCmd Queue Size"); + Serial.println(Gap._cfg_central.wr_cmd_qsize); + } + + /*------------- Settings -------------*/ + Serial.println("\n--------- BLE Settings ---------"); + // Name + Serial.printf(title_fmt, "Name"); + { + char name[32]; + memclr(name, sizeof(name)); + getName(name, sizeof(name)); + Serial.printf(name); + } + Serial.println(); + + // Max Connections + Serial.printf(title_fmt, "Max Connections"); + Serial.printf("Peripheral = %d, ", _prph_count ? 1 : 0); + Serial.printf("Central = %d ", _central_count ? BLE_CENTRAL_MAX_CONN : 0); + Serial.println(); + + // Address + Serial.printf(title_fmt, "Address"); + { + char const * type_str[] = { "Public", "Static", "Private Resolvable", "Private Non Resolvable" }; + uint8_t mac[6]; + uint8_t type = Gap.getAddr(mac); + + // MAC is in little endian --> print reverse + Serial.printBufferReverse(mac, 6, ':'); + Serial.printf(" (%s)", type_str[type]); + } + Serial.println(); + + // Tx Power + Serial.printf(title_fmt, "TX Power"); + Serial.printf("%d dBm", _tx_power); + Serial.println(); + + // Connection Intervals + Serial.printf(title_fmt, "Conn Intervals"); + Serial.printf("min = %.2f ms, ", _ppcp.min_conn_interval*1.25f); + Serial.printf("max = %.2f ms", _ppcp.max_conn_interval*1.25f); + Serial.println(); + + Serial.printf(title_fmt, "Conn Timeout"); + Serial.printf("%.2f ms", _ppcp.conn_sup_timeout*10.0f); + Serial.println(); + + /*------------- List the paried device -------------*/ + if ( _prph_count ) + { + Serial.printf(title_fmt, "Peripheral Paired Devices"); + Serial.println(); + bond_print_list(BLE_GAP_ROLE_PERIPH); + } + + if ( _central_count ) + { + Serial.printf(title_fmt, "Central Paired Devices"); + Serial.println(); + bond_print_list(BLE_GAP_ROLE_CENTRAL); + } + + Serial.println(); +} diff --git a/arduino/libraries/Bluefruit52Lib/src/bluefruit.h b/arduino/libraries/Bluefruit52Lib/src/bluefruit.h new file mode 100755 index 0000000..c33f510 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/bluefruit.h @@ -0,0 +1,247 @@ +/**************************************************************************/ +/*! + @file bluefruit.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#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_PRPH_MAX_CONN 1 +#define BLE_CENTRAL_MAX_CONN 4 +#define BLE_CENTRAL_MAX_SECURE_CONN 1 // should be enough + +#define BLE_MAX_CONN (BLE_CENTRAL_MAX_CONN+BLE_PRPH_MAX_CONN) + +#include "BLEUuid.h" +#include "BLEAdvertising.h" +#include "BLECharacteristic.h" +#include "BLEService.h" + +#include "BLEScanner.h" +#include "BLECentral.h" +#include "BLEClientCharacteristic.h" +#include "BLEClientService.h" +#include "BLEDiscovery.h" +#include "BLEGap.h" +#include "BLEGatt.h" + +// Services +#include "services/BLEDis.h" +#include "services/BLEDfu.h" +#include "services/BLEUart.h" +#include "services/BLEBas.h" +#include "services/BLEBeacon.h" +#include "services/BLEHidGeneric.h" +#include "services/BLEHidAdafruit.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 "utility/AdaCallback.h" +#include "utility/bonding.h" + +enum +{ + BANDWIDTH_AUTO = 0, + BANDWIDTH_LOW, + BANDWIDTH_NORMAL, + BANDWIDTH_HIGH, + BANDWIDTH_MAX, +}; + +extern "C" +{ + void SD_EVT_IRQHandler(void); +} + +class AdafruitBluefruit +{ + public: + AdafruitBluefruit(void); // Constructor + + /*------------------------------------------------------------------*/ + /* Lower Level Classes (Bluefruit.Advertising.*, etc.) + *------------------------------------------------------------------*/ + BLEGap Gap; + BLEGatt Gatt; + + BLEAdvertising Advertising; + BLEAdvertisingData ScanResponse; + BLEScanner Scanner; + BLECentral Central; + 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); + + // Config Bandwidth for connections + void configPrphConn (uint16_t mtu_max, uint8_t event_len, uint8_t hvn_qsize, uint8_t wrcmd_qsize); + void configCentralConn (uint16_t mtu_max, uint8_t event_len, uint8_t hvn_qsize, uint8_t wrcmd_qsize); + + // Convenient function to config connection + void configPrphBandwidth (uint8_t bw); + void configCentralBandwidth(uint8_t bw); + + err_t begin(uint8_t prph_count = 1, uint8_t central_count = 0); + + /*------------------------------------------------------------------*/ + /* General Functions + *------------------------------------------------------------------*/ + void setName (const char* str); + uint8_t getName (char* name, uint16_t bufsize); + + bool setTxPower (int8_t power); + int8_t getTxPower (void); + + bool setApperance (uint16_t appear); + uint16_t getApperance (void); + + void autoConnLed (bool enabled); + void setConnLedInterval (uint32_t ms); + + /*------------------------------------------------------------------*/ + /* GAP, Connections and Bonding + *------------------------------------------------------------------*/ + bool connected (void); + bool disconnect (void); + + bool setConnInterval (uint16_t min, uint16_t max); + bool setConnIntervalMS (uint16_t min_ms, uint16_t max_ms); + bool setConnSupervisionTimeout(uint16_t timeout); + bool setConnSupervisionTimeoutMS(uint16_t timeout_ms); + + uint16_t connHandle (void); + bool connPaired (void); + uint16_t connInterval (void); + + bool requestPairing (void); + void clearBonds (void); + + ble_gap_addr_t getPeerAddr (void); + uint8_t getPeerAddr (uint8_t addr[6]); + + void printInfo(void); + + /*------------------------------------------------------------------*/ + /* Callbacks + *------------------------------------------------------------------*/ + void setConnectCallback ( BLEGap::connect_callback_t fp); + void setDisconnectCallback( BLEGap::disconnect_callback_t fp); + + void setEventCallback ( void (*fp) (ble_evt_t*) ); + + COMMENT_OUT ( bool setPIN(const char* pin); ) + + /*------------------------------------------------------------------*/ + /* 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); + + private: + /*------------- SoftDevice Configuration -------------*/ + struct { + uint32_t attr_table_size; + uint8_t service_changed; + uint8_t uuid128_max; + }_sd_cfg; + + uint8_t _prph_count; + uint8_t _central_count; + + // Peripheral Preferred Connection Parameters (PPCP) + ble_gap_conn_params_t _ppcp; + + // Actual connection interval in use + uint16_t _conn_interval; + + int8_t _tx_power; + + SemaphoreHandle_t _ble_event_sem; + SemaphoreHandle_t _soc_event_sem; + + TimerHandle_t _led_blink_th; + bool _led_conn; + + BLEDfu _dfu_svc; + + uint16_t _conn_hdl; + + BLEGap::connect_callback_t _connect_cb; + BLEGap::disconnect_callback_t _disconnect_cb; + + void (*_event_cb) (ble_evt_t*); + +COMMENT_OUT( + uint8_t _auth_type; + char _pin[BLE_GAP_PASSKEY_LEN]; +) + + /*------------------------------------------------------------------*/ + /* 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 diff --git a/arduino/libraries/Bluefruit52Lib/src/bluefruit_common.h b/arduino/libraries/Bluefruit52Lib/src/bluefruit_common.h new file mode 100755 index 0000000..977d44e --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/bluefruit_common.h @@ -0,0 +1,67 @@ +/**************************************************************************/ +/*! + @file bluefruit_common.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLUEFRUIT_COMMON_H_ +#define BLUEFRUIT_COMMON_H_ + +#include <Arduino.h> +#include "ble.h" +#include "nrf_sdm.h" + +#include "utility/AdaMsg.h" + +#define CFG_MAX_DEVNAME_LEN 32 + +#define BLE_GENERIC_TIMEOUT 100 + +#define BLE_GAP_CONN_SUPERVISION_TIMEOUT_MS 2000 +#define BLE_GAP_CONN_SLAVE_LATENCY 0 + +#define BLE_GAP_CONN_MIN_INTERVAL_DFLT MS100TO125(20) +#define BLE_GAP_CONN_MAX_INTERVAL_DFLT MS100TO125(30) + +// Converts an integer of 1.25ms units to msecs +#define MS100TO125(ms100) (((ms100)*4)/5) + +// Converts an integer of 1.25ms units to msecs +#define MS125TO100(ms125) (((ms125)*5)/4) + +// Converts msec to 0.625 unit +#define MS1000TO625(ms1000) (((ms1000)*8)/5) + +// Converts an integer of 625ms units to msecs +#define MS625TO1000(u625) ( ((u625)*5) / 8 ) + +#endif /* BLUEFRUIT_COMMON_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/clients/BLEAncs.cpp b/arduino/libraries/Bluefruit52Lib/src/clients/BLEAncs.cpp new file mode 100755 index 0000000..e62c116 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/clients/BLEAncs.cpp @@ -0,0 +1,391 @@ +/**************************************************************************/ +/*! + @file BLEAncs.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" + +#define BLE_ANCS_TIMEOUT (5*BLE_GENERIC_TIMEOUT) + +#define DEBUG_ANCS 0 + +void bleancs_notification_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len); +void bleancs_data_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len); + +/* ANCS Service : 7905F431-B5CE-4E99-A40F-4B1E122D00D0 + * Control Point : 69D1D8F3-45E1-49A8-9821-9BBDFDAAD9D9 + * Notification Source : 9FBF120D-6301-42D9-8C58-25E699A21DBD + * Data Source : 22EAC6E9-24D6-4BB5-BE44-B36ACE7C7BFB + */ + +const uint8_t BLEANCS_UUID_SERVICE[] = +{ + 0xD0, 0x00, 0x2D, 0x12, 0x1E, 0x4B, 0x0F, 0xA4, + 0x99, 0x4E, 0xCE, 0xB5, 0x31, 0xF4, 0x05, 0x79 +}; + +const uint8_t BLEANCS_UUID_CHR_CONTROL[] = +{ + 0xD9, 0xD9, 0xAA, 0xFD, 0xBD, 0x9B, 0x21, 0x98, + 0xA8, 0x49, 0xE1, 0x45, 0xF3, 0xD8, 0xD1, 0x69 +}; + +const uint8_t BLEANCS_UUID_CHR_NOTIFICATION[] +{ + 0xBD, 0x1D, 0xA2, 0x99, 0xE6, 0x25, 0x58, 0x8C, + 0xD9, 0x42, 0x01, 0x63, 0x0D, 0x12, 0xBF, 0x9F +}; + +const uint8_t BLEANCS_UUID_CHR_DATA[] = +{ + 0xFB, 0x7B, 0x7C, 0xCE, 0x6A, 0xB3, 0x44, 0xBE, + 0xB5, 0x4B, 0xD6, 0x24, 0xE9, 0xC6, 0xEA, 0x22 +}; + +BLEAncs::BLEAncs(void) + : BLEClientService(BLEANCS_UUID_SERVICE), _control(BLEANCS_UUID_CHR_CONTROL), + _notification(BLEANCS_UUID_CHR_NOTIFICATION), _data(BLEANCS_UUID_CHR_DATA), + _adamsg() +{ + _notif_cb = NULL; +} + +bool BLEAncs::begin(void) +{ + // Invoke base class begin() + BLEClientService::begin(); + + _adamsg.begin(false); + + _control.begin(); + _notification.begin(); + _data.begin(); + + _notification.setNotifyCallback(bleancs_notification_cb); + + // Data Attribute is most likely requested in notification callback + // let's call data's callback in the ble task + _data.setNotifyCallback(bleancs_data_cb, false); + + return true; +} + +bool BLEAncs::discover(uint16_t conn_handle) +{ + // Call BLECentralService discover + VERIFY( BLEClientService::discover(conn_handle) ); + _conn_hdl = BLE_CONN_HANDLE_INVALID; // make as invalid until we found all chars + + // Discover characteristics + BLEClientCharacteristic* chr_arr[] = { &_control, &_notification, &_data }; + + VERIFY( 3 == Bluefruit.Discovery.discoverCharacteristic(conn_handle, chr_arr, 3) ); + + _conn_hdl = conn_handle; + return true; +} + +void BLEAncs::setNotificationCallback(notification_callback_t fp) +{ + _notif_cb = fp; +} + +bool BLEAncs::enableNotification(void) +{ + // enable both Notification & Data Source + VERIFY ( _data.enableNotify() ); + VERIFY ( _notification.enableNotify() ); + + return true; +} + +bool BLEAncs::disableNotification(void) +{ + _notification.disableNotify(); + _data.disableNotify(); + + return true; +} + +/*------------------------------------------------------------------*/ +/* NOTIFICATION + *------------------------------------------------------------------*/ +typedef struct ATTR_PACKED +{ + // Cortex M4 can access unaligned memory + uint8_t cmd; + uint32_t uid; + uint8_t attr; + uint16_t len; // optional when sending command +}get_notif_attr_t; + +VERIFY_STATIC( sizeof(get_notif_attr_t) == 8); + +typedef struct ATTR_PACKED +{ + // Cortex M4 can access unaligned memory + uint8_t cmd; + uint32_t uid; + uint8_t actionid; +}perform_action_t; + +VERIFY_STATIC( sizeof(perform_action_t) == 6); + +/*------------------------------------------------------------------*/ +/* + *------------------------------------------------------------------*/ +uint16_t BLEAncs::getAttribute(uint32_t uid, uint8_t attr, void* buffer, uint16_t bufsize) +{ + VERIFY ( attr < ANCS_ATTR_INVALID, 0); + + // command ID | uid | attr (+ len) + get_notif_attr_t command = + { + .cmd = ANCS_CMD_GET_NOTIFICATION_ATTR, + .uid = uid, + .attr = attr, + .len = bufsize + }; + uint8_t cmdlen = 6; + + // Title, Subtitle, Message must require 2-byte length + if (attr == ANCS_ATTR_TITLE || attr == ANCS_ATTR_SUBTITLE || attr == ANCS_ATTR_MESSAGE) + { + cmdlen = 8; + } + + // Write command using write response +#if DEBUG_ANCS + PRINT_BUFFER(&command, cmdlen); +#endif + + _adamsg.prepare(buffer, bufsize); + VERIFY( cmdlen == _control.write_resp(&command, cmdlen), 0); + VERIFY( _adamsg.waitUntilComplete(BLE_ANCS_TIMEOUT) >= 0, 0); + + // At least 1 packet arrived, enough to know attribute length + uint16_t attr_len = ((get_notif_attr_t*) buffer)->len; + + // wait until all data received Or we run out of memory + while ( ( (attr_len + sizeof(get_notif_attr_t)) > _adamsg.xferlen ) && + ( _adamsg.remaining > 0 ) ) + { + VERIFY( _adamsg.waitUntilComplete(BLE_ANCS_TIMEOUT) >= 0, 0); + } + + // Received length could be less if we run out of buffer + attr_len = _adamsg.xferlen - sizeof(get_notif_attr_t); + + // Shift out the Command data, left only Attribute data + memmove(buffer, ((uint8_t*)buffer) +sizeof(get_notif_attr_t), attr_len); + + // Include null-terminator for some string application + if ( attr_len < bufsize ) + { + ((char*) buffer)[attr_len] = 0; + } + + return attr_len; + +} + +uint16_t BLEAncs::getAppAttribute(const char* appid, uint8_t attr, void* buffer, uint16_t bufsize) +{ + VERIFY ( attr < ANCS_APP_ATTR_INVALID, 0); + + // command ID | App ID (including Null terminator) | Attr + uint8_t cmdlen = 1 + strlen(appid)+1 + 1; + uint8_t* command = (uint8_t*) rtos_malloc( cmdlen ); + + command[0] = ANCS_CMD_GET_APP_ATTR; + strcpy( (char*) command+1, appid); + command[cmdlen-1] = attr; + +#if DEBUG_ANCS + PRINT_BUFFER(command, cmdlen); +#endif + _adamsg.prepare(buffer, bufsize); + + // Write command using write response + if ( cmdlen != _control.write_resp(command, cmdlen) ) + { + rtos_free(command); + return 0; + } + rtos_free(command); + + // Phase 1: Get data until Attribute Length is known + while ( (cmdlen+2) > _adamsg.xferlen && + (_adamsg.remaining > 0) ) + { + VERIFY( _adamsg.waitUntilComplete(BLE_ANCS_TIMEOUT) >= 0, 0); + } + + uint16_t attr_len; + memcpy(&attr_len, ((uint8_t*)buffer)+cmdlen, 2); + + // Phase 2: Get data until all attribute data received + // Or we run out of memory + while ( (attr_len + cmdlen+2) > _adamsg.xferlen && + (_adamsg.remaining > 0) ) + { + VERIFY( _adamsg.waitUntilComplete(BLE_ANCS_TIMEOUT) >= 0, 0); + } + + // Received length could be less if we run out of buffer + attr_len = _adamsg.xferlen - (cmdlen+2); + + // Shift out the Command data, left only Attribute data + memmove(buffer, ((uint8_t*)buffer) +cmdlen+2, attr_len); + + // including null-terminator for some string application + if ( attr_len < bufsize ) + { + ((char*) buffer)[attr_len] = 0; + } + + return attr_len; +} + +bool BLEAncs::performAction(uint32_t uid, uint8_t actionid) +{ + perform_action_t action = + { + .cmd = ANCS_CMD_PERFORM_NOTIFICATION_ACTION, + .uid = uid, + .actionid = actionid + }; + + return sizeof(perform_action_t) == _control.write_resp(&action, sizeof(perform_action_t)); +} + +void BLEAncs::_handleNotification(uint8_t* data, uint16_t len) +{ + if ( len != 8 ) return; + if ( _notif_cb ) _notif_cb((AncsNotification_t*) data); +} + +void BLEAncs::_handleData(uint8_t* data, uint16_t len) +{ +#if DEBUG_ANCS + PRINT_BUFFER(data, len); +#endif + + _adamsg.feed(data, len); + _adamsg.complete(); // mark as complete each time we received data +} + +/*------------------------------------------------------------------*/ +/* High Level API + *------------------------------------------------------------------*/ +uint16_t BLEAncs::getAppID(uint32_t uid, void* buffer, uint16_t bufsize) +{ + return getAttribute(uid, ANCS_ATTR_APP_IDENTIFIER, buffer, bufsize); +} + +uint16_t BLEAncs::getTitle(uint32_t uid, void* buffer, uint16_t bufsize) +{ + return getAttribute(uid, ANCS_ATTR_TITLE, buffer, bufsize); +} + +uint16_t BLEAncs::getSubtitle(uint32_t uid, void* buffer, uint16_t bufsize) +{ + return getAttribute(uid, ANCS_ATTR_SUBTITLE, buffer, bufsize); +} + +uint16_t BLEAncs::getMessage(uint32_t uid, void* buffer, uint16_t bufsize) +{ + return getAttribute(uid, ANCS_ATTR_MESSAGE, buffer, bufsize); +} + +uint16_t BLEAncs::getMessageSize(uint32_t uid) +{ + char buf[20] = { 0 }; + + VERIFY( getAttribute(uid, ANCS_ATTR_MESSAGE_SIZE, buf, sizeof(buf)), 0); + uint16_t result = strtoul(buf, NULL, 10); + + return result; +} + +uint16_t BLEAncs::getDate(uint32_t uid, void* buffer, uint16_t bufsize) +{ + return getAttribute(uid, ANCS_ATTR_DATE, buffer, bufsize); +} + +uint16_t BLEAncs::getPosActionLabel(uint32_t uid, void* buffer, uint16_t bufsize) +{ + return getAttribute(uid, ANCS_ATTR_POSITIVE_ACTION_LABEL, buffer, bufsize); +} + +uint16_t BLEAncs::getNegActionLabel(uint32_t uid, void* buffer, uint16_t bufsize) +{ + return getAttribute(uid, ANCS_ATTR_NEGATIVE_ACTION_LABEL, buffer, bufsize); +} + +uint16_t BLEAncs::getAppName(uint32_t uid, void* buffer, uint16_t bufsize) +{ + // First get AppID + char appID[64] = { 0 }; + VERIFY( getAppID(uid, appID, sizeof(appID)), 0 ); + + // Then get App Display Name + return getAppAttribute(appID, ANCS_APP_ATTR_DISPLAY_NAME, buffer, bufsize); +} + +bool BLEAncs::actPositive(uint32_t uid) +{ + return performAction(uid, ANCS_ACTION_POSITIVE); +} + +bool BLEAncs::actNegative(uint32_t uid) +{ + return performAction(uid, ANCS_ACTION_NEGATIVE); +} + + +/*------------------------------------------------------------------*/ +/* Callback + *------------------------------------------------------------------*/ +void bleancs_notification_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len) +{ + BLEAncs& svc = (BLEAncs&) chr->parentService(); + svc._handleNotification(data, len); +} + +void bleancs_data_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len) +{ + BLEAncs& svc = (BLEAncs&) chr->parentService(); + svc._handleData(data, len); +} diff --git a/arduino/libraries/Bluefruit52Lib/src/clients/BLEAncs.h b/arduino/libraries/Bluefruit52Lib/src/clients/BLEAncs.h new file mode 100755 index 0000000..05d8e29 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/clients/BLEAncs.h @@ -0,0 +1,182 @@ +/**************************************************************************/ +/*! + @file BLEAncs.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLEANCS_H_ +#define BLEANCS_H_ + +#include "../BLEClientCharacteristic.h" +#include "bluefruit_common.h" + +#include "BLEClientService.h" + +extern const uint8_t BLEANCS_UUID_SERVICE[]; +extern const uint8_t BLEANCS_UUID_CHR_CONTROL[]; +extern const uint8_t BLEANCS_UUID_CHR_NOTIFICATION[]; +extern const uint8_t BLEANCS_UUID_CHR_DATA[]; + +// Category ID +enum +{ + ANCS_CAT_OTHER , + ANCS_CAT_INCOMING_CALL , + ANCS_CAT_MISSED_CALL , + ANCS_CAT_VOICE_MAIL , + ANCS_CAT_SOCIAL , + ANCS_CAT_SCHEDULE , + ANCS_CAT_EMAIL , + ANCS_CAT_NEWS , + ANCS_CAT_HEALTH_AND_FITNESS , + ANCS_CAT_BUSSINESS_AND_FINANCE , + ANCS_CAT_LOCATION , + ANCS_CAT_ENTERTAINMENT +}; + +// Event ID +enum +{ + ANCS_EVT_NOTIFICATION_ADDED , + ANCS_EVT_NOTIFICATION_MODIFIED , + ANCS_EVT_NOTIFICATION_REMOVED +}; + +// Command ID +enum +{ + ANCS_CMD_GET_NOTIFICATION_ATTR , + ANCS_CMD_GET_APP_ATTR , + ANCS_CMD_PERFORM_NOTIFICATION_ACTION +}; + +// Notification Attribute ID +enum +{ + ANCS_ATTR_APP_IDENTIFIER , + ANCS_ATTR_TITLE , // followed bye 2-byte length + ANCS_ATTR_SUBTITLE , // followed bye 2-byte length + ANCS_ATTR_MESSAGE , // followed bye 2-byte length + ANCS_ATTR_MESSAGE_SIZE , + ANCS_ATTR_DATE , // UTC#35 yyyyMMdd'T'HHmmSS + ANCS_ATTR_POSITIVE_ACTION_LABEL , + ANCS_ATTR_NEGATIVE_ACTION_LABEL , + + ANCS_ATTR_INVALID +}; + +// Action ID +enum +{ + ANCS_ACTION_POSITIVE, + ANCS_ACTION_NEGATIVE +}; + +// Application Attribute ID +enum +{ + ANCS_APP_ATTR_DISPLAY_NAME, + + ANCS_APP_ATTR_INVALID +}; + +typedef struct +{ + uint8_t eventID; + + struct ATTR_PACKED + { + uint8_t silent : 1; + uint8_t important : 1; + uint8_t preExisting : 1; + uint8_t positiveAction : 1; + uint8_t NegativeAction : 1; + }eventFlags; + + uint8_t categoryID; + uint8_t categoryCount; + uint32_t uid; +} AncsNotification_t; + +VERIFY_STATIC( sizeof(AncsNotification_t) == 8); + +class BLEAncs : public BLEClientService +{ + public: + typedef void (*notification_callback_t) (AncsNotification_t* notif); + + BLEAncs(void); + + virtual bool begin(void); + virtual bool discover(uint16_t conn_handle); + + void setNotificationCallback(notification_callback_t fp); + bool enableNotification(void); + bool disableNotification(void); + + // Main commands + uint16_t getAttribute (uint32_t uid, uint8_t attr, void* buffer, uint16_t bufsize); + uint16_t getAppAttribute (const char* appid, uint8_t attr, void* buffer, uint16_t bufsize); + bool performAction (uint32_t uid, uint8_t actionid); + + // High Level helper + uint16_t getAppName (uint32_t uid, void* buffer, uint16_t bufsize); + uint16_t getAppID (uint32_t uid, void* buffer, uint16_t bufsize); + uint16_t getTitle (uint32_t uid, void* buffer, uint16_t bufsize); + uint16_t getSubtitle (uint32_t uid, void* buffer, uint16_t bufsize); + uint16_t getMessage (uint32_t uid, void* buffer, uint16_t bufsize); + uint16_t getMessageSize (uint32_t uid); + uint16_t getDate (uint32_t uid, void* buffer, uint16_t bufsize); + uint16_t getPosActionLabel (uint32_t uid, void* buffer, uint16_t bufsize); + uint16_t getNegActionLabel (uint32_t uid, void* buffer, uint16_t bufsize); + + bool actPositive (uint32_t uid); + bool actNegative (uint32_t uid); + + private: + BLEClientCharacteristic _control; + BLEClientCharacteristic _notification; + BLEClientCharacteristic _data; + + notification_callback_t _notif_cb; + + AdaMsg _adamsg; + + void _handleNotification(uint8_t* data, uint16_t len); + void _handleData(uint8_t* data, uint16_t len); + + friend void bleancs_notification_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len); + friend void bleancs_data_cb (BLEClientCharacteristic* chr, uint8_t* data, uint16_t len); +}; + + +#endif /* BLEANCS_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientBas.cpp b/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientBas.cpp new file mode 100755 index 0000000..d0d8d07 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientBas.cpp @@ -0,0 +1,87 @@ +/**************************************************************************/ +/*! + @file BLEClientBas.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2019, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" + +BLEClientBas::BLEClientBas(void) + : BLEClientService(UUID16_SVC_BATTERY), _battery(UUID16_CHR_BATTERY_LEVEL) +{ + +} + +bool BLEClientBas::begin(void) +{ + // Invoke base class begin() + BLEClientService::begin(); + + _battery.begin(this); + + return true; +} + +bool BLEClientBas::discover(uint16_t conn_handle) +{ + // Call BLECentralService discover + VERIFY( BLEClientService::discover(conn_handle) ); + _conn_hdl = BLE_CONN_HANDLE_INVALID; // make as invalid until we found all chars + + // Discover TXD, RXD characteristics + VERIFY( 1 == Bluefruit.Discovery.discoverCharacteristic(conn_handle, _battery) ); + + _conn_hdl = conn_handle; + return true; +} + +uint8_t BLEClientBas::read(void) +{ + return _battery.read8(); +} + +bool BLEClientBas::enableNotify(void) +{ + return _battery.enableNotify(); +} + +bool BLEClientBas::disableNotify(void) +{ + return _battery.disableNotify(); +} + +void BLEClientBas::setNotifyCallback(BLEClientCharacteristic::notify_cb_t fp, bool useAdaCallback) +{ + return _battery.setNotifyCallback(fp, useAdaCallback); +} + diff --git a/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientBas.h b/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientBas.h new file mode 100755 index 0000000..5eadc4f --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientBas.h @@ -0,0 +1,63 @@ +/**************************************************************************/ +/*! + @file BLEClientBas.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2019, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef BLECLIENTBAS_H_ +#define BLECLIENTBAS_H_ + +#include "bluefruit_common.h" +#include "BLEClientCharacteristic.h" +#include "BLEClientService.h" + +class BLEClientBas : public BLEClientService +{ + public: + BLEClientBas(void); + + virtual bool begin(void); + virtual bool discover(uint16_t conn_handle); + + uint8_t read(void); + + bool enableNotify(void); + bool disableNotify(void); + + void setNotifyCallback(BLEClientCharacteristic::notify_cb_t fp, bool useAdaCallback = true); + + private: + BLEClientCharacteristic _battery; +}; + +#endif /* BLECLIENTBAS_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientCts.cpp b/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientCts.cpp new file mode 100755 index 0000000..d88ef62 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientCts.cpp @@ -0,0 +1,113 @@ +/**************************************************************************/ +/*! + @file BLEClientCts.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" + +void blects_central_notify_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len); + +BLEClientCts::BLEClientCts(void) + : BLEClientService(UUID16_SVC_CURRENT_TIME), _cur_time(UUID16_CHR_CURRENT_TIME), _local_info(UUID16_CHR_LOCAL_TIME_INFORMATION) +{ + varclr(&Time); + varclr(&LocalInfo); + + _adjust_cb = NULL; +} + +bool BLEClientCts::begin(void) +{ + VERIFY_STATIC(sizeof(Time) == 10); + VERIFY_STATIC(sizeof(LocalInfo) == 2); + + // Invoke base class begin() + BLEClientService::begin(); + + _cur_time.begin(this); + _cur_time.setNotifyCallback(blects_central_notify_cb); + + _local_info.begin(this); + + return true; +} + +bool BLEClientCts::discover(uint16_t conn_handle) +{ + // Call Base class discover + VERIFY( BLEClientService::discover(conn_handle) ); + _conn_hdl = BLE_CONN_HANDLE_INVALID; // make as invalid until we found all chars + + // Discover characteristics + Bluefruit.Discovery.discoverCharacteristic(conn_handle, _cur_time, _local_info); + + // Current Time chars is mandatory + VERIFY( _cur_time.valueHandle() != BLE_GATT_HANDLE_INVALID, false); + + _conn_hdl = conn_handle; + return true; +} + +bool BLEClientCts::getCurrentTime(void) +{ + return _cur_time.read(&Time, sizeof(Time)) > 0; +} + +bool BLEClientCts::getLocalTimeInfo(void) +{ + return _local_info.read(&LocalInfo, sizeof(LocalInfo)) > 0; +} + +bool BLEClientCts::enableAdjust(void) +{ + return _cur_time.enableNotify(); +} + +void BLEClientCts::setAdjustCallback(adjust_callback_t fp) +{ + _adjust_cb = fp; +} + +void BLEClientCts::_cur_time_notify_cb(uint8_t* data, uint16_t len) +{ + memcpy(&Time, data, len); + + // invoked callback if set + if ( _adjust_cb ) _adjust_cb( Time.adjust_reason ); +} + +void blects_central_notify_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len) +{ + ((BLEClientCts&) chr->parentService())._cur_time_notify_cb(data, len); +} diff --git a/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientCts.h b/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientCts.h new file mode 100755 index 0000000..872a869 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientCts.h @@ -0,0 +1,93 @@ +/**************************************************************************/ +/*! + @file BLEClientCts.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLECLIENTCTS_H_ +#define BLECLIENTCTS_H_ + +#include "bluefruit_common.h" +#include "BLEClientCharacteristic.h" +#include "BLEClientService.h" + +class BLEClientCts : public BLEClientService +{ + public: + // Callback Signatures + typedef void (*adjust_callback_t) (uint8_t reason); + + BLEClientCts(void); + + virtual bool begin(void); + virtual bool discover(uint16_t conn_handle); + + bool getCurrentTime(void); + bool getLocalTimeInfo(void); + + bool enableAdjust(void); + void setAdjustCallback(adjust_callback_t fp); + + // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.current_time.xml + struct ATTR_PACKED { + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; + uint8_t weekday; + uint8_t subsecond; + uint8_t adjust_reason; + } Time; + + // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.local_time_information.xml + struct ATTR_PACKED { + int8_t timezone; + uint8_t dst_offset; + }LocalInfo; + + /*------------------------------------------------------------------*/ + /* INTERNAL USAGE ONLY + * Although declare as public, it is meant to be invoked by internal + * code. User should not call these directly + *------------------------------------------------------------------*/ + void _cur_time_notify_cb(uint8_t* data, uint16_t len); + + private: + BLEClientCharacteristic _cur_time; + BLEClientCharacteristic _local_info; + + adjust_callback_t _adjust_cb; +}; + +#endif /* BLECLIENTCTS_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientDis.cpp b/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientDis.cpp new file mode 100755 index 0000000..2a9b387 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientDis.cpp @@ -0,0 +1,111 @@ +/**************************************************************************/ +/*! + @file BLEClientDis.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" + +BLEClientDis::BLEClientDis(void) + : BLEClientService(UUID16_SVC_DEVICE_INFORMATION) +{ + +} + +bool BLEClientDis::begin(void) +{ + // Invoke base class begin() + BLEClientService::begin(); + + return true; +} + +bool BLEClientDis::discover(uint16_t conn_handle) +{ + // Call BLECentralService discover + VERIFY( BLEClientService::discover(conn_handle) ); + + return true; +} + +uint16_t BLEClientDis::getChars(uint16_t uuid, char* buffer, uint16_t bufsize) +{ + uint16_t count = 0; + ble_gattc_handle_range_t bck_range = Bluefruit.Discovery.getHandleRange(); + + // Set discovery handle to DIS service + Bluefruit.Discovery.setHandleRange(_hdl_range); + + BLEClientCharacteristic chr(uuid); + chr.begin(this); + + if ( Bluefruit.Discovery.discoverCharacteristic(_conn_hdl, chr) ) + { + count = chr.read(buffer, bufsize); + } + + // Set back + Bluefruit.Discovery.setHandleRange(bck_range); + + return count; +} + +uint16_t BLEClientDis::getModel(char* buffer, uint16_t bufsize) +{ + return getChars(UUID16_CHR_MODEL_NUMBER_STRING, buffer, bufsize); +} + +uint16_t BLEClientDis::getSerial(char* buffer, uint16_t bufsize) +{ + return getChars(UUID16_CHR_SERIAL_NUMBER_STRING, buffer, bufsize); +} + +uint16_t BLEClientDis::getFirmwareRev(char* buffer, uint16_t bufsize) +{ + return getChars(UUID16_CHR_FIRMWARE_REVISION_STRING, buffer, bufsize); +} + +uint16_t BLEClientDis::getHardwareRev(char* buffer, uint16_t bufsize) +{ + return getChars(UUID16_CHR_HARDWARE_REVISION_STRING, buffer, bufsize); +} + +uint16_t BLEClientDis::getSoftwareRev(char* buffer, uint16_t bufsize) +{ + return getChars(UUID16_CHR_SOFTWARE_REVISION_STRING, buffer, bufsize); +} + +uint16_t BLEClientDis::getManufacturer(char* buffer, uint16_t bufsize) +{ + return getChars(UUID16_CHR_MANUFACTURER_NAME_STRING, buffer, bufsize); +} diff --git a/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientDis.h b/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientDis.h new file mode 100755 index 0000000..056c6d8 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientDis.h @@ -0,0 +1,69 @@ +/**************************************************************************/ +/*! + @file BLEClientDis.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#ifndef BLECLIENTDIS_H_ +#define BLECLIENTDIS_H_ + +#include "bluefruit_common.h" +#include "BLEClientCharacteristic.h" +#include "BLEClientService.h" + +class BLEClientDis : public BLEClientService +{ + public: + BLEClientDis(void); + + virtual bool begin(void); + virtual bool discover(uint16_t conn_handle); + + uint16_t getChars(uint16_t uuid, char* buffer, uint16_t bufsize); + + uint16_t getModel (char* buffer, uint16_t bufsize); + uint16_t getSerial (char* buffer, uint16_t bufsize); + uint16_t getFirmwareRev (char* buffer, uint16_t bufsize); + uint16_t getHardwareRev (char* buffer, uint16_t bufsize); + uint16_t getSoftwareRev (char* buffer, uint16_t bufsize); + uint16_t getManufacturer(char* buffer, uint16_t bufsize); + + private: + + // BLE DIS has several characteristics but is often used one or two times + // It is better to implement get() with on-the-fly BLEClientCharacteristic +}; + + + +#endif /* BLECLIENTDIS_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientHidAdafruit.cpp b/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientHidAdafruit.cpp new file mode 100755 index 0000000..cd5cfe4 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientHidAdafruit.cpp @@ -0,0 +1,194 @@ +/**************************************************************************/ +/*! + @file BLEClientHidAdafruit.cpp + @author hathach (tinyusb.org) (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" + +BLEClientHidAdafruit::BLEClientHidAdafruit(void) + : BLEClientService(UUID16_SVC_HUMAN_INTERFACE_DEVICE), + _protcol_mode(UUID16_CHR_PROTOCOL_MODE), + _kbd_boot_input(UUID16_CHR_BOOT_KEYBOARD_INPUT_REPORT), _kbd_boot_output(UUID16_CHR_BOOT_KEYBOARD_OUTPUT_REPORT), + _mse_boot_input(UUID16_CHR_BOOT_MOUSE_INPUT_REPORT), + _hid_info(UUID16_CHR_HID_INFORMATION), _hid_control(UUID16_CHR_HID_CONTROL_POINT) +{ + _kbd_cb = NULL; + _mse_cb = NULL; + varclr(&_last_kbd_report); + varclr(&_last_mse_report); +} + + +void kbd_client_notify_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len) +{ + BLEClientHidAdafruit& svc = (BLEClientHidAdafruit&) chr->parentService(); + svc._handle_kbd_input(data, len); +} + +void mse_client_notify_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len) +{ + BLEClientHidAdafruit& svc = (BLEClientHidAdafruit&) chr->parentService(); + svc._handle_mse_input(data, len); +} + + +bool BLEClientHidAdafruit::begin(void) +{ + // Invoke base class begin() + BLEClientService::begin(); + + _protcol_mode.begin(this); + _hid_info.begin(this); + _hid_control.begin(this); + + _kbd_boot_input.begin(this); + _kbd_boot_output.begin(this); + + _mse_boot_input.begin(this); + + + // set notify callback + _kbd_boot_input.setNotifyCallback(kbd_client_notify_cb); + _mse_boot_input.setNotifyCallback(mse_client_notify_cb); +} + +void BLEClientHidAdafruit::setKeyboardReportCallback(kbd_callback_t fp) +{ + _kbd_cb = fp; +} + +void BLEClientHidAdafruit::setMouseReportCallback(mse_callback_t fp) +{ + _mse_cb = fp; +} + +bool BLEClientHidAdafruit::discover(uint16_t conn_handle) +{ + // Call Base class discover + VERIFY( BLEClientService::discover(conn_handle) ); + _conn_hdl = BLE_CONN_HANDLE_INVALID; // make as invalid until we found all chars + + // Discover all characteristics + Bluefruit.Discovery.discoverCharacteristic(conn_handle, _protcol_mode, _kbd_boot_input, _kbd_boot_output, _mse_boot_input, _hid_info, _hid_control); + + VERIFY( _protcol_mode.discovered() && _hid_info.discovered() && _hid_control.discovered() ); + VERIFY ( keyboardPresent() || mousePresent() ); + + _conn_hdl = conn_handle; + return true; +} + +/*------------------------------------------------------------------*/ +/* Info + *------------------------------------------------------------------*/ +bool BLEClientHidAdafruit::getHidInfo(uint8_t info[4]) +{ + return 4 == _hid_info.read(info, 4); +} + +uint8_t BLEClientHidAdafruit::getCountryCode(void) +{ + uint8_t info[4] = { 0 }; + getHidInfo(info); + + return info[2]; +} + +bool BLEClientHidAdafruit::setProtocolMode(uint8_t mode) +{ + return _protcol_mode.write8(mode); +} + +/*------------------------------------------------------------------*/ +/* Keyboard + *------------------------------------------------------------------*/ +bool BLEClientHidAdafruit::keyboardPresent(void) +{ + return _kbd_boot_input.discovered() && _kbd_boot_output.discovered(); +} + +bool BLEClientHidAdafruit::enableKeyboard(void) +{ + _kbd_boot_input.enableNotify(); +} + +bool BLEClientHidAdafruit::disableKeyboard(void) +{ + _kbd_boot_input.disableNotify(); +} + +void BLEClientHidAdafruit::_handle_kbd_input(uint8_t* data, uint16_t len) +{ + varclr(&_last_kbd_report); + memcpy(&_last_kbd_report, data, len); + + if ( _kbd_cb ) _kbd_cb(&_last_kbd_report); +} + +void BLEClientHidAdafruit::getKeyboardReport(hid_keyboard_report_t* report) +{ + memcpy(report, &_last_kbd_report, sizeof(hid_keyboard_report_t)); +} + +/*------------------------------------------------------------------*/ +/* Mouse + *------------------------------------------------------------------*/ +bool BLEClientHidAdafruit::mousePresent(void) +{ + return _mse_boot_input.discovered(); +} + +bool BLEClientHidAdafruit::enableMouse(void) +{ + _mse_boot_input.enableNotify(); +} + +bool BLEClientHidAdafruit::disableMouse(void) +{ + _mse_boot_input.disableNotify(); +} + +void BLEClientHidAdafruit::_handle_mse_input(uint8_t* data, uint16_t len) +{ + varclr(&_last_mse_report); + memcpy(&_last_mse_report, data, len); + + if ( _mse_cb ) _mse_cb(&_last_mse_report); +} + +void BLEClientHidAdafruit::getMouseReport(hid_mouse_report_t* report) +{ + memcpy(report, &_last_mse_report, sizeof(hid_mouse_report_t)); +} + diff --git a/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientHidAdafruit.h b/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientHidAdafruit.h new file mode 100755 index 0000000..04140bc --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientHidAdafruit.h @@ -0,0 +1,107 @@ +/**************************************************************************/ +/*! + @file BLEClientHidAdafruit.h + @author hathach (tinyusb.org) (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLECLIENTHIDADAFRUIT_H_ +#define BLECLIENTHIDADAFRUIT_H_ + +#include "bluefruit_common.h" +#include "BLEClientCharacteristic.h" +#include "BLEClientService.h" + +#include "services/BLEHidGeneric.h" + +// Only support Boot Keyboard and/or Boot Mouse, there is no Consumer Control support +class BLEClientHidAdafruit : public BLEClientService +{ + public: + // Callback Signatures + typedef void (*kbd_callback_t ) (hid_keyboard_report_t* report); + typedef void (*mse_callback_t ) (hid_mouse_report_t* report); + + BLEClientHidAdafruit(void); + + virtual bool begin(void); + virtual bool discover(uint16_t conn_handle); + + bool getHidInfo(uint8_t info[4]); + uint8_t getCountryCode(void); + + bool setProtocolMode(uint8_t mode); + + // Keyboard API + bool keyboardPresent(void); + bool enableKeyboard(void); + bool disableKeyboard(void); + + void getKeyboardReport(hid_keyboard_report_t* report); + + // Mouse API + bool mousePresent(void); + bool enableMouse(void); + bool disableMouse(void); + + void getMouseReport(hid_mouse_report_t* report); + + // Report callback + void setKeyboardReportCallback(kbd_callback_t fp); + void setMouseReportCallback(mse_callback_t fp); + + protected: + kbd_callback_t _kbd_cb; + mse_callback_t _mse_cb; + + hid_keyboard_report_t _last_kbd_report; + hid_mouse_report_t _last_mse_report; + + // Only support Boot protocol for keyboard and Mouse + BLEClientCharacteristic _protcol_mode; + BLEClientCharacteristic _hid_info; + BLEClientCharacteristic _hid_control; + + BLEClientCharacteristic _kbd_boot_input; + BLEClientCharacteristic _kbd_boot_output; + + BLEClientCharacteristic _mse_boot_input; + + void _handle_kbd_input(uint8_t* data, uint16_t len); + void _handle_mse_input(uint8_t* data, uint16_t len); + + friend void kbd_client_notify_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len); + friend void mse_client_notify_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len); +}; + + + +#endif /* BLECLIENTHIDADAFRUIT_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientUart.cpp b/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientUart.cpp new file mode 100755 index 0000000..9ba507e --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientUart.cpp @@ -0,0 +1,147 @@ +/**************************************************************************/ +/*! + @file BLEClientUart.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" + +void bleuart_central_notify_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len); + +BLEClientUart::BLEClientUart(uint16_t fifo_depth) + : BLEClientService(BLEUART_UUID_SERVICE), _txd(BLEUART_UUID_CHR_TXD), _rxd(BLEUART_UUID_CHR_RXD), + _rx_fifo(1, fifo_depth) +{ + _rx_cb = NULL; +} + +bool BLEClientUart::begin(void) +{ + _rx_fifo.begin(); + + // Invoke base class begin() + BLEClientService::begin(); + + _rxd.begin(this); + _txd.begin(this); + + // set up notify callback + _txd.setNotifyCallback(bleuart_central_notify_cb); + + return true; +} + +bool BLEClientUart::enableTXD(void) +{ + return _txd.enableNotify(); +} + +bool BLEClientUart::disableTXD(void) +{ + return _txd.disableNotify(); +} + +void BLEClientUart::setRxCallback( rx_callback_t fp) +{ + _rx_cb = fp; +} + +bool BLEClientUart::discover(uint16_t conn_handle) +{ + // Call Base class discover + VERIFY( BLEClientService::discover(conn_handle) ); + _conn_hdl = BLE_CONN_HANDLE_INVALID; // make as invalid until we found all chars + + // Discover TXD, RXD characteristics + VERIFY( 2 == Bluefruit.Discovery.discoverCharacteristic(conn_handle, _rxd, _txd) ); + + _conn_hdl = conn_handle; + return true; +} + +void BLEClientUart::disconnect(void) +{ + BLEClientService::disconnect(); + + flush(); +} + +void bleuart_central_notify_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len) +{ + BLEClientUart& uart_svc = (BLEClientUart&) chr->parentService(); + uart_svc._rx_fifo.write(data, len); + + // invoke callback + if ( uart_svc._rx_cb ) uart_svc._rx_cb(uart_svc); +} + +/*------------------------------------------------------------------*/ +/* STREAM API + *------------------------------------------------------------------*/ +int BLEClientUart::read (void) +{ + uint8_t ch; + return read(&ch, 1) ? (int) ch : EOF; +} + +int BLEClientUart::read (uint8_t * buf, size_t size) +{ + return _rx_fifo.read(buf, size); +} + +size_t BLEClientUart::write (uint8_t b) +{ + return write(&b, 1); +} + +size_t BLEClientUart::write (const uint8_t *content, size_t len) +{ + // write without response + return _rxd.write(content, len); +} + +int BLEClientUart::available (void) +{ + return _rx_fifo.count(); +} + +int BLEClientUart::peek (void) +{ + uint8_t ch; + return _rx_fifo.peek(&ch) ? (int) ch : EOF; +} + +void BLEClientUart::flush (void) +{ + _rx_fifo.clear(); +} diff --git a/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientUart.h b/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientUart.h new file mode 100755 index 0000000..168a172 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/clients/BLEClientUart.h @@ -0,0 +1,89 @@ +/**************************************************************************/ +/*! + @file BLECentralUart.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLECLIENTUART_H_ +#define BLECLIENTUART_H_ + +#include "bluefruit_common.h" +#include "utility/adafruit_fifo.h" + +#include "BLEClientCharacteristic.h" +#include "BLEClientService.h" + +#include "services/BLEUart.h" + +class BLEClientUart : public BLEClientService, public Stream +{ + public: + // Callback Signatures + typedef void (*rx_callback_t) (BLEClientUart& svc); + + BLEClientUart(uint16_t fifo_depth = BLE_UART_DEFAULT_FIFO_DEPTH); + + virtual bool begin(void); + virtual bool discover(uint16_t conn_handle); + + void setRxCallback( rx_callback_t fp); + + bool enableTXD(void); + bool disableTXD(void); + + // Stream API + virtual int read ( void ); + virtual int read ( uint8_t * buf, size_t size ); + int read ( char * buf, size_t size ) { return read( (uint8_t*) buf, size); } + virtual size_t write ( uint8_t b ); + virtual size_t write ( const uint8_t *content, size_t len ); + virtual int available ( void ); + virtual int peek ( void ); + virtual void flush ( void ); + + // pull in write(str) and write(buf, size) from Print + using Print::write; + + protected: + virtual void disconnect(void); + + private: + BLEClientCharacteristic _txd; + BLEClientCharacteristic _rxd; + + Adafruit_FIFO _rx_fifo; + rx_callback_t _rx_cb; + + friend void bleuart_central_notify_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len); +}; + +#endif /* BLECLIENTUART_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/services/BLEBas.cpp b/arduino/libraries/Bluefruit52Lib/src/services/BLEBas.cpp new file mode 100755 index 0000000..2cfd172 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/services/BLEBas.cpp @@ -0,0 +1,66 @@ +/**************************************************************************/ +/*! + @file BLEBas.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" + +BLEBas::BLEBas(void) : + BLEService(UUID16_SVC_BATTERY), _battery(UUID16_CHR_BATTERY_LEVEL) +{ + +} + +err_t BLEBas::begin(void) +{ + // Invoke base class begin() + VERIFY_STATUS( BLEService::begin() ); + + _battery.setProperties(CHR_PROPS_READ | CHR_PROPS_NOTIFY); // could support notify + _battery.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); + _battery.setFixedLen(1); + VERIFY_STATUS( _battery.begin() ); + + return ERROR_NONE; +} + +bool BLEBas::write(uint8_t level) +{ + return _battery.write8(level) > 0; +} + +bool BLEBas::notify(uint8_t level) +{ + return _battery.notify8(level); +} diff --git a/arduino/libraries/Bluefruit52Lib/src/services/BLEBas.h b/arduino/libraries/Bluefruit52Lib/src/services/BLEBas.h new file mode 100755 index 0000000..d091ce5 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/services/BLEBas.h @@ -0,0 +1,60 @@ +/**************************************************************************/ +/*! + @file BLEBas.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLEBAS_H_ +#define BLEBAS_H_ + +#include "bluefruit_common.h" + +#include "BLECharacteristic.h" +#include "BLEService.h" + +class BLEBas : public BLEService +{ + protected: + BLECharacteristic _battery; + + public: + BLEBas(void); + + virtual err_t begin(void); + + bool write (uint8_t level); + bool notify(uint8_t level); +}; + + + +#endif /* BLEBAS_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/services/BLEBeacon.cpp b/arduino/libraries/Bluefruit52Lib/src/services/BLEBeacon.cpp new file mode 100755 index 0000000..21d3120 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/services/BLEBeacon.cpp @@ -0,0 +1,126 @@ +/**************************************************************************/ +/*! + @file BLEBeacon.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" + +void BLEBeacon::_init(void) +{ + _manufacturer_id = UUID16_COMPANY_ID_APPLE; // default is Apple + _uuid128 = NULL; + + _major_be = _minor_be = 0; + _rssi_at_1m = -54; +} + +BLEBeacon::BLEBeacon(void) +{ + _init(); +} + +BLEBeacon::BLEBeacon(uint8_t const uuid128[16]) +{ + _init(); + _uuid128 = uuid128; +} + +BLEBeacon::BLEBeacon(uint8_t const uuid128[16], uint16_t major, uint16_t minor, int8_t rssi) +{ + _init(); + _uuid128 = uuid128; + _major_be = __swap16(major); + _minor_be = __swap16(minor); + _rssi_at_1m = rssi; +} + +void BLEBeacon::setManufacturer(uint16_t manfacturer) +{ + _manufacturer_id = manfacturer; +} + +void BLEBeacon::setUuid(uint8_t const uuid128[16]) +{ + _uuid128 = uuid128; +} + +void BLEBeacon::setMajorMinor(uint16_t major, uint16_t minor) +{ + _major_be = major; + _minor_be = minor; +} + +void BLEBeacon::setRssiAt1m(int8_t rssi) +{ + _rssi_at_1m = rssi; +} + +bool BLEBeacon::start(void) +{ + return start(Bluefruit.Advertising); +} + +bool BLEBeacon::start(BLEAdvertising& adv) +{ + adv.clearData(); + + struct ATTR_PACKED + { + uint16_t manufacturer; + + uint8_t beacon_type; + uint8_t beacon_len; + + uint8_t uuid128[16]; + uint16_t major; + uint16_t minor; + int8_t rssi_at_1m; + } beacon_data = + { + .manufacturer = _manufacturer_id, + .beacon_type = 0x02, + .beacon_len = sizeof(beacon_data) - 4, // len of uuid + major + minor + rssi + .uuid128 = { 0 }, + .major = _major_be, + .minor = _minor_be, + .rssi_at_1m = _rssi_at_1m + }; + + VERIFY_STATIC(sizeof(beacon_data) == 25); + + memcpy(beacon_data.uuid128, _uuid128, 16); + + adv.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE); + return adv.addData(BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA, &beacon_data, sizeof(beacon_data)); +} diff --git a/arduino/libraries/Bluefruit52Lib/src/services/BLEBeacon.h b/arduino/libraries/Bluefruit52Lib/src/services/BLEBeacon.h new file mode 100755 index 0000000..d138813 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/services/BLEBeacon.h @@ -0,0 +1,73 @@ +/**************************************************************************/ +/*! + @file BLEBeacon.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLEBEACON_H_ +#define BLEBEACON_H_ + +#include "bluefruit_common.h" + +#include "BLECharacteristic.h" +#include "BLEService.h" + +class BLEAdvertising; // forward declare + +class BLEBeacon +{ + private: + uint16_t _manufacturer_id; + uint8_t const* _uuid128; + uint16_t _major_be; // Big Endian + uint16_t _minor_be; // Big Endian + int8_t _rssi_at_1m; + + void _init(void); + + public: + BLEBeacon(void); + BLEBeacon(uint8_t const uuid128[16]); + BLEBeacon(uint8_t const uuid128[16], uint16_t major, uint16_t minor, int8_t rssi); + + void setManufacturer(uint16_t manfacturer); + void setUuid(uint8_t const uuid128[16]); + void setMajorMinor(uint16_t major, uint16_t minor); + void setRssiAt1m(int8_t rssi); + + bool start(void); + bool start(BLEAdvertising& adv); +}; + + + +#endif /* BLEBEACON_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/services/BLEDfu.cpp b/arduino/libraries/Bluefruit52Lib/src/services/BLEDfu.cpp new file mode 100755 index 0000000..3ff2516 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/services/BLEDfu.cpp @@ -0,0 +1,223 @@ +/**************************************************************************/ +/*! + @file BLEDfu.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" + +#define DFU_REV_APPMODE 0x0001 + +/* DFU Serivce : 00001530-1212-EFDE-1523-785FEABCD123 + * DFU Control : 00001531-1212-EFDE-1523-785FEABCD123 + * DFU Packet : 00001532-1212-EFDE-1523-785FEABCD123 + * DFU Revision: 00001534-1212-EFDE-1523-785FEABCD123 + */ + +const uint8_t UUID128_SVC_DFU_OTA[16] = +{ + 0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, + 0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00 +}; + + +const uint8_t UUID128_CHR_DFU_CONTROL[16] = +{ + 0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, + 0xDE, 0xEF, 0x12, 0x12, 0x31, 0x15, 0x00, 0x00 +}; + +const uint8_t UUID128_CHR_DFU_PACKET[16] = +{ + 0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, + 0xDE, 0xEF, 0x12, 0x12, 0x32, 0x15, 0x00, 0x00 +}; + +const uint8_t UUID128_CHR_DFU_REVISON[16] = +{ + 0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, + 0xDE, 0xEF, 0x12, 0x12, 0x34, 0x15, 0x00, 0x00 +}; + +extern "C" void bootloader_util_app_start(uint32_t start_addr); + +static uint16_t crc16(const uint8_t* data_p, uint8_t length) +{ + uint8_t x; + uint16_t crc = 0xFFFF; + + while ( length-- ) + { + x = crc >> 8 ^ *data_p++; + x ^= x >> 4; + crc = (crc << 8) ^ ((uint16_t) (x << 12)) ^ ((uint16_t) (x << 5)) ^ ((uint16_t) x); + } + return crc; +} + +static void bledfu_control_wr_authorize_cb(BLECharacteristic& chr, ble_gatts_evt_write_t* request) +{ + if ( (request->handle == chr.handles().value_handle) && + (request->op != BLE_GATTS_OP_PREP_WRITE_REQ) && + (request->op != BLE_GATTS_OP_EXEC_WRITE_REQ_NOW) && + (request->op != BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL)) + { + uint16_t conn_hdl = Bluefruit.connHandle(); + + ble_gatts_rw_authorize_reply_params_t reply = { .type = BLE_GATTS_AUTHORIZE_TYPE_WRITE }; + + if ( !chr.notifyEnabled() ) + { + reply.params.write.gatt_status = BLE_GATT_STATUS_ATTERR_CPS_CCCD_CONFIG_ERROR; + sd_ble_gatts_rw_authorize_reply(conn_hdl, &reply); + return; + } + + reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS; + sd_ble_gatts_rw_authorize_reply(conn_hdl, &reply); + + enum { START_DFU = 1 }; + if ( request->data[0] == START_DFU ) + { + // Peer data information so that bootloader could re-connect after reboot + typedef struct { + ble_gap_addr_t addr; + ble_gap_irk_t irk; + ble_gap_enc_key_t enc_key; + uint8_t sys_attr[8]; + uint16_t crc16; + }peer_data_t; + + VERIFY_STATIC(offsetof(peer_data_t, crc16) == 60); + + /* Save Peer data + * Peer data address is defined in bootloader linker @0x20007F80 + * - If bonded : save Security information + * - Otherwise : save Address for direct advertising + * + * TODO may force bonded only for security reason + */ + peer_data_t* peer_data = (peer_data_t*) (0x20007F80UL); + varclr(peer_data); + + // Get CCCD + uint16_t sysattr_len = sizeof(peer_data->sys_attr); + sd_ble_gatts_sys_attr_get(conn_hdl, peer_data->sys_attr, &sysattr_len, BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS); + + // Get Bond Data or using Address if not bonded + peer_data->addr = Bluefruit.Gap.getPeerAddr(conn_hdl); + + if ( Bluefruit.Gap.paired(conn_hdl) ) + { + bond_keys_t bkeys; + + if ( bond_load_keys( BLE_GAP_ROLE_PERIPH, Bluefruit.Gap._get_peer(conn_hdl)->ediv, &bkeys ) ) + { + peer_data->addr = bkeys.peer_id.id_addr_info; + peer_data->irk = bkeys.peer_id.id_info; + peer_data->enc_key = bkeys.own_enc; + } + } + + // Calculate crc + peer_data->crc16 = crc16((uint8_t*) peer_data, offsetof(peer_data_t, crc16)); + + // Initiate DFU Sequence and reboot into DFU OTA mode + Bluefruit.Advertising.restartOnDisconnect(false); + Bluefruit.disconnect(); + + // Set GPReset to DFU OTA + enum { DFU_OTA_MAGIC = 0xB1 }; + + sd_power_gpregret_clr(0, 0xFF); + VERIFY_STATUS( sd_power_gpregret_set(0, DFU_OTA_MAGIC), ); + VERIFY_STATUS( sd_softdevice_disable(), ); + + // Disable all interrupts + #if defined(NRF52832_XXAA) + #define MAX_NUMBER_INTERRUPTS 39 + #elif defined(NRF52840_XXAA) + #define MAX_NUMBER_INTERRUPTS 48 + #endif + + NVIC_ClearPendingIRQ(SD_EVT_IRQn); + for(int i=0; i < MAX_NUMBER_INTERRUPTS; i++) + { + NVIC_DisableIRQ( (IRQn_Type) i ); + } + + // Clear RTC1 timer to prevent Interrupt happens after changing vector table +// NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE0_Msk; +// NRF_RTC1->INTENCLR = RTC_INTENSET_COMPARE0_Msk; +// NRF_RTC1->TASKS_STOP = 1; +// NRF_RTC1->TASKS_CLEAR = 1; + + VERIFY_STATUS( sd_softdevice_vector_table_base_set(NRF_UICR->NRFFW[0]), ); + + __set_CONTROL(0); // switch to MSP, required if using FreeRTOS + bootloader_util_app_start(NRF_UICR->NRFFW[0]); + } + } +} + +BLEDfu::BLEDfu(void) : BLEService(UUID128_SVC_DFU_OTA), _chr_control(UUID128_CHR_DFU_CONTROL) +{ + +} + +err_t BLEDfu::begin(void) +{ + // Invoke base class begin() + VERIFY_STATUS( BLEService::begin() ); + + // No need to keep packet & revision characteristics + BLECharacteristic chr_packet(UUID128_CHR_DFU_PACKET); + chr_packet.setTempMemory(); + chr_packet.setProperties(CHR_PROPS_WRITE_WO_RESP); + chr_packet.setMaxLen(20); + VERIFY_STATUS( chr_packet.begin() ); + + _chr_control.setProperties(CHR_PROPS_WRITE | CHR_PROPS_NOTIFY); + _chr_control.setMaxLen(23); + _chr_control.setWriteAuthorizeCallback(bledfu_control_wr_authorize_cb); + VERIFY_STATUS( _chr_control.begin() ); + + BLECharacteristic chr_revision(UUID128_CHR_DFU_REVISON); + chr_revision.setTempMemory(); + chr_revision.setProperties(CHR_PROPS_READ); + chr_revision.setFixedLen(2); + VERIFY_STATUS( chr_revision.begin()); + chr_revision.write16(DFU_REV_APPMODE); + + return ERROR_NONE; +} diff --git a/arduino/libraries/Bluefruit52Lib/src/services/BLEDfu.h b/arduino/libraries/Bluefruit52Lib/src/services/BLEDfu.h new file mode 100755 index 0000000..224a338 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/services/BLEDfu.h @@ -0,0 +1,55 @@ +/**************************************************************************/ +/*! + @file BLEDfu.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLEDFU_H_ +#define BLEDFU_H_ + +#include "bluefruit_common.h" + +#include "BLECharacteristic.h" +#include "BLEService.h" + +class BLEDfu : public BLEService +{ + protected: + BLECharacteristic _chr_control; + + public: + BLEDfu(void); + + virtual err_t begin(void); +}; + +#endif /* BLEDFU_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/services/BLEDis.cpp b/arduino/libraries/Bluefruit52Lib/src/services/BLEDis.cpp new file mode 100755 index 0000000..c1a08e6 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/services/BLEDis.cpp @@ -0,0 +1,101 @@ +/**************************************************************************/ +/*! + @file BLEDis.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" +#include "utility/utilities.h" + +BLEDis::BLEDis(void) + : BLEService(UUID16_SVC_DEVICE_INFORMATION) +{ +#ifdef NRF52840_XXAA + _model = "Bluefruit Feather nRF52840"; +#else + _model = "Bluefruit Feather nRF52832"; +#endif + + _serial = NULL; + _firmware_rev = NULL; + _hardware_rev = NULL; + _software_rev = ARDUINO_BSP_VERSION; + _manufacturer = "Adafruit Industries"; +} + +void BLEDis::setModel(const char* model) +{ + _model = model; +} + +void BLEDis::setHardwareRev(const char* hw_rev) +{ + _hardware_rev = hw_rev; +} + +void BLEDis::setSoftwareRev(const char* sw_rev) +{ + _software_rev = sw_rev; +} + +void BLEDis::setManufacturer(const char* manufacturer) +{ + _manufacturer = manufacturer; +} + +err_t BLEDis::begin(void) +{ + // Invoke base class begin() + VERIFY_STATUS( BLEService::begin() ); + + _serial = getMcuUniqueID(); + _firmware_rev = getBootloaderVersion(); + + for(uint8_t i=0; i<arrcount(_strarr); i++) + { + if ( _strarr[i] != NULL ) + { + BLECharacteristic chars(UUID16_CHR_MODEL_NUMBER_STRING+i); + chars.setTempMemory(); + + chars.setProperties(CHR_PROPS_READ); + chars.setFixedLen(strlen(_strarr[i])); + + VERIFY_STATUS( chars.begin() ); + chars.write(_strarr[i]); + } + } + + return ERROR_NONE; +} + diff --git a/arduino/libraries/Bluefruit52Lib/src/services/BLEDis.h b/arduino/libraries/Bluefruit52Lib/src/services/BLEDis.h new file mode 100755 index 0000000..bc5e710 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/services/BLEDis.h @@ -0,0 +1,72 @@ +/**************************************************************************/ +/*! + @file BLEDis.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLEDIS_H_ +#define BLEDIS_H_ + +#include "bluefruit_common.h" + +#include "BLECharacteristic.h" +#include "BLEService.h" + +class BLEDis : public BLEService +{ + protected: + union { + struct { + const char * _model; + const char * _serial; + const char * _firmware_rev; + const char * _hardware_rev; + const char * _software_rev; + const char * _manufacturer; + }; + + const char * _strarr[6]; + }; + + public: + BLEDis(void); + + void setModel(const char* model); + void setHardwareRev(const char* hw_rev); + void setSoftwareRev(const char* sw_rev); + void setManufacturer(const char* manufacturer); + + virtual err_t begin(void); +}; + + +#endif /* BLEDIS_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/services/BLEHidAdafruit.cpp b/arduino/libraries/Bluefruit52Lib/src/services/BLEHidAdafruit.cpp new file mode 100755 index 0000000..3657f3a --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/services/BLEHidAdafruit.cpp @@ -0,0 +1,394 @@ +/**************************************************************************/ +/*! + @file BLEHidAdafruit.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" + +enum +{ + REPORT_ID_KEYBOARD = 1, + REPORT_ID_CONSUMER_CONTROL, + REPORT_ID_MOUSE, + REPORT_ID_GAMEPAD +}; + +uint8_t const hid_report_descriptor[] = +{ + //------------- Keyboard Report -------------// + HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ), + HID_USAGE ( HID_USAGE_DESKTOP_KEYBOARD ), + HID_COLLECTION ( HID_COLLECTION_APPLICATION ), + HID_REPORT_ID ( REPORT_ID_KEYBOARD ), + HID_USAGE_PAGE( HID_USAGE_PAGE_KEYBOARD ), + // 8 bits Modifier Keys (Shfit, Control, Alt) + HID_USAGE_MIN ( 224 ), + HID_USAGE_MAX ( 231 ), + HID_LOGICAL_MIN ( 0 ), + HID_LOGICAL_MAX ( 1 ), + + HID_REPORT_COUNT ( 8 ), + HID_REPORT_SIZE ( 1 ), + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ), + + // 8 bit reserved + HID_REPORT_COUNT ( 1 ), + HID_REPORT_SIZE ( 8 ), + HID_INPUT ( HID_CONSTANT ), + + // 6-byte Keycodes + HID_USAGE_PAGE (HID_USAGE_PAGE_KEYBOARD), + HID_USAGE_MIN ( 0 ), + HID_USAGE_MAX ( 255 ), + HID_LOGICAL_MIN ( 0 ), + HID_LOGICAL_MAX ( 255 ), + + HID_REPORT_COUNT ( 6 ), + HID_REPORT_SIZE ( 8 ), + HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ), + + // LED Indicator Kana | Compose | Scroll Lock | CapsLock | NumLock + HID_USAGE_PAGE ( HID_USAGE_PAGE_LED ), + /* 5-bit Led report */ + HID_USAGE_MIN ( 1 ), + HID_USAGE_MAX ( 5 ), + HID_REPORT_COUNT ( 5 ), + HID_REPORT_SIZE ( 1 ), + HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ), + /* led padding */ + HID_REPORT_COUNT ( 1 ), + HID_REPORT_SIZE ( 3 ), + HID_OUTPUT ( HID_CONSTANT ), + HID_COLLECTION_END, + + //------------- Consumer Control Report -------------// + HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ), + HID_USAGE ( HID_USAGE_CONSUMER_CONTROL ), + HID_COLLECTION ( HID_COLLECTION_APPLICATION ), + HID_REPORT_ID( REPORT_ID_CONSUMER_CONTROL ), + HID_LOGICAL_MIN ( 0x00 ), + HID_LOGICAL_MAX_N( 0x03FF, 2 ), + HID_USAGE_MIN ( 0x00 ), + HID_USAGE_MAX_N ( 0x03FF, 2 ), + HID_REPORT_COUNT ( 1 ), + HID_REPORT_SIZE ( 16 ), + HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ), + HID_COLLECTION_END, + + //------------- Mouse Report: buttons + dx + dy + scroll + pan -------------// + HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ), + HID_USAGE ( HID_USAGE_DESKTOP_MOUSE ), + HID_COLLECTION ( HID_COLLECTION_APPLICATION ), + HID_REPORT_ID( REPORT_ID_MOUSE ), + HID_USAGE (HID_USAGE_DESKTOP_POINTER ), + HID_COLLECTION ( HID_COLLECTION_PHYSICAL ), + HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ), + HID_USAGE_MIN ( 1 ), + HID_USAGE_MAX ( 5 ), + HID_LOGICAL_MIN ( 0 ), + HID_LOGICAL_MAX ( 1 ), + + HID_REPORT_COUNT ( 5 ), /* Forward, Backward, Middle, Right, Left */ + HID_REPORT_SIZE ( 1 ), + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ), + + HID_REPORT_COUNT ( 1 ), + HID_REPORT_SIZE ( 3 ), + HID_INPUT ( HID_CONSTANT ), /* 5 bit padding followed 3 bit buttons */ + + HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ), + HID_USAGE ( HID_USAGE_DESKTOP_X ), + HID_USAGE ( HID_USAGE_DESKTOP_Y ), + HID_LOGICAL_MIN ( 0x81 ), /* -127 */ + HID_LOGICAL_MAX ( 0x7f ), /* 127 */ + + HID_REPORT_COUNT ( 2 ), /* X, Y position */ + HID_REPORT_SIZE ( 8 ), + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ), /* relative values */ + + HID_USAGE ( HID_USAGE_DESKTOP_WHEEL ), /* mouse scroll */ + HID_LOGICAL_MIN ( 0x81 ), /* -127 */ + HID_LOGICAL_MAX ( 0x7f ), /* 127 */ + HID_REPORT_COUNT( 1 ), + HID_REPORT_SIZE ( 8 ), /* 8-bit value */ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ), /* relative values */ + + HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ), + HID_USAGE_N ( HID_USAGE_CONSUMER_AC_PAN, 2 ), /* Horizontal wheel scroll */ + HID_LOGICAL_MIN ( 0x81 ), /* -127 */ + HID_LOGICAL_MAX ( 0x7f ), /* 127 */ + HID_REPORT_COUNT( 1 ), + HID_REPORT_SIZE ( 8 ), /* 8-bit value */ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ), /* relative values */ + HID_COLLECTION_END, + HID_COLLECTION_END, + +#if 0 + //------------- Gamepad Report -------------// + /* Byte 0: 4 pad | 2 Y-axis | 2 X-axis + * Byte 1: Button7-Button0 + */ + HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ), + HID_USAGE ( HID_USAGE_DESKTOP_GAMEPAD ), + HID_COLLECTION ( HID_COLLECTION_APPLICATION ), + HID_REPORT_ID ( REPORT_ID_GAMEPAD ), + HID_USAGE (HID_USAGE_DESKTOP_POINTER ), + HID_COLLECTION ( HID_COLLECTION_PHYSICAL ), + // X,Y joystick + HID_USAGE ( HID_USAGE_DESKTOP_X ), + HID_USAGE ( HID_USAGE_DESKTOP_Y ), + HID_LOGICAL_MIN ( 0xFF ), /* -1 */ + HID_LOGICAL_MAX ( 0x01 ), /* 1 */ + HID_REPORT_COUNT( 2 ), /* X, Y position */ + HID_REPORT_SIZE ( 2 ), /* 2-bit value */ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ), /* input values */ + HID_COLLECTION_END, + + /* X,Y padding */ + HID_REPORT_COUNT ( 4 ), + HID_REPORT_SIZE ( 1 ), + HID_INPUT ( HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE), + + // Buttons + HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ), + HID_USAGE_MIN ( 1 ), + HID_USAGE_MAX ( 8 ), + HID_LOGICAL_MIN ( 0 ), + HID_LOGICAL_MAX ( 1 ), + HID_REPORT_COUNT ( 8 ), // Keyboard + HID_REPORT_SIZE ( 1 ), + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE), + HID_COLLECTION_END +#endif +}; + +BLEHidAdafruit::BLEHidAdafruit(void) + : BLEHidGeneric(3, 1, 0) +{ + _mse_buttons = 0; + _kbd_led_cb = NULL; +} + +err_t BLEHidAdafruit::begin(void) +{ + uint16_t input_len [] = { sizeof(hid_keyboard_report_t), sizeof(hid_consumer_control_report_t), sizeof(hid_mouse_report_t) }; + uint16_t output_len[] = { 1 }; + + setReportLen(input_len, output_len, NULL); + enableKeyboard(true); + enableMouse(true); + setReportMap(hid_report_descriptor, sizeof(hid_report_descriptor)); + + VERIFY_STATUS( BLEHidGeneric::begin() ); + + // Attempt to change the connection interval to 11.25-15 ms when starting HID + Bluefruit.setConnInterval(9, 12); + + return ERROR_NONE; +} + +/*------------------------------------------------------------------*/ +/* Keyboard + *------------------------------------------------------------------*/ + +void blehid_ada_keyboard_output_cb(BLECharacteristic& chr, uint8_t* data, uint16_t len, uint16_t offset) +{ + LOG_LV2("HID", "Keyboard LED : 0x%02X", data[0]); + VERIFY(len == 1, ); + + BLEHidAdafruit& svc = (BLEHidAdafruit&) chr.parentService(); + if ( svc._kbd_led_cb ) svc._kbd_led_cb(data[0]); +} + +void BLEHidAdafruit::setKeyboardLedCallback(kbd_led_cb_t fp) +{ + _kbd_led_cb = fp; + + // Report mode + this->setOutputReportCallback(REPORT_ID_KEYBOARD, fp ? blehid_ada_keyboard_output_cb : NULL); + + // Boot mode + _chr_boot_keyboard_output->setWriteCallback(fp ? blehid_ada_keyboard_output_cb : NULL); +} + +bool BLEHidAdafruit::keyboardReport(hid_keyboard_report_t* report) +{ + if ( isBootMode() ) + { + return bootKeyboardReport(report, sizeof(hid_keyboard_report_t)); + }else + { + return inputReport( REPORT_ID_KEYBOARD, report, sizeof(hid_keyboard_report_t)); + } +} + +bool BLEHidAdafruit::keyboardReport(uint8_t modifier, uint8_t keycode[6]) +{ + hid_keyboard_report_t report = + { + .modifier = modifier, + }; + memcpy(report.keycode, keycode, 6); + + return keyboardReport(&report); +} + +bool BLEHidAdafruit::keyboardReport(uint8_t modifier, uint8_t keycode0, uint8_t keycode1, uint8_t keycode2, uint8_t keycode3, uint8_t keycode4, uint8_t keycode5) +{ + hid_keyboard_report_t report = + { + .modifier = modifier, + .reserved = 0, + .keycode = { keycode0, keycode1, keycode2, keycode3, keycode4, keycode5 } + }; + + return keyboardReport(&report); +} + +bool BLEHidAdafruit::keyPress(char ch) +{ + hid_keyboard_report_t report; + varclr(&report); + + report.modifier = ( HID_ASCII_TO_KEYCODE[(uint8_t)ch].shift ) ? KEYBOARD_MODIFIER_LEFTSHIFT : 0; + report.keycode[0] = HID_ASCII_TO_KEYCODE[(uint8_t)ch].keycode; + + return keyboardReport(&report); +} + +bool BLEHidAdafruit::keyRelease(void) +{ + hid_keyboard_report_t report; + varclr(&report); + + return keyboardReport(&report); +} + +bool BLEHidAdafruit::keySequence(const char* str, int interal) +{ + // Send each key in sequence + char ch; + while( (ch = *str++) != 0 ) + { + char lookahead = *str; + + keyPress(ch); + delay(interal); + + /* Only need to empty report if the next character is NULL or the same with + * the current one, else no need to send */ + if ( lookahead == ch || lookahead == 0 ) + { + keyRelease(); + delay(interal); + } + } + + return true; +} + +/*------------------------------------------------------------------*/ +/* Consumer Media Key + *------------------------------------------------------------------*/ +bool BLEHidAdafruit::consumerReport(uint16_t usage_code) +{ + return inputReport( REPORT_ID_CONSUMER_CONTROL, &usage_code, sizeof(usage_code)); +} + +bool BLEHidAdafruit::consumerKeyPress(uint16_t usage_code) +{ + return consumerReport(usage_code); +} + +bool BLEHidAdafruit::consumerKeyRelease(void) +{ + uint16_t usage = 0; + return consumerReport(usage); +} + +/*------------------------------------------------------------------*/ +/* Mouse + *------------------------------------------------------------------*/ +bool BLEHidAdafruit::mouseReport(hid_mouse_report_t* report) +{ + if ( isBootMode() ) + { + return bootMouseReport(report, sizeof(hid_mouse_report_t)); + }else + { + return inputReport( REPORT_ID_MOUSE, report, sizeof(hid_mouse_report_t)); + } +} + +bool BLEHidAdafruit::mouseReport(uint8_t buttons, int8_t x, int8_t y, int8_t wheel, int8_t pan) +{ + hid_mouse_report_t report = + { + .buttons = buttons, + .x = x, + .y = y, + .wheel = wheel, + .pan = pan + }; + + _mse_buttons = buttons; + + return mouseReport(&report); +} + +bool BLEHidAdafruit::mouseButtonPress(uint8_t buttons) +{ + _mse_buttons = buttons; + return mouseReport(buttons, 0, 0, 0, 0); +} + +bool BLEHidAdafruit::mouseButtonRelease(void) +{ + return mouseReport(0, 0, 0, 0, 0); +} + +bool BLEHidAdafruit::mouseMove(int8_t x, int8_t y) +{ + return mouseReport(_mse_buttons, x, y, 0, 0); +} + +bool BLEHidAdafruit::mouseScroll(int8_t scroll) +{ + return mouseReport(_mse_buttons, 0, 0, scroll, 0); +} + +bool BLEHidAdafruit::mousePan(int8_t pan) +{ + return mouseReport(_mse_buttons, 0, 0, 0, pan); +} diff --git a/arduino/libraries/Bluefruit52Lib/src/services/BLEHidAdafruit.h b/arduino/libraries/Bluefruit52Lib/src/services/BLEHidAdafruit.h new file mode 100755 index 0000000..6563705 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/services/BLEHidAdafruit.h @@ -0,0 +1,90 @@ +/**************************************************************************/ +/*! + @file BLEHidAdafruit.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLEHIDADAFRUIT_H_ +#define BLEHIDADAFRUIT_H_ + +#include "bluefruit_common.h" + +#include "BLECharacteristic.h" +#include "BLEHidGeneric.h" +#include "BLEService.h" + + +class BLEHidAdafruit : public BLEHidGeneric +{ + public: + /*--------- Callback Signatures ----------*/ + typedef void (*kbd_led_cb_t) (uint8_t leds_bitmap); + + BLEHidAdafruit(void); + + virtual err_t begin(void); + + // Keyboard + bool keyboardReport(hid_keyboard_report_t* report); + bool keyboardReport(uint8_t modifier, uint8_t keycode[6]); + bool keyboardReport(uint8_t modifier, uint8_t keycode0, uint8_t keycode1=0, uint8_t keycode2=0, uint8_t keycode3=0, uint8_t keycode4=0, uint8_t keycode5=0); + + void setKeyboardLedCallback(kbd_led_cb_t fp); + + bool keyPress(char ch); + bool keyRelease(void); + bool keySequence(const char* str, int interal=5); + + // Consumer Media Keys + bool consumerReport(uint16_t usage_code); + bool consumerKeyPress(uint16_t usage_code); + bool consumerKeyRelease(void); + + // Mouse + bool mouseReport(hid_mouse_report_t* report); + bool mouseReport(uint8_t buttons, int8_t x, int8_t y, int8_t wheel=0, int8_t pan=0); + + bool mouseButtonPress(uint8_t buttons); + bool mouseButtonRelease(void); + + bool mouseMove(int8_t x, int8_t y); + bool mouseScroll(int8_t scroll); + bool mousePan(int8_t pan); + + protected: + uint8_t _mse_buttons; + kbd_led_cb_t _kbd_led_cb; + + friend void blehid_ada_keyboard_output_cb(BLECharacteristic& chr, uint8_t* data, uint16_t len, uint16_t offset); +}; + +#endif /* BLEHIDADAFRUIT_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/services/BLEHidGeneric.cpp b/arduino/libraries/Bluefruit52Lib/src/services/BLEHidGeneric.cpp new file mode 100755 index 0000000..a95ddc2 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/services/BLEHidGeneric.cpp @@ -0,0 +1,516 @@ +/**************************************************************************/ +/*! + @file BLEHidGeneric.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" + +enum { + REPORT_TYPE_INPUT = 1, + REPORT_TYPE_OUTPUT, + REPORT_TYPE_FEATURE +}; + +BLEHidGeneric::BLEHidGeneric(uint8_t num_input, uint8_t num_output, uint8_t num_feature) + : BLEService(UUID16_SVC_HUMAN_INTERFACE_DEVICE), _chr_control(UUID16_CHR_HID_CONTROL_POINT) +{ + _has_keyboard = _has_mouse = false; + _protocol_mode = HID_PROTOCOL_MODE_REPORT; + + _report_map = NULL; + _report_map_len = 0; + + _input_len = _output_len = _feature_len = NULL; + + _num_input = num_input; + _num_output = num_output; + _num_feature = num_feature; + + // HID Information + // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.hid_information.xml + // bcd 1.1, country = 0, flag = normal connect + _hid_info[0] = 0x01; + _hid_info[1] = 0x01; + _hid_info[2] = 0x00; + _hid_info[3] = bit(1); + + _chr_protocol = NULL; + _chr_inputs = _chr_outputs = _chr_features = NULL; + _chr_boot_keyboard_input = _chr_boot_keyboard_output = _chr_boot_mouse_input = NULL; + + if ( _num_input ) + { + _chr_inputs = new BLECharacteristic[_num_input]; + } + + if ( _num_output ) + { + _chr_outputs = new BLECharacteristic[_num_output]; + } + + if ( _num_feature ) + { + _chr_features = new BLECharacteristic[_num_feature]; + } +} + +/*------------------------------------------------------------------*/ +/* CONFIG + *------------------------------------------------------------------*/ +void BLEHidGeneric::enableKeyboard(bool enable) +{ + _has_keyboard = enable; +} + +void BLEHidGeneric::enableMouse(bool enable) +{ + _has_mouse = enable; +} + +void BLEHidGeneric::setHidInfo(uint16_t bcd, uint8_t country, uint8_t flags) +{ + memcpy(_hid_info, &bcd, 2); + _hid_info[2] = country; + _hid_info[3] = flags; +} + +void BLEHidGeneric::setReportMap(const uint8_t* report_map, size_t len) +{ + _report_map = report_map; + _report_map_len = len; +} + +void BLEHidGeneric::setReportLen(uint16_t input_len[], uint16_t output_len[], uint16_t feature_len[]) +{ + _input_len = input_len; + _output_len = output_len; + _feature_len = feature_len; +} + +void BLEHidGeneric::setOutputReportCallback(uint8_t reportID, BLECharacteristic::write_cb_t fp) +{ + // index is ID-1 + uint8_t const idx = ( reportID ? (reportID-1) : 0 ); + + // report mode + if ( idx < _num_output ) _chr_outputs[idx].setWriteCallback(fp); +} + +/*------------------------------------------------------------------*/ +/* Callbacks + *------------------------------------------------------------------*/ +void blehid_generic_protocol_mode_cb(BLECharacteristic& chr, uint8_t* data, uint16_t len, uint16_t offset) +{ + BLEHidGeneric& svc = (BLEHidGeneric&) chr.parentService(); + svc._protocol_mode = *data; + + LOG_LV2("HID", "Protocol Mode : %d (0 Boot, 1 Report)", *data); +} + +/*------------------------------------------------------------------*/ +/* Begin + *------------------------------------------------------------------*/ +err_t BLEHidGeneric::begin(void) +{ + VERIFY ( (_report_map != NULL) && _report_map_len, NRF_ERROR_INVALID_PARAM); + + // Invoke base class begin() + VERIFY_STATUS( BLEService::begin() ); + + // Protocol Mode + if ( _has_keyboard || _has_mouse ) + { + _chr_protocol = new BLECharacteristic(UUID16_CHR_PROTOCOL_MODE); + VERIFY(_chr_protocol, NRF_ERROR_NO_MEM); + + _chr_protocol->setProperties(CHR_PROPS_READ | CHR_PROPS_WRITE_WO_RESP); + _chr_protocol->setFixedLen(1); + _chr_protocol->setWriteCallback(blehid_generic_protocol_mode_cb); + VERIFY_STATUS( _chr_protocol->begin() ); + _chr_protocol->write8(_protocol_mode); + } + + // Input reports + for(uint8_t i=0; i<_num_input; i++) + { + _chr_inputs[i].setUuid(UUID16_CHR_REPORT); + _chr_inputs[i].setProperties(CHR_PROPS_READ | CHR_PROPS_NOTIFY); + _chr_inputs[i].setPermission(SECMODE_ENC_NO_MITM, SECMODE_NO_ACCESS); + _chr_inputs[i].setReportRefDescriptor(i+1, REPORT_TYPE_INPUT); + + // Input report len is configured, else variable len up to 255 + if ( _input_len ) _chr_inputs[i].setFixedLen( _input_len[i] ); + + VERIFY_STATUS( _chr_inputs[i].begin() ); + } + + // Output reports + for(uint8_t i=0; i<_num_output; i++) + { + _chr_outputs[i].setUuid(UUID16_CHR_REPORT); + _chr_outputs[i].setProperties(CHR_PROPS_READ | CHR_PROPS_WRITE | CHR_PROPS_WRITE_WO_RESP); + _chr_outputs[i].setPermission(SECMODE_ENC_NO_MITM, SECMODE_ENC_NO_MITM); + _chr_outputs[i].setReportRefDescriptor(i+1, REPORT_TYPE_OUTPUT); + + // Input report len is configured, else variable len up to 255 + if ( _output_len ) _chr_outputs[i].setFixedLen( _output_len[i] ); + + VERIFY_STATUS( _chr_outputs[i].begin() ); + + _chr_outputs[i].write8(0); + } + + // Report Map (HID Report Descriptor) + BLECharacteristic report_map(UUID16_CHR_REPORT_MAP); + report_map.setTempMemory(); + report_map.setProperties(CHR_PROPS_READ); + report_map.setPermission(SECMODE_ENC_NO_MITM, SECMODE_NO_ACCESS); + report_map.setFixedLen(_report_map_len); + VERIFY_STATUS( report_map.begin() ); + report_map.write(_report_map, _report_map_len); + + // Boot Keyboard Input & Output Report + if ( _has_keyboard ) + { + _chr_boot_keyboard_input = new BLECharacteristic(UUID16_CHR_BOOT_KEYBOARD_INPUT_REPORT); + _chr_boot_keyboard_input->setProperties(CHR_PROPS_READ | CHR_PROPS_NOTIFY); + _chr_boot_keyboard_input->setFixedLen(8); // boot keyboard is 8 bytes + _chr_boot_keyboard_input->setPermission(SECMODE_ENC_NO_MITM, SECMODE_NO_ACCESS); + VERIFY_STATUS(_chr_boot_keyboard_input->begin()); + + _chr_boot_keyboard_output = new BLECharacteristic(UUID16_CHR_BOOT_KEYBOARD_OUTPUT_REPORT); + _chr_boot_keyboard_output->setProperties(CHR_PROPS_READ | CHR_PROPS_WRITE | CHR_PROPS_WRITE_WO_RESP); + _chr_boot_keyboard_output->setFixedLen(1); // boot keyboard is 1 byte + _chr_boot_keyboard_output->setPermission(SECMODE_ENC_NO_MITM, SECMODE_ENC_NO_MITM); + VERIFY_STATUS(_chr_boot_keyboard_output->begin()); + _chr_boot_keyboard_output->write8(0); + } + + // Boot Mouse Input Report + if ( _has_mouse ) + { + _chr_boot_mouse_input = new BLECharacteristic(UUID16_CHR_BOOT_MOUSE_INPUT_REPORT); + _chr_boot_mouse_input->setProperties(CHR_PROPS_READ | CHR_PROPS_NOTIFY); + _chr_boot_mouse_input->setFixedLen(sizeof(hid_mouse_report_t)); + _chr_boot_mouse_input->setPermission(SECMODE_ENC_NO_MITM, SECMODE_NO_ACCESS); + VERIFY_STATUS(_chr_boot_mouse_input->begin()); + } + + // HID Info + BLECharacteristic hid_info(UUID16_CHR_HID_INFORMATION); + hid_info.setTempMemory(); + hid_info.setProperties(CHR_PROPS_READ); + hid_info.setPermission(SECMODE_ENC_NO_MITM, SECMODE_NO_ACCESS); + hid_info.setFixedLen(sizeof(_hid_info)); + VERIFY_STATUS( hid_info.begin() ); + hid_info.write(_hid_info, sizeof(_hid_info)); + + // HID Control Point + _chr_control.setProperties(CHR_PROPS_WRITE_WO_RESP); + _chr_control.setPermission(SECMODE_NO_ACCESS, SECMODE_ENC_NO_MITM); + _chr_control.setFixedLen(1); + VERIFY_STATUS( _chr_control.begin() ); + _chr_control.write8(0); + + return ERROR_NONE; +} + +/*------------------------------------------------------------------*/ +/* Input Report + *------------------------------------------------------------------*/ +bool BLEHidGeneric::inputReport(uint8_t reportID, void const* data, int len) +{ + // index is ID-1 + uint8_t const idx = ( reportID ? (reportID-1) : 0 ); + + return _chr_inputs[idx].notify( (uint8_t const*) data, len); +} + +bool BLEHidGeneric::bootKeyboardReport(void const* data, int len) +{ + return _chr_boot_keyboard_input->notify(data, len); +} + +bool BLEHidGeneric::bootMouseReport(void const* data, int len) +{ + return _chr_boot_mouse_input->notify(data, len); +} + +/*------------------------------------------------------------------*/ +/* Ascii to Keycode + *------------------------------------------------------------------*/ +const hid_ascii_to_keycode_entry_t HID_ASCII_TO_KEYCODE[128] = +{ + {0, 0 }, // 0x00 Null + {0, 0 }, // 0x01 + {0, 0 }, // 0x02 + {0, 0 }, // 0x03 + {0, 0 }, // 0x04 + {0, 0 }, // 0x05 + {0, 0 }, // 0x06 + {0, 0 }, // 0x07 + {0, HID_KEY_BACKSPACE }, // 0x08 Backspace + {0, HID_KEY_TAB }, // 0x09 Horizontal Tab + {0, HID_KEY_RETURN }, // 0x0A Line Feed + {0, 0 }, // 0x0B + {0, 0 }, // 0x0C + {0, HID_KEY_RETURN }, // 0x0D Carriage return + {0, 0 }, // 0x0E + {0, 0 }, // 0x0F + {0, 0 }, // 0x10 + {0, 0 }, // 0x11 + {0, 0 }, // 0x12 + {0, 0 }, // 0x13 + {0, 0 }, // 0x14 + {0, 0 }, // 0x15 + {0, 0 }, // 0x16 + {0, 0 }, // 0x17 + {0, 0 }, // 0x18 + {0, 0 }, // 0x19 + {0, 0 }, // 0x1A + {0, HID_KEY_ESCAPE }, // 0x1B Escape + {0, 0 }, // 0x1C + {0, 0 }, // 0x1D + {0, 0 }, // 0x1E + {0, 0 }, // 0x1F + + {0, HID_KEY_SPACE }, // 0x20 + {1, HID_KEY_1 }, // 0x21 ! + {1, HID_KEY_APOSTROPHE }, // 0x22 " + {1, HID_KEY_3 }, // 0x23 # + {1, HID_KEY_4 }, // 0x24 $ + {1, HID_KEY_5 }, // 0x25 % + {1, HID_KEY_7 }, // 0x26 & + {0, HID_KEY_APOSTROPHE }, // 0x27 ' + {1, HID_KEY_9 }, // 0x28 ( + {1, HID_KEY_0 }, // 0x29 ) + {1, HID_KEY_8 }, // 0x2A * + {1, HID_KEY_EQUAL }, // 0x2B + + {0, HID_KEY_COMMA }, // 0x2C , + {0, HID_KEY_MINUS }, // 0x2D - + {0, HID_KEY_PERIOD }, // 0x2E . + {0, HID_KEY_SLASH }, // 0x2F / + {0, HID_KEY_0 }, // 0x30 0 + {0, HID_KEY_1 }, // 0x31 1 + {0, HID_KEY_2 }, // 0x32 2 + {0, HID_KEY_3 }, // 0x33 3 + {0, HID_KEY_4 }, // 0x34 4 + {0, HID_KEY_5 }, // 0x35 5 + {0, HID_KEY_6 }, // 0x36 6 + {0, HID_KEY_7 }, // 0x37 7 + {0, HID_KEY_8 }, // 0x38 8 + {0, HID_KEY_9 }, // 0x39 9 + {1, HID_KEY_SEMICOLON }, // 0x3A : + {0, HID_KEY_SEMICOLON }, // 0x3B ; + {1, HID_KEY_COMMA }, // 0x3C < + {0, HID_KEY_EQUAL }, // 0x3D = + {1, HID_KEY_PERIOD }, // 0x3E > + {1, HID_KEY_SLASH }, // 0x3F ? + + {1, HID_KEY_2 }, // 0x40 @ + {1, HID_KEY_A }, // 0x41 A + {1, HID_KEY_B }, // 0x42 B + {1, HID_KEY_C }, // 0x43 C + {1, HID_KEY_D }, // 0x44 D + {1, HID_KEY_E }, // 0x45 E + {1, HID_KEY_F }, // 0x46 F + {1, HID_KEY_G }, // 0x47 G + {1, HID_KEY_H }, // 0x48 H + {1, HID_KEY_I }, // 0x49 I + {1, HID_KEY_J }, // 0x4A J + {1, HID_KEY_K }, // 0x4B K + {1, HID_KEY_L }, // 0x4C L + {1, HID_KEY_M }, // 0x4D M + {1, HID_KEY_N }, // 0x4E N + {1, HID_KEY_O }, // 0x4F O + {1, HID_KEY_P }, // 0x50 P + {1, HID_KEY_Q }, // 0x51 Q + {1, HID_KEY_R }, // 0x52 R + {1, HID_KEY_S }, // 0x53 S + {1, HID_KEY_T }, // 0x55 T + {1, HID_KEY_U }, // 0x55 U + {1, HID_KEY_V }, // 0x56 V + {1, HID_KEY_W }, // 0x57 W + {1, HID_KEY_X }, // 0x58 X + {1, HID_KEY_Y }, // 0x59 Y + {1, HID_KEY_Z }, // 0x5A Z + {0, HID_KEY_BRACKET_LEFT }, // 0x5B [ + {0, HID_KEY_BACKSLASH }, // 0x5C '\' + {0, HID_KEY_BRACKET_RIGHT }, // 0x5D ] + {1, HID_KEY_6 }, // 0x5E ^ + {1, HID_KEY_MINUS }, // 0x5F _ + + {0, HID_KEY_GRAVE }, // 0x60 ` + {0, HID_KEY_A }, // 0x61 a + {0, HID_KEY_B }, // 0x62 b + {0, HID_KEY_C }, // 0x63 c + {0, HID_KEY_D }, // 0x66 d + {0, HID_KEY_E }, // 0x65 e + {0, HID_KEY_F }, // 0x66 f + {0, HID_KEY_G }, // 0x67 g + {0, HID_KEY_H }, // 0x68 h + {0, HID_KEY_I }, // 0x69 i + {0, HID_KEY_J }, // 0x6A j + {0, HID_KEY_K }, // 0x6B k + {0, HID_KEY_L }, // 0x6C l + {0, HID_KEY_M }, // 0x6D m + {0, HID_KEY_N }, // 0x6E n + {0, HID_KEY_O }, // 0x6F o + {0, HID_KEY_P }, // 0x70 p + {0, HID_KEY_Q }, // 0x71 q + {0, HID_KEY_R }, // 0x72 r + {0, HID_KEY_S }, // 0x73 s + {0, HID_KEY_T }, // 0x75 t + {0, HID_KEY_U }, // 0x75 u + {0, HID_KEY_V }, // 0x76 v + {0, HID_KEY_W }, // 0x77 w + {0, HID_KEY_X }, // 0x78 x + {0, HID_KEY_Y }, // 0x79 y + {0, HID_KEY_Z }, // 0x7A z + {1, HID_KEY_BRACKET_LEFT }, // 0x7B { + {1, HID_KEY_BACKSLASH }, // 0x7C | + {1, HID_KEY_BRACKET_RIGHT }, // 0x7D } + {1, HID_KEY_GRAVE }, // 0x7E ~ + {0, HID_KEY_DELETE } // 0x7F Delete +}; + +/*------------------------------------------------------------------*/ +/* Keycode to Ascii + *------------------------------------------------------------------*/ +const hid_keycode_to_ascii_t HID_KEYCODE_TO_ASCII[128] = +{ + {0 , 0 }, // 0x00 + {0 , 0 }, // 0x01 + {0 , 0 }, // 0x02 + {0 , 0 }, // 0x03 + {'a' , 'A' }, // 0x04 + {'b' , 'B' }, // 0x05 + {'c' , 'C' }, // 0x06 + {'d' , 'D' }, // 0x07 + {'e' , 'E' }, // 0x08 + {'f' , 'F' }, // 0x09 + {'g' , 'G' }, // 0x0a + {'h' , 'H' }, // 0x0b + {'i' , 'I' }, // 0x0c + {'j' , 'J' }, // 0x0d + {'k' , 'K' }, // 0x0e + {'l' , 'L' }, // 0x0f + {'m' , 'M' }, // 0x10 + {'n' , 'N' }, // 0x11 + {'o' , 'O' }, // 0x12 + {'p' , 'P' }, // 0x13 + {'q' , 'Q' }, // 0x14 + {'r' , 'R' }, // 0x15 + {'s' , 'S' }, // 0x16 + {'t' , 'T' }, // 0x17 + {'u' , 'U' }, // 0x18 + {'v' , 'V' }, // 0x19 + {'w' , 'W' }, // 0x1a + {'x' , 'X' }, // 0x1b + {'y' , 'Y' }, // 0x1c + {'z' , 'Z' }, // 0x1d + {'1' , '!' }, // 0x1e + {'2' , '@' }, // 0x1f + {'3' , '#' }, // 0x20 + {'4' , '$' }, // 0x21 + {'5' , '%' }, // 0x22 + {'6' , '^' }, // 0x23 + {'7' , '&' }, // 0x24 + {'8' , '*' }, // 0x25 + {'9' , '(' }, // 0x26 + {'0' , ')' }, // 0x27 + {'\r' , '\r' }, // 0x28 + {'\x1b', '\x1b' }, // 0x29 + {'\b' , '\b' }, // 0x2a + {'\t' , '\t' }, // 0x2b + {' ' , ' ' }, // 0x2c + {'-' , '_' }, // 0x2d + {'=' , '+' }, // 0x2e + {'[' , '{' }, // 0x2f + {']' , '}' }, // 0x30 + {'\\' , '|' }, // 0x31 + {'#' , '~' }, // 0x32 + {';' , ':' }, // 0x33 + {'\'' , '\"' }, // 0x34 + {0 , 0 }, // 0x35 + {',' , '<' }, // 0x36 + {'.' , '>' }, // 0x37 + {'/' , '?' }, // 0x38 + + {0 , 0 }, // 0x39 + {0 , 0 }, // 0x3a + {0 , 0 }, // 0x3b + {0 , 0 }, // 0x3c + {0 , 0 }, // 0x3d + {0 , 0 }, // 0x3e + {0 , 0 }, // 0x3f + {0 , 0 }, // 0x40 + {0 , 0 }, // 0x41 + {0 , 0 }, // 0x42 + {0 , 0 }, // 0x43 + {0 , 0 }, // 0x44 + {0 , 0 }, // 0x45 + {0 , 0 }, // 0x46 + {0 , 0 }, // 0x47 + {0 , 0 }, // 0x48 + {0 , 0 }, // 0x49 + {0 , 0 }, // 0x4a + {0 , 0 }, // 0x4b + {0 , 0 }, // 0x4c + {0 , 0 }, // 0x4d + {0 , 0 }, // 0x4e + {0 , 0 }, // 0x4f + {0 , 0 }, // 0x50 + {0 , 0 }, // 0x51 + {0 , 0 }, // 0x52 + {0 , 0 }, // 0x53 + + {'/' , '/' }, // 0x54 + {'*' , '*' }, // 0x55 + {'-' , '-' }, // 0x56 + {'+' , '+' }, // 0x57 + {'\r' , '\r' }, // 0x58 + {'1' , 0 }, // 0x59 /* numpad1 & end */ \ + {'2' , 0 }, // 0x5a + {'3' , 0 }, // 0x5b + {'4' , 0 }, // 0x5c + {'5' , '5' }, // 0x5d + {'6' , 0 }, // 0x5e + {'7' , 0 }, // 0x5f + {'8' , 0 }, // 0x60 + {'9' , 0 }, // 0x61 + {'0' , 0 }, // 0x62 + {'0' , 0 }, // 0x63 + {'=' , '=' }, // 0x67 +}; diff --git a/arduino/libraries/Bluefruit52Lib/src/services/BLEHidGeneric.h b/arduino/libraries/Bluefruit52Lib/src/services/BLEHidGeneric.h new file mode 100755 index 0000000..449668a --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/services/BLEHidGeneric.h @@ -0,0 +1,583 @@ +/**************************************************************************/ +/*! + @file BLEHidGeneric.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLEHIDGENERIC_H_ +#define BLEHIDGENERIC_H_ + +#include "bluefruit_common.h" + +#include "BLECharacteristic.h" +#include "BLEService.h" + +enum +{ + HID_PROTOCOL_MODE_BOOT = 0, + HID_PROTOCOL_MODE_REPORT = 1 +}; + +typedef struct{ + uint8_t shift; + uint8_t keycode; +}hid_ascii_to_keycode_entry_t; +extern const hid_ascii_to_keycode_entry_t HID_ASCII_TO_KEYCODE[128]; + +typedef struct{ + uint8_t ascii; + uint8_t shifted; +}hid_keycode_to_ascii_t; +extern hid_keycode_to_ascii_t const HID_KEYCODE_TO_ASCII[128]; + +/// Standard HID Boot Protocol Mouse Report. +typedef ATTR_PACKED_STRUCT(struct) +{ + uint8_t buttons; /**< buttons mask for currently pressed buttons in the mouse. */ + int8_t x; /**< Current delta x movement of the mouse. */ + int8_t y; /**< Current delta y movement on the mouse. */ + int8_t wheel; /**< Current delta vertical wheel movement on the mouse. */ + int8_t pan; /**< Current delta horizontal wheel movement on the mouse. */ +} hid_mouse_report_t; + +/// Standard HID Boot Protocol Keyboard Report. +typedef ATTR_PACKED_STRUCT(struct) +{ + uint8_t modifier; /**< Keyboard modifier byte, indicating pressed modifier keys (a combination of HID_KEYBOARD_MODIFER_* masks). */ + uint8_t reserved; /**< Reserved for OEM use, always set to 0. */ + uint8_t keycode[6]; /**< Key codes of the currently pressed keys. */ +} hid_keyboard_report_t; + +/// HID Consumer Control Report +typedef ATTR_PACKED_STRUCT(struct) +{ + uint16_t usage_value; ///< Usage value of the pressed control +} hid_consumer_control_report_t; + +/// Gamepad report +typedef ATTR_PACKED_STRUCT(struct) +{ + ATTR_PACKED_STRUCT(struct){ + uint8_t x : 2; + uint8_t y : 2; + uint8_t : 4; + }; + + uint8_t buttons; +}hid_gamepad_report_t; + + +class BLEHidGeneric : public BLEService +{ + public: + BLEHidGeneric(uint8_t num_input, uint8_t num_output = 0, uint8_t num_feature = 0); + + void enableKeyboard(bool enable); + void enableMouse(bool enable); + + void setHidInfo(uint16_t bcd, uint8_t country, uint8_t flags); + + void setReportLen(uint16_t input_len[], uint16_t output_len[] = NULL, uint16_t feature_len[] = NULL); + void setReportMap(const uint8_t* report_map, size_t len); + + void setOutputReportCallback(uint8_t reportID, BLECharacteristic::write_cb_t fp); + + virtual err_t begin(void); + + bool isBootMode(void) { return _protocol_mode == HID_PROTOCOL_MODE_BOOT; } + + // Report + bool inputReport(uint8_t reportID, void const* data, int len); + bool bootKeyboardReport(void const* data, int len); + bool bootMouseReport(void const* data, int len); + + protected: + uint8_t _num_input; + uint8_t _num_output; + uint8_t _num_feature; + + bool _has_keyboard; + bool _has_mouse; + bool _protocol_mode; + + uint8_t _hid_info[4]; + const uint8_t* _report_map; + size_t _report_map_len; + + uint16_t* _input_len; + uint16_t* _output_len; + uint16_t* _feature_len; + + BLECharacteristic* _chr_protocol; + + BLECharacteristic* _chr_inputs; + BLECharacteristic* _chr_outputs; + BLECharacteristic* _chr_features; + + BLECharacteristic* _chr_boot_keyboard_input; + BLECharacteristic* _chr_boot_keyboard_output; + BLECharacteristic* _chr_boot_mouse_input; + + BLECharacteristic _chr_control; + + friend void blehid_generic_protocol_mode_cb(BLECharacteristic& chr, uint8_t* data, uint16_t len, uint16_t offset); +}; + +//--------------------------------------------------------------------+ +// MOUSE +//--------------------------------------------------------------------+ + +/// Standard Mouse Buttons Bitmap +typedef enum { + MOUSE_BUTTON_LEFT = bit(0), ///< Left button + MOUSE_BUTTON_RIGHT = bit(1), ///< Right button + MOUSE_BUTTON_MIDDLE = bit(2), ///< Middle button, + MOUSE_BUTTON_BACKWARD = bit(3), ///< Backward button, + MOUSE_BUTTON_FORWARD = bit(4), ///< Forward button, +}hid_mouse_button_bm_t; + +//--------------------------------------------------------------------+ +// Keyboard +//--------------------------------------------------------------------+ + +///// Keyboard modifier codes bitmap +typedef enum { + KEYBOARD_MODIFIER_LEFTCTRL = bit(0), ///< Left Control + KEYBOARD_MODIFIER_LEFTSHIFT = bit(1), ///< Left Shift + KEYBOARD_MODIFIER_LEFTALT = bit(2), ///< Left Alt + KEYBOARD_MODIFIER_LEFTGUI = bit(3), ///< Left Window + KEYBOARD_MODIFIER_RIGHTCTRL = bit(4), ///< Right Control + KEYBOARD_MODIFIER_RIGHTSHIFT = bit(5), ///< Right Shift + KEYBOARD_MODIFIER_RIGHTALT = bit(6), ///< Right Alt + KEYBOARD_MODIFIER_RIGHTGUI = bit(7) ///< Right Window +}hid_keyboard_modifier_bm_t; + +typedef enum { + KEYBOARD_LED_NUMLOCK = bit(0), ///< Num Lock LED + KEYBOARD_LED_CAPSLOCK = bit(1), ///< Caps Lock LED + KEYBOARD_LED_SCROLLLOCK = bit(2), ///< Scroll Lock LED + KEYBOARD_LED_COMPOSE = bit(3), ///< Composition Mode + KEYBOARD_LED_KANA = bit(4) ///< Kana mode +}hid_keyboard_led_bm_t; + +//--------------------------------------------------------------------+ +// HID KEYCODE +//--------------------------------------------------------------------+ +#define HID_KEY_NONE 0x00 +#define HID_KEY_A 0x04 +#define HID_KEY_B 0x05 +#define HID_KEY_C 0x06 +#define HID_KEY_D 0x07 +#define HID_KEY_E 0x08 +#define HID_KEY_F 0x09 +#define HID_KEY_G 0x0A +#define HID_KEY_H 0x0B +#define HID_KEY_I 0x0C +#define HID_KEY_J 0x0D +#define HID_KEY_K 0x0E +#define HID_KEY_L 0x0F +#define HID_KEY_M 0x10 +#define HID_KEY_N 0x11 +#define HID_KEY_O 0x12 +#define HID_KEY_P 0x13 +#define HID_KEY_Q 0x14 +#define HID_KEY_R 0x15 +#define HID_KEY_S 0x16 +#define HID_KEY_T 0x17 +#define HID_KEY_U 0x18 +#define HID_KEY_V 0x19 +#define HID_KEY_W 0x1A +#define HID_KEY_X 0x1B +#define HID_KEY_Y 0x1C +#define HID_KEY_Z 0x1D +#define HID_KEY_1 0x1E +#define HID_KEY_2 0x1F +#define HID_KEY_3 0x20 +#define HID_KEY_4 0x21 +#define HID_KEY_5 0x22 +#define HID_KEY_6 0x23 +#define HID_KEY_7 0x24 +#define HID_KEY_8 0x25 +#define HID_KEY_9 0x26 +#define HID_KEY_0 0x27 +#define HID_KEY_RETURN 0x28 +#define HID_KEY_ESCAPE 0x29 +#define HID_KEY_BACKSPACE 0x2A +#define HID_KEY_TAB 0x2B +#define HID_KEY_SPACE 0x2C +#define HID_KEY_MINUS 0x2D +#define HID_KEY_EQUAL 0x2E +#define HID_KEY_BRACKET_LEFT 0x2F +#define HID_KEY_BRACKET_RIGHT 0x30 +#define HID_KEY_BACKSLASH 0x31 +#define HID_KEY_EUROPE_1 0x32 +#define HID_KEY_SEMICOLON 0x33 +#define HID_KEY_APOSTROPHE 0x34 +#define HID_KEY_GRAVE 0x35 +#define HID_KEY_COMMA 0x36 +#define HID_KEY_PERIOD 0x37 +#define HID_KEY_SLASH 0x38 +#define HID_KEY_CAPS_LOCK 0x39 +#define HID_KEY_F1 0x3A +#define HID_KEY_F2 0x3B +#define HID_KEY_F3 0x3C +#define HID_KEY_F4 0x3D +#define HID_KEY_F5 0x3E +#define HID_KEY_F6 0x3F +#define HID_KEY_F7 0x40 +#define HID_KEY_F8 0x41 +#define HID_KEY_F9 0x42 +#define HID_KEY_F10 0x43 +#define HID_KEY_F11 0x44 +#define HID_KEY_F12 0x45 +#define HID_KEY_PRINT_SCREEN 0x46 +#define HID_KEY_SCROLL_LOCK 0x47 +#define HID_KEY_PAUSE 0x48 +#define HID_KEY_INSERT 0x49 +#define HID_KEY_HOME 0x4A +#define HID_KEY_PAGE_UP 0x4B +#define HID_KEY_DELETE 0x4C +#define HID_KEY_END 0x4D +#define HID_KEY_PAGE_DOWN 0x4E +#define HID_KEY_ARROW_RIGHT 0x4F +#define HID_KEY_ARROW_LEFT 0x50 +#define HID_KEY_ARROW_DOWN 0x51 +#define HID_KEY_ARROW_UP 0x52 +#define HID_KEY_NUM_LOCK 0x53 +#define HID_KEY_KEYPAD_DIVIDE 0x54 +#define HID_KEY_KEYPAD_MULTIPLY 0x55 +#define HID_KEY_KEYPAD_SUBTRACT 0x56 +#define HID_KEY_KEYPAD_ADD 0x57 +#define HID_KEY_KEYPAD_ENTER 0x58 +#define HID_KEY_KEYPAD_1 0x59 +#define HID_KEY_KEYPAD_2 0x5A +#define HID_KEY_KEYPAD_3 0x5B +#define HID_KEY_KEYPAD_4 0x5C +#define HID_KEY_KEYPAD_5 0x5D +#define HID_KEY_KEYPAD_6 0x5E +#define HID_KEY_KEYPAD_7 0x5F +#define HID_KEY_KEYPAD_8 0x60 +#define HID_KEY_KEYPAD_9 0x61 +#define HID_KEY_KEYPAD_0 0x62 +#define HID_KEY_KEYPAD_DECIMAL 0x63 +#define HID_KEY_EUROPE_2 0x64 +#define HID_KEY_APPLICATION 0x65 +#define HID_KEY_POWER 0x66 +#define HID_KEY_KEYPAD_EQUAL 0x67 +#define HID_KEY_F13 0x68 +#define HID_KEY_F14 0x69 +#define HID_KEY_F15 0x6A +#define HID_KEY_CONTROL_LEFT 0xE0 +#define HID_KEY_SHIFT_LEFT 0xE1 +#define HID_KEY_ALT_LEFT 0xE2 +#define HID_KEY_GUI_LEFT 0xE3 +#define HID_KEY_CONTROL_RIGHT 0xE4 +#define HID_KEY_SHIFT_RIGHT 0xE5 +#define HID_KEY_ALT_RIGHT 0xE6 +#define HID_KEY_GUI_RIGHT 0xE7 + +//--------------------------------------------------------------------+ +// REPORT DESCRIPTOR +//--------------------------------------------------------------------+ +//------------- ITEM & TAG -------------// +#define HID_REPORT_DATA_0(data) +#define HID_REPORT_DATA_1(data) , (data) +#define HID_REPORT_DATA_2(data) , U16_BYTES_LE(data) +#define HID_REPORT_DATA_3(data) , U32_BYTES_LE(data) + +#define HID_REPORT_ITEM(data, tag, type, size) \ + (((tag) << 4) | ((type) << 2) | (size)) HID_REPORT_DATA_##size(data) + +#define RI_TYPE_MAIN 0 +#define RI_TYPE_GLOBAL 1 +#define RI_TYPE_LOCAL 2 + +//------------- MAIN ITEMS 6.2.2.4 -------------// +#define HID_INPUT(x) HID_REPORT_ITEM(x, 8, RI_TYPE_MAIN, 1) +#define HID_OUTPUT(x) HID_REPORT_ITEM(x, 9, RI_TYPE_MAIN, 1) +#define HID_COLLECTION(x) HID_REPORT_ITEM(x, 10, RI_TYPE_MAIN, 1) +#define HID_FEATURE(x) HID_REPORT_ITEM(x, 11, RI_TYPE_MAIN, 1) +#define HID_COLLECTION_END HID_REPORT_ITEM(x, 12, RI_TYPE_MAIN, 0) + +//------------- INPUT, OUTPUT, FEATURE 6.2.2.5 -------------// +#define HID_DATA (0<<0) +#define HID_CONSTANT (1<<0) + +#define HID_ARRAY (0<<1) +#define HID_VARIABLE (1<<1) + +#define HID_ABSOLUTE (0<<2) +#define HID_RELATIVE (1<<2) + +#define HID_WRAP_NO (0<<3) +#define HID_WRAP (1<<3) + +#define HID_LINEAR (0<<4) +#define HID_NONLINEAR (1<<4) + +#define HID_PREFERRED_STATE (0<<5) +#define HID_PREFERRED_NO (1<<5) + +#define HID_NO_NULL_POSITION (0<<6) +#define HID_NULL_STATE (1<<6) + +#define HID_NON_VOLATILE (0<<7) +#define HID_VOLATILE (1<<7) + +#define HID_BITFIELD (0<<8) +#define HID_BUFFERED_BYTES (1<<8) + +//------------- COLLECTION ITEM 6.2.2.6 -------------// +enum { + HID_COLLECTION_PHYSICAL = 0, + HID_COLLECTION_APPLICATION, + HID_COLLECTION_LOGICAL, + HID_COLLECTION_REPORT, + HID_COLLECTION_NAMED_ARRAY, + HID_COLLECTION_USAGE_SWITCH, + HID_COLLECTION_USAGE_MODIFIER +}; + +//------------- GLOBAL ITEMS 6.2.2.7 -------------// +#define HID_USAGE_PAGE(x) HID_REPORT_ITEM(x, 0, RI_TYPE_GLOBAL, 1) +#define HID_USAGE_PAGE_N(x, n) HID_REPORT_ITEM(x, 0, RI_TYPE_GLOBAL, n) + +#define HID_LOGICAL_MIN(x) HID_REPORT_ITEM(x, 1, RI_TYPE_GLOBAL, 1) +#define HID_LOGICAL_MIN_N(x, n) HID_REPORT_ITEM(x, 1, RI_TYPE_GLOBAL, n) + +#define HID_LOGICAL_MAX(x) HID_REPORT_ITEM(x, 2, RI_TYPE_GLOBAL, 1) +#define HID_LOGICAL_MAX_N(x, n) HID_REPORT_ITEM(x, 2, RI_TYPE_GLOBAL, n) + +#define HID_PHYSICAL_MIN(x) HID_REPORT_ITEM(x, 3, RI_TYPE_GLOBAL, 1) +#define HID_PHYSICAL_MIN_N(x, n) HID_REPORT_ITEM(x, 3, RI_TYPE_GLOBAL, n) + +#define HID_PHYSICAL_MAX(x) HID_REPORT_ITEM(x, 4, RI_TYPE_GLOBAL, 1) +#define HID_PHYSICAL_MAX_N(x, n) HID_REPORT_ITEM(x, 4, RI_TYPE_GLOBAL, n) + +#define HID_UNIT_EXPONENT(x) HID_REPORT_ITEM(x, 5, RI_TYPE_GLOBAL, 1) +#define HID_UNIT_EXPONENT_N(x, n) HID_REPORT_ITEM(x, 5, RI_TYPE_GLOBAL, n) + +#define HID_UNIT(x) HID_REPORT_ITEM(x, 6, RI_TYPE_GLOBAL, 1) +#define HID_UNIT_N(x, n) HID_REPORT_ITEM(x, 6, RI_TYPE_GLOBAL, n) + +#define HID_REPORT_SIZE(x) HID_REPORT_ITEM(x, 7, RI_TYPE_GLOBAL, 1) +#define HID_REPORT_SIZE_N(x, n) HID_REPORT_ITEM(x, 7, RI_TYPE_GLOBAL, n) + +#define HID_REPORT_ID(x) HID_REPORT_ITEM(x, 8, RI_TYPE_GLOBAL, 1) +#define HID_REPORT_ID_N(x) HID_REPORT_ITEM(x, 8, RI_TYPE_GLOBAL, n) + +#define HID_REPORT_COUNT(x) HID_REPORT_ITEM(x, 9, RI_TYPE_GLOBAL, 1) +#define HID_REPORT_COUNT_N(x, n) HID_REPORT_ITEM(x, 9, RI_TYPE_GLOBAL, n) + +#define HID_PUSH HID_REPORT_ITEM(x, 10, RI_TYPE_GLOBAL, 0) +#define HID_POP HID_REPORT_ITEM(x, 11, RI_TYPE_GLOBAL, 0) + +//------------- LOCAL ITEMS 6.2.2.8 -------------// +#define HID_USAGE(x) HID_REPORT_ITEM(x, 0, RI_TYPE_LOCAL, 1) +#define HID_USAGE_N(x, n) HID_REPORT_ITEM(x, 0, RI_TYPE_LOCAL, n) + +#define HID_USAGE_MIN(x) HID_REPORT_ITEM(x, 1, RI_TYPE_LOCAL, 1) +#define HID_USAGE_MIN_N(x, n) HID_REPORT_ITEM(x, 1, RI_TYPE_LOCAL, n) + +#define HID_USAGE_MAX(x) HID_REPORT_ITEM(x, 2, RI_TYPE_LOCAL, 1) +#define HID_USAGE_MAX_N(x, n) HID_REPORT_ITEM(x, 2, RI_TYPE_LOCAL, n) + +//--------------------------------------------------------------------+ +// Usage Table +//--------------------------------------------------------------------+ + +/// HID Usage Table - Table 1: Usage Page Summary +enum { + HID_USAGE_PAGE_DESKTOP = 0x01, + HID_USAGE_PAGE_SIMULATE = 0x02, + HID_USAGE_PAGE_VIRTUAL_REALITY = 0x03, + HID_USAGE_PAGE_SPORT = 0x04, + HID_USAGE_PAGE_GAME = 0x05, + HID_USAGE_PAGE_GENERIC_DEVICE = 0x06, + HID_USAGE_PAGE_KEYBOARD = 0x07, + HID_USAGE_PAGE_LED = 0x08, + HID_USAGE_PAGE_BUTTON = 0x09, + HID_USAGE_PAGE_ORDINAL = 0x0a, + HID_USAGE_PAGE_TELEPHONY = 0x0b, + HID_USAGE_PAGE_CONSUMER = 0x0c, + HID_USAGE_PAGE_DIGITIZER = 0x0d, + HID_USAGE_PAGE_PID = 0x0f, + HID_USAGE_PAGE_UNICODE = 0x10, + HID_USAGE_PAGE_ALPHA_DISPLAY = 0x14, + HID_USAGE_PAGE_MEDICAL = 0x40, + HID_USAGE_PAGE_MONITOR = 0x80, //0x80 - 0x83 + HID_USAGE_PAGE_POWER = 0x84, // 0x084 - 0x87 + HID_USAGE_PAGE_BARCODE_SCANNER = 0x8c, + HID_USAGE_PAGE_SCALE = 0x8d, + HID_USAGE_PAGE_MSR = 0x8e, + HID_USAGE_PAGE_CAMERA = 0x90, + HID_USAGE_PAGE_ARCADE = 0x91, + HID_USAGE_PAGE_VENDOR = 0xFFFF // 0xFF00 - 0xFFFF +}; + +/// HID Usage Table - Table 6: Generic Desktop Page +enum +{ + HID_USAGE_DESKTOP_POINTER = 0x01, + HID_USAGE_DESKTOP_MOUSE = 0x02, + HID_USAGE_DESKTOP_JOYSTICK = 0x04, + HID_USAGE_DESKTOP_GAMEPAD = 0x05, + HID_USAGE_DESKTOP_KEYBOARD = 0x06, + HID_USAGE_DESKTOP_KEYPAD = 0x07, + HID_USAGE_DESKTOP_MULTI_AXIS_CONTROLLER = 0x08, + HID_USAGE_DESKTOP_TABLET_PC_SYSTEM = 0x09, + HID_USAGE_DESKTOP_X = 0x30, + HID_USAGE_DESKTOP_Y = 0x31, + HID_USAGE_DESKTOP_Z = 0x32, + HID_USAGE_DESKTOP_RX = 0x33, + HID_USAGE_DESKTOP_RY = 0x34, + HID_USAGE_DESKTOP_RZ = 0x35, + HID_USAGE_DESKTOP_SLIDER = 0x36, + HID_USAGE_DESKTOP_DIAL = 0x37, + HID_USAGE_DESKTOP_WHEEL = 0x38, + HID_USAGE_DESKTOP_HAT_SWITCH = 0x39, + HID_USAGE_DESKTOP_COUNTED_BUFFER = 0x3a, + HID_USAGE_DESKTOP_BYTE_COUNT = 0x3b, + HID_USAGE_DESKTOP_MOTION_WAKEUP = 0x3c, + HID_USAGE_DESKTOP_START = 0x3d, + HID_USAGE_DESKTOP_SELECT = 0x3e, + HID_USAGE_DESKTOP_VX = 0x40, + HID_USAGE_DESKTOP_VY = 0x41, + HID_USAGE_DESKTOP_VZ = 0x42, + HID_USAGE_DESKTOP_VBRX = 0x43, + HID_USAGE_DESKTOP_VBRY = 0x44, + HID_USAGE_DESKTOP_VBRZ = 0x45, + HID_USAGE_DESKTOP_VNO = 0x46, + HID_USAGE_DESKTOP_FEATURE_NOTIFICATION = 0x47, + HID_USAGE_DESKTOP_RESOLUTION_MULTIPLIER = 0x48, + HID_USAGE_DESKTOP_SYSTEM_CONTROL = 0x80, + HID_USAGE_DESKTOP_SYSTEM_POWER_DOWN = 0x81, + HID_USAGE_DESKTOP_SYSTEM_SLEEP = 0x82, + HID_USAGE_DESKTOP_SYSTEM_WAKE_UP = 0x83, + HID_USAGE_DESKTOP_SYSTEM_CONTEXT_MENU = 0x84, + HID_USAGE_DESKTOP_SYSTEM_MAIN_MENU = 0x85, + HID_USAGE_DESKTOP_SYSTEM_APP_MENU = 0x86, + HID_USAGE_DESKTOP_SYSTEM_MENU_HELP = 0x87, + HID_USAGE_DESKTOP_SYSTEM_MENU_EXIT = 0x88, + HID_USAGE_DESKTOP_SYSTEM_MENU_SELECT = 0x89, + HID_USAGE_DESKTOP_SYSTEM_MENU_RIGHT = 0x8A, + HID_USAGE_DESKTOP_SYSTEM_MENU_LEFT = 0x8B, + HID_USAGE_DESKTOP_SYSTEM_MENU_UP = 0x8C, + HID_USAGE_DESKTOP_SYSTEM_MENU_DOWN = 0x8D, + HID_USAGE_DESKTOP_SYSTEM_COLD_RESTART = 0x8E, + HID_USAGE_DESKTOP_SYSTEM_WARM_RESTART = 0x8F, + HID_USAGE_DESKTOP_DPAD_UP = 0x90, + HID_USAGE_DESKTOP_DPAD_DOWN = 0x91, + HID_USAGE_DESKTOP_DPAD_RIGHT = 0x92, + HID_USAGE_DESKTOP_DPAD_LEFT = 0x93, + HID_USAGE_DESKTOP_SYSTEM_DOCK = 0xA0, + HID_USAGE_DESKTOP_SYSTEM_UNDOCK = 0xA1, + HID_USAGE_DESKTOP_SYSTEM_SETUP = 0xA2, + HID_USAGE_DESKTOP_SYSTEM_BREAK = 0xA3, + HID_USAGE_DESKTOP_SYSTEM_DEBUGGER_BREAK = 0xA4, + HID_USAGE_DESKTOP_APPLICATION_BREAK = 0xA5, + HID_USAGE_DESKTOP_APPLICATION_DEBUGGER_BREAK = 0xA6, + HID_USAGE_DESKTOP_SYSTEM_SPEAKER_MUTE = 0xA7, + HID_USAGE_DESKTOP_SYSTEM_HIBERNATE = 0xA8, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_INVERT = 0xB0, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_INTERNAL = 0xB1, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_EXTERNAL = 0xB2, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_BOTH = 0xB3, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_DUAL = 0xB4, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_TOGGLE_INT_EXT = 0xB5, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_SWAP_PRIMARY_SECONDARY = 0xB6, + HID_USAGE_DESKTOP_SYSTEM_DISPLAY_LCD_AUTOSCALE = 0xB7 +}; + +/// HID Usage Table: Consumer Page (0x0C) +/// Only contains controls that supported by Windows (whole list is too long) +enum +{ + // Generic Control + HID_USAGE_CONSUMER_CONTROL = 0x0001, + + // Power Control + HID_USAGE_CONSUMER_POWER = 0x0030, + HID_USAGE_CONSUMER_RESET = 0x0031, + HID_USAGE_CONSUMER_SLEEP = 0x0032, + + // Screen Brightness + HID_USAGE_CONSUMER_BRIGHTNESS_INCREMENT = 0x006F, + HID_USAGE_CONSUMER_BRIGHTNESS_DECREMENT = 0x0070, + + // These HID usages operate only on mobile systems (battery powered) and + // require Windows 8 (build 8302 or greater). + HID_USAGE_CONSUMER_WIRELESS_RADIO_CONTROLS = 0x000C, + HID_USAGE_CONSUMER_WIRELESS_RADIO_BUTTONS = 0x00C6, + HID_USAGE_CONSUMER_WIRELESS_RADIO_LED = 0x00C7, + HID_USAGE_CONSUMER_WIRELESS_RADIO_SLIDER_SWITCH = 0x00C8, + + // Media Control + HID_USAGE_CONSUMER_PLAY_PAUSE = 0x00CD, + HID_USAGE_CONSUMER_SCAN_NEXT = 0x00B5, + HID_USAGE_CONSUMER_SCAN_PREVIOUS = 0x00B6, + HID_USAGE_CONSUMER_STOP = 0x00B7, + HID_USAGE_CONSUMER_EJECT = 0x00B8, + + HID_USAGE_CONSUMER_VOLUME = 0x00E0, + HID_USAGE_CONSUMER_MUTE = 0x00E2, + HID_USAGE_CONSUMER_BASS = 0x00E3, + HID_USAGE_CONSUMER_TREBLE = 0x00E4, + HID_USAGE_CONSUMER_BASS_BOOST = 0x00E5, + HID_USAGE_CONSUMER_VOLUME_INCREMENT = 0x00E9, + HID_USAGE_CONSUMER_VOLUME_DECREMENT = 0x00EA, + HID_USAGE_CONSUMER_BASS_INCREMENT = 0x0152, + HID_USAGE_CONSUMER_BASS_DECREMENT = 0x0153, + HID_USAGE_CONSUMER_TREBLE_INCREMENT = 0x0154, + HID_USAGE_CONSUMER_TREBLE_DECREMENT = 0x0155, + + // Application Launcher + HID_USAGE_CONSUMER_AL_CONSUMER_CONTROL_CONFIGURATION = 0x0183, + HID_USAGE_CONSUMER_AL_EMAIL_READER = 0x018A, + HID_USAGE_CONSUMER_AL_CALCULATOR = 0x0192, + HID_USAGE_CONSUMER_AL_LOCAL_BROWSER = 0x0194, + + // Browser/Explorer Specific + HID_USAGE_CONSUMER_AC_SEARCH = 0x0221, + HID_USAGE_CONSUMER_AC_HOME = 0x0223, + HID_USAGE_CONSUMER_AC_BACK = 0x0224, + HID_USAGE_CONSUMER_AC_FORWARD = 0x0225, + HID_USAGE_CONSUMER_AC_STOP = 0x0226, + HID_USAGE_CONSUMER_AC_REFRESH = 0x0227, + HID_USAGE_CONSUMER_AC_BOOKMARKS = 0x022A, + + // Mouse Horizontal scroll + HID_USAGE_CONSUMER_AC_PAN = 0x0238, +}; + + +#endif /* BLEHIDGENERIC_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/services/BLEMidi.cpp b/arduino/libraries/Bluefruit52Lib/src/services/BLEMidi.cpp new file mode 100755 index 0000000..cc16f6d --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/services/BLEMidi.cpp @@ -0,0 +1,395 @@ +/**************************************************************************/ +/*! + @file BLEMidi.cpp + @author hathach (tinyusb.org) & toddtreece + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" + +// GCC 5x new feature to detect optional include +#ifdef __has_include +#if __has_include("MIDI.h") + #include <MIDI.h> + #define MIDI_LIB_INCLUDED +#endif +#endif + + +/* MIDI Service: 03B80E5A-EDE8-4B33-A751-6CE34EC4C700 + * MIDI I/O : 7772E5DB-3868-4112-A1A9-F2669D106BF3 + */ + +const uint8_t BLEMIDI_UUID_SERVICE[] = +{ + 0x00, 0xC7, 0xC4, 0x4E, 0xE3, 0x6C, 0x51, 0xA7, + 0x33, 0x4B, 0xE8, 0xED, 0x5A, 0x0E, 0xB8, 0x03 +}; + +const uint8_t BLEMIDI_UUID_CHR_IO[] = +{ + 0xF3, 0x6B, 0x10, 0x9D, 0x66, 0xF2, 0xA9, 0xA1, + 0x12, 0x41, 0x68, 0x38, 0xDB, 0xE5, 0x72, 0x77 +}; + +/*------------------------------------------------------------------*/ +/* MIDI Data Type + *------------------------------------------------------------------*/ +typedef union ATTR_PACKED +{ + struct { + uint8_t timestamp_hi : 6; + uint8_t : 1; + uint8_t start_bit : 1; + }; + + uint8_t byte; +} midi_header_t; + +VERIFY_STATIC ( sizeof(midi_header_t) == 1 ); + +typedef union ATTR_PACKED +{ + struct { + uint8_t timestamp_low : 7; + uint8_t start_bit : 1; + }; + + uint8_t byte; +} midi_timestamp_t; + +VERIFY_STATIC ( sizeof(midi_timestamp_t) == 1 ); + +typedef struct ATTR_PACKED +{ + midi_header_t header; + midi_timestamp_t timestamp; + uint8_t data[BLE_MIDI_TX_BUFFER_SIZE]; +} midi_event_packet_t; + +VERIFY_STATIC ( sizeof(midi_event_packet_t) == (BLE_MIDI_TX_BUFFER_SIZE + 2) ); + +typedef struct ATTR_PACKED +{ + midi_header_t header; + uint8_t data[BLE_MIDI_TX_BUFFER_SIZE]; +} midi_split_packet_t; + +VERIFY_STATIC ( sizeof(midi_split_packet_t) == (BLE_MIDI_TX_BUFFER_SIZE + 1) ); + +void blemidi_write_cb(BLECharacteristic& chr, uint8_t* data, uint16_t len, uint16_t offset); + +/*------------------------------------------------------------------*/ +/* IMPLEMENTATION + *------------------------------------------------------------------*/ +BLEMidi::BLEMidi(uint16_t fifo_depth) + : BLEService(BLEMIDI_UUID_SERVICE), _io(BLEMIDI_UUID_CHR_IO), _rxd_fifo(1, fifo_depth) +{ + _write_cb = NULL; + _midilib_obj = NULL; +} + +bool BLEMidi::notifyEnabled(void) +{ + return Bluefruit.connPaired() && _io.notifyEnabled(); +} + +void BLEMidi::setWriteCallback(midi_write_cb_t fp) +{ + _write_cb = fp; +} + +void BLEMidi::autoMIDIread(void* midi_obj) +{ + _midilib_obj = midi_obj; +} +void BLEMidi::begin(int baudrate) +{ + (void) baudrate; + begin(); +} + +err_t BLEMidi::begin(void) +{ + _rxd_fifo.begin(); + + // Invoke base class begin() + VERIFY_STATUS( BLEService::begin() ); + + // IO characteristic + _io.setProperties(CHR_PROPS_READ | CHR_PROPS_WRITE | CHR_PROPS_WRITE_WO_RESP | CHR_PROPS_NOTIFY); + _io.setPermission(SECMODE_ENC_NO_MITM, SECMODE_ENC_NO_MITM); + _io.setWriteCallback(blemidi_write_cb); + + VERIFY_STATUS( _io.begin() ); + + // Attempt to change the connection interval to 11.25-15 ms when starting HID + Bluefruit.setConnInterval(9, 12); + + return ERROR_NONE; +} + +/*------------------------------------------------------------------*/ +/* Callbacks + *------------------------------------------------------------------*/ +void blemidi_write_cb(BLECharacteristic& chr, uint8_t* data, uint16_t len, uint16_t offset) +{ + (void) offset; + if ( len < 3 ) return; + + BLEMidi& midi_svc = (BLEMidi&) chr.parentService(); + midi_svc._write_handler(data, len); +} + +void BLEMidi::_write_handler(uint8_t* data, uint16_t len) +{ + // drop the BLE MIDI header byte + data++; + len--; + + while (len) + { + + // timestamp low byte followed by a MIDI status, + // so we drop the timestamp low byte + if ( isStatusByte(data[0]) && isStatusByte(data[1]) ) + { + data++; + len--; + } + // timestamp low byte on it's own (running status), + // so we drop the timestamp low byte + else if ( isStatusByte(data[0]) && ! isStatusByte(data[1]) ) + { + data++; + len--; + } + + // write the status or MIDI data to the FIFO + _rxd_fifo.write(data++, 1); + len--; + } + + // Call write callback if configured + if ( _write_cb ) _write_cb(); + +#ifdef MIDI_LIB_INCLUDED + // read while possible if configured + if ( _midilib_obj ) + { + while( ((midi::MidiInterface<BLEMidi>*)_midilib_obj)->read() ) { } + } +#endif + +} + +/*------------------------------------------------------------------*/ +/* Stream API + *------------------------------------------------------------------*/ +int BLEMidi::read ( void ) +{ + uint8_t ch; + return _rxd_fifo.read(&ch) ? (int) ch : EOF; +} + + +size_t BLEMidi::write ( uint8_t b ) +{ + // MIDI Library will write event byte by byte. + // We need to buffer the data until we have a full event, + // or until we reach the TX buffer limit. + static uint8_t count = 0; + static uint8_t buf[BLE_MIDI_TX_BUFFER_SIZE] = { 0 }; + + // the current byte is a sysex end status message, + // and we still have an existing buffer. send the + // existing buffer and clear it so we can send + // the sysex end status message with the appropriate + // BLE header and timestamp bytes. + if(b == 0xF7 && count > 0) + { + // send and clear the last of the existing buffer. + // it should contain the final bytes in the sysex payload. + if (isStatusByte(buf[0])) + send(buf, count); + else + sendSplit(buf, count); + + // reset buffer + buf[0] = 0; + count = 0; + } + + // add the current byte to the buffer + buf[count++] = b; + + // send matching 1, 2, or 3 byte messages + // and clear the buffer + if ( (oneByteMessage(buf[0]) && count == 1) || + (twoByteMessage(buf[0]) && count == 2) || + (threeByteMessage(buf[0]) && count == 3) ) + { + send(buf, count); + // reset buffer + buf[0] = 0; + count = 0; + } + + // do we have a full buffer at this point? + if(count == BLE_MIDI_TX_BUFFER_SIZE) + { + // send a full or split message depending + // on the type of the first byte in the buffer + if (isStatusByte(buf[0])) + send(buf, count); + else + sendSplit(buf, count); + + // reset buffer + buf[0] = 0; + count = 0; + } + + return 1; +} + +int BLEMidi::available ( void ) +{ + return _rxd_fifo.count(); +} + +int BLEMidi::peek ( void ) +{ + uint8_t ch; + return _rxd_fifo.peek(&ch) ? (int) ch : EOF; +} + +void BLEMidi::flush ( void ) +{ + _rxd_fifo.clear(); +} + +/*------------------------------------------------------------------*/ +/* Message Type Helpers + *------------------------------------------------------------------*/ +bool BLEMidi::isStatusByte( uint8_t b ) +{ + // if the bit 7 is set, then it's a MIDI status message + if (bitRead(b, 7)) + return true; + else + return false; +} + +bool BLEMidi::oneByteMessage( uint8_t status ) +{ + // system messages + if (status >= 0xF4 && status <= 0xFF) return true; + + // system common + if (status == 0xF1) return true; + + // sysex end + if (status == 0xF7) return true; + + return false; +} + +bool BLEMidi::twoByteMessage( uint8_t status ) +{ + // program change, aftertouch + if (status >= 0xC0 && status <= 0xDF) return true; + + // song select + if (status == 0xF3) return true; + + return false; +} + +bool BLEMidi::threeByteMessage( uint8_t status ) +{ + // note off, note on, aftertouch, control change + if (status >= 0x80 && status <= 0xBF) return true; + + // pitch wheel change + if (status >= 0xE0 && status <= 0xEF) return true; + + // song position pointer + if (status == 0xF2) return true; + + return false; +} + +/*------------------------------------------------------------------*/ +/* Send Event (notify) + *------------------------------------------------------------------*/ +bool BLEMidi::send(uint8_t data[], uint8_t len) +{ + uint32_t tstamp = millis(); + + midi_event_packet_t event = + { + .header = {{ + .timestamp_hi = (uint8_t) ((tstamp & 0x1F80UL) >> 7), + .start_bit = 1 + }}, + + .timestamp = {{ + .timestamp_low = (uint8_t) (tstamp & 0x7FUL), + .start_bit = 1 + }} + }; + + memcpy(event.data, data, len); + + // send data length + 1 byte for header + 1 byte for timestamp + return _io.notify(&event, len + 2); +} + +bool BLEMidi::sendSplit(uint8_t data[], uint8_t len) +{ + uint32_t tstamp = millis(); + + midi_split_packet_t event = + { + .header = {{ + .timestamp_hi = (uint8_t) ((tstamp & 0x1F80UL) >> 7), + .start_bit = 1 + }} + }; + + memcpy(event.data, data, len); + + // send data length + 1 byte for header + // don't include the second timestamp byte + return _io.notify(&event, len + 1); +} diff --git a/arduino/libraries/Bluefruit52Lib/src/services/BLEMidi.h b/arduino/libraries/Bluefruit52Lib/src/services/BLEMidi.h new file mode 100755 index 0000000..dbcb872 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/services/BLEMidi.h @@ -0,0 +1,99 @@ +/**************************************************************************/ +/*! + @file BLEMidi.h + @author hathach (tinyusb.org) & toddtreece + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLEMIDI_H_ +#define BLEMIDI_H_ + +#include "bluefruit_common.h" +#include "utility/adafruit_fifo.h" + +#include "BLECharacteristic.h" +#include "BLEService.h" + +#define MIDI_CREATE_BLE_INSTANCE(midiService) MIDI_CREATE_INSTANCE(BLEMidi, midiService, MIDI) + +#define BLE_MIDI_DEFAULT_FIFO_DEPTH 128 +#define BLE_MIDI_TX_BUFFER_SIZE 3 + +extern const uint8_t BLEMIDI_UUID_SERVICE[]; +extern const uint8_t BLEMIDI_UUID_CHR_IO[]; + +class BLEMidi: public BLEService, public Stream +{ + public: + typedef void (*midi_write_cb_t) (void); + + BLEMidi(uint16_t fifo_depth = BLE_MIDI_DEFAULT_FIFO_DEPTH); + + virtual err_t begin(void); + void begin(int baudrate); // MidiInterface + bool notifyEnabled(void); + + bool send(uint8_t data[], uint8_t len); + bool sendSplit(uint8_t data[], uint8_t len); + + // message type helpers + bool isStatusByte(uint8_t b); + bool oneByteMessage(uint8_t status); + bool twoByteMessage(uint8_t status); + bool threeByteMessage(uint8_t status); + + void setWriteCallback(midi_write_cb_t fp); + void autoMIDIread(void* midi_obj); + + // Stream API for MIDI Interface + virtual int read ( void ); + virtual size_t write ( uint8_t b ); + virtual int available ( void ); + virtual int peek ( void ); + virtual void flush ( void ); + + using Print::write; // pull in write(str) and write(buf, size) from Print + + private: + BLECharacteristic _io; + + Adafruit_FIFO _rxd_fifo; + midi_write_cb_t _write_cb; + + void* _midilib_obj; + + void _write_handler(uint8_t* data, uint16_t len); + + friend void blemidi_write_cb(BLECharacteristic& chr, uint8_t* data, uint16_t len, uint16_t offset); +}; + + + +#endif /* BLEMIDI_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/services/BLEUart.cpp b/arduino/libraries/Bluefruit52Lib/src/services/BLEUart.cpp new file mode 100755 index 0000000..050ecc8 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/services/BLEUart.cpp @@ -0,0 +1,325 @@ +/**************************************************************************/ +/*! + @file BLEUart.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" +#include "utility/TimeoutTimer.h" + +/* UART Serivce: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E + * UART RXD : 6E400002-B5A3-F393-E0A9-E50E24DCCA9E + * UART TXD : 6E400003-B5A3-F393-E0A9-E50E24DCCA9E + */ + +const uint8_t BLEUART_UUID_SERVICE[] = +{ + 0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, + 0x93, 0xF3, 0xA3, 0xB5, 0x01, 0x00, 0x40, 0x6E +}; + +const uint8_t BLEUART_UUID_CHR_RXD[] = +{ + 0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, + 0x93, 0xF3, 0xA3, 0xB5, 0x02, 0x00, 0x40, 0x6E +}; + +const uint8_t BLEUART_UUID_CHR_TXD[] = +{ + 0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, + 0x93, 0xF3, 0xA3, 0xB5, 0x03, 0x00, 0x40, 0x6E +}; + +/** + * Constructor + */ +BLEUart::BLEUart(uint16_t fifo_depth) + : BLEService(BLEUART_UUID_SERVICE), _txd(BLEUART_UUID_CHR_TXD), _rxd(BLEUART_UUID_CHR_RXD) +{ + _rx_fifo = NULL; + _rx_cb = NULL; + _rx_fifo_depth = fifo_depth; + + _tx_fifo = NULL; + _tx_buffered = 0; + _buffered_th = NULL; +} + +/** + * Destructor + */ +BLEUart::~BLEUart() +{ + if ( _tx_fifo ) delete _tx_fifo; +} + +/** + * Callback when received new data + * @param chr + * @param data + * @param len + * @param offset + */ +void bleuart_rxd_cb(BLECharacteristic& chr, uint8_t* data, uint16_t len, uint16_t offset) +{ + (void) offset; + + BLEUart& svc = (BLEUart&) chr.parentService(); + svc._rx_fifo->write(data, len); + +#if CFG_DEBUG >= 2 + LOG_LV2("BLEUART", "RX: "); + PRINT_BUFFER(data, len); +#endif + + // invoke user callback + if ( svc._rx_cb ) svc._rx_cb(); +} + +/** + * Timer callback periodically to send TX packet (if enabled). + * @param timer + */ +void bleuart_txd_buffered_hdlr(TimerHandle_t timer) +{ + BLEUart* svc = (BLEUart*) pvTimerGetTimerID(timer); + + // skip if null (unlikely) + if ( !svc->_tx_fifo ) return; + + // flush tx data + (void) svc->flush_tx_buffered(); +} + +void bleuart_txd_cccd_cb(BLECharacteristic& chr, uint16_t value) +{ + BLEUart& svc = (BLEUart&) chr.parentService(); + + if ( svc._buffered_th == NULL) return; + + // Enable TXD timer if configured + if (value & BLE_GATT_HVX_NOTIFICATION) + { + xTimerStart(svc._buffered_th, 0); // if started --> timer got reset + }else + { + xTimerStop(svc._buffered_th, 0); + } +} + +void BLEUart::setRxCallback( rx_callback_t fp) +{ + _rx_cb = fp; +} + +/** + * Enable packet buffered for TXD + * Note: packet is sent right away if it reach MTU bytes + * @param enable true or false + */ +void BLEUart::bufferTXD(uint8_t enable) +{ + _tx_buffered = enable; + + if ( enable ) + { + // enable cccd callback to start timer when enabled + _txd.setCccdWriteCallback(bleuart_txd_cccd_cb); + + // Create FIFO for TX TODO Larger MTU Size + if ( _tx_fifo == NULL ) + { + _tx_fifo = new Adafruit_FIFO(1); + _tx_fifo->begin( Bluefruit.Gap.getMaxMtuByConnCfg(CONN_CFG_PERIPHERAL) ); + } + }else + { + _txd.setCccdWriteCallback(NULL); + + if ( _tx_fifo ) delete _tx_fifo; + } +} + +err_t BLEUart::begin(void) +{ + _rx_fifo = new Adafruit_FIFO(1); + _rx_fifo->begin(_rx_fifo_depth); + + // Invoke base class begin() + VERIFY_STATUS( BLEService::begin() ); + + uint16_t max_mtu = Bluefruit.Gap.getMaxMtuByConnCfg(CONN_CFG_PERIPHERAL); + + // Add TXD Characteristic + _txd.setProperties(CHR_PROPS_NOTIFY); + // TODO enable encryption when bonding is enabled + _txd.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); + _txd.setMaxLen( max_mtu ); + _txd.setUserDescriptor("TXD"); + VERIFY_STATUS( _txd.begin() ); + + // Add RXD Characteristic + _rxd.setProperties(CHR_PROPS_WRITE | CHR_PROPS_WRITE_WO_RESP); + _rxd.setWriteCallback(bleuart_rxd_cb); + + // TODO enable encryption when bonding is enabled + _rxd.setPermission(SECMODE_NO_ACCESS, SECMODE_OPEN); + _rxd.setMaxLen( max_mtu ); + _rxd.setUserDescriptor("RXD"); + VERIFY_STATUS(_rxd.begin()); + + return ERROR_NONE; +} + +bool BLEUart::notifyEnabled(void) +{ + return _txd.notifyEnabled(); +} + +void BLEUart::_disconnect_cb(void) +{ + if (_buffered_th) + { + xTimerDelete(_buffered_th, 0); + _buffered_th = NULL; + + if (_tx_fifo) _tx_fifo->clear(); + } +} + +void BLEUart::_connect_cb (void) +{ + if ( _tx_buffered ) + { + // create TXD timer TODO take connInterval into account + // ((5*ms2tick(Bluefruit.connInterval())) / 4) / 2 + _buffered_th = xTimerCreate(NULL, ms2tick(10), true, this, bleuart_txd_buffered_hdlr); + + // Start the timer + xTimerStart(_buffered_th, 0); + } +} + +/*------------------------------------------------------------------*/ +/* STREAM API + *------------------------------------------------------------------*/ +int BLEUart::read (void) +{ + uint8_t ch; + return read(&ch, 1) ? (int) ch : EOF; +} + +int BLEUart::read (uint8_t * buf, size_t size) +{ + return _rx_fifo->read(buf, size); +} + +size_t BLEUart::write (uint8_t b) +{ + return write(&b, 1); +} + +size_t BLEUart::write (const uint8_t *content, size_t len) +{ + // notify right away if txd buffered is not enabled + if ( !(_tx_buffered && _tx_fifo) ) + { + return _txd.notify(content, len) ? len : 0; + }else + { + // skip if not enabled + if ( !notifyEnabled() ) return 0; + + uint16_t written = _tx_fifo->write(content, len); + + // TODO multiple prph connections + // Not up to GATT MTU, notify will be sent later by TXD timer handler + if ( _tx_fifo->count() < (Bluefruit.Gap.getMTU( Bluefruit.connHandle() ) - 3) ) + { + return len; + } + else + { + // TX fifo has enough data, send notify right away + VERIFY( flush_tx_buffered(), 0); + + // still more data left, send them all + if ( written < len ) + { + VERIFY( _txd.notify(content+written, len-written), written); + } + + return len; + } + } +} + +int BLEUart::available (void) +{ + return _rx_fifo->count(); +} + +int BLEUart::peek (void) +{ + uint8_t ch; + return _rx_fifo->peek(&ch) ? (int) ch : EOF; +} + +void BLEUart::flush (void) +{ + _rx_fifo->clear(); +} + +bool BLEUart::flush_tx_buffered(void) +{ + uint16_t max_hvx = Bluefruit.Gap.getMTU( Bluefruit.connHandle() ) - 3; + uint8_t* ff_data = (uint8_t*) rtos_malloc( max_hvx ); + + if (!ff_data) return false; + + uint16_t len = _tx_fifo->read(ff_data, max_hvx); + bool result = true; + + if ( len ) + { + result = _txd.notify(ff_data, len); + } + + rtos_free(ff_data); + + return result; +} + + + + diff --git a/arduino/libraries/Bluefruit52Lib/src/services/BLEUart.h b/arduino/libraries/Bluefruit52Lib/src/services/BLEUart.h new file mode 100755 index 0000000..d8c589d --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/services/BLEUart.h @@ -0,0 +1,105 @@ +/**************************************************************************/ +/*! + @file BLEUart.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BLEUART_H_ +#define BLEUART_H_ + +#include "bluefruit_common.h" +#include "utility/adafruit_fifo.h" + +#include "BLECharacteristic.h" +#include "BLEService.h" + +#define BLE_UART_DEFAULT_FIFO_DEPTH 256 + +extern const uint8_t BLEUART_UUID_SERVICE[]; +extern const uint8_t BLEUART_UUID_CHR_RXD[]; +extern const uint8_t BLEUART_UUID_CHR_TXD[]; + +class BLEUart : public BLEService, public Stream +{ + public: + typedef void (*rx_callback_t) (void); + + BLEUart(uint16_t fifo_depth = BLE_UART_DEFAULT_FIFO_DEPTH); + virtual ~BLEUart(); + + virtual err_t begin(void); + + bool notifyEnabled (void); + void setRxCallback (rx_callback_t fp); + void bufferTXD (uint8_t enable); + + // Stream API + virtual int read ( void ); + virtual int read ( uint8_t * buf, size_t size ); + int read ( char * buf, size_t size ) { return read( (uint8_t*) buf, size); } + virtual size_t write ( uint8_t b ); + virtual size_t write ( const uint8_t *content, size_t len ); + virtual int available ( void ); + virtual int peek ( void ); + virtual void flush ( void ); + + // pull in write(str) and write(buf, size) from Print + using Print::write; + + protected: + BLECharacteristic _txd; + BLECharacteristic _rxd; + + // RXD + Adafruit_FIFO* _rx_fifo; + uint16_t _rx_fifo_depth; + rx_callback_t _rx_cb; + + // TXD + Adafruit_FIFO* _tx_fifo; + uint8_t _tx_buffered; + TimerHandle_t _buffered_th; + + bool flush_tx_buffered(void); + + // from BLEService + virtual void _disconnect_cb(void); + virtual void _connect_cb(void); + + friend void bleuart_rxd_cb(BLECharacteristic& chr, uint8_t* data, uint16_t len, uint16_t offset); + friend void bleuart_txd_cccd_cb(BLECharacteristic& chr, uint16_t value); + friend void bleuart_txd_buffered_hdlr(TimerHandle_t timer); +}; + + + +#endif /* BLEUART_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/services/EddyStone.cpp b/arduino/libraries/Bluefruit52Lib/src/services/EddyStone.cpp new file mode 100755 index 0000000..22b45b3 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/services/EddyStone.cpp @@ -0,0 +1,180 @@ +/**************************************************************************/ +/*! + @file EddyStone.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" + +/*------------------------------------------------------------------*/ +/* EddyStone URL + *------------------------------------------------------------------*/ +char const * const prefix_scheme[] = +{ + [0] = "http://www." , + [1] = "https://www." , + [2] = "http://" , + [3] = "https://" +}; +enum { PREFIX_COUNT = sizeof(prefix_scheme)/sizeof(char*) }; + +char const * const url_expansion[] = +{ + [0 ] = ".com/" , + [1 ] = ".org/" , + [2 ] = ".edu/" , + [3 ] = ".net/" , + [4 ] = ".info/" , + [5 ] = ".biz/" , + [6 ] = ".gov/" , + + [7 ] = ".com" , + [8 ] = ".org" , + [9 ] = ".edu" , + [10] = ".net" , + [11] = ".info" , + [12] = ".biz" , + [13] = ".gov" , +}; + +enum { EXPANNSION_COUNT = sizeof(url_expansion)/sizeof(char*) }; + +EddyStoneUrl::EddyStoneUrl(void) +{ + _rssi = 0; + _url = NULL; +} + +EddyStoneUrl::EddyStoneUrl(int8_t rssiAt0m, const char* url) +{ + _rssi = rssiAt0m; + _url = url; +} + +bool EddyStoneUrl::setUrl(const char* url) +{ + _url = url; +} + +void EddyStoneUrl::setRssi(int8_t rssiAt0m) +{ + _rssi = rssiAt0m; +} + +char const* findExpansion(char const* p_url, uint8_t * p_idx) +{ + for(uint8_t i=0; i<EXPANNSION_COUNT; i++) + { + char const * substr = strstr(p_url, url_expansion[i]); + + if ( substr ) + { + *p_idx = i; + return substr; + } + } + + return NULL; +} + +bool EddyStoneUrl::start(void) +{ + enum { URL_MAXLEN = 17 }; + struct ATTR_PACKED { + uint16_t eddy_uuid; + + uint8_t frame_type; + int8_t rssi; + uint8_t url_scheme; + uint8_t urlencode[URL_MAXLEN]; + }eddy = + { + .eddy_uuid = UUID16_SVC_EDDYSTONE, + .frame_type = EDDYSTONE_TYPE_URL, + .rssi = _rssi, + .url_scheme = 0xff + }; + + const char* url = _url; + + // Detect url scheme + for(uint8_t i=0; i<PREFIX_COUNT; i++) + { + uint8_t prelen = strlen(prefix_scheme[i]); + if ( !memcmp(url, prefix_scheme[i], prelen) ) + { + eddy.url_scheme = i; + url += prelen; + + break; + } + } + VERIFY( eddy.url_scheme < PREFIX_COUNT ); + + // Encode url data + uint8_t len = 0; + + while(*url) + { + uint8_t ex_code; + char const * expansion = findExpansion(url, &ex_code); + + // copy url up to the found expansion, if expansion is found, one more + // byte must be reserved for it + uint8_t cp_num = (expansion) ? (expansion-url) : strlen(url); + if ( cp_num > URL_MAXLEN-(len + (expansion ? 1:0)) ) + { + LOG_LV1("EDDYS", "url is too long"); + return false; + } + + memcpy(eddy.urlencode+len, url, cp_num); + url += cp_num; + len += cp_num; + + // copy expansion code if found + if (expansion) + { + eddy.urlencode[len++] = ex_code; + url += strlen(url_expansion[ex_code]); + } + } + + // Add UUID16 list with EddyStone + VERIFY ( Bluefruit.Advertising.addUuid(UUID16_SVC_EDDYSTONE) ); + + // Add Eddystone Service Data + VERIFY ( Bluefruit.Advertising.addData(BLE_GAP_AD_TYPE_SERVICE_DATA, &eddy, len + 5) ); + + return true; +} diff --git a/arduino/libraries/Bluefruit52Lib/src/services/EddyStone.h b/arduino/libraries/Bluefruit52Lib/src/services/EddyStone.h new file mode 100755 index 0000000..4dc4585 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/services/EddyStone.h @@ -0,0 +1,69 @@ +/**************************************************************************/ +/*! + @file EddyStone.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef EDDYSTONE_H_ +#define EDDYSTONE_H_ + +#include "bluefruit_common.h" + +#include "BLECharacteristic.h" +#include "BLEService.h" + +enum +{ + EDDYSTONE_TYPE_UID = 0x00, + EDDYSTONE_TYPE_URL = 0x10, + EDDYSTONE_TYPE_TLM = 0x20, + EDDYSTONE_TYPE_EID = 0x30, +}; + +class EddyStoneUrl +{ + private: + int8_t _rssi; + const char* _url; + + public: + EddyStoneUrl(void); + EddyStoneUrl(int8_t rssiAt0m, const char* url = NULL); + + bool setUrl(const char* url); + void setRssi(int8_t rssiAt0m); + + bool start(void); +}; + + +#endif /* EDDYSTONE_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/utility/AdaMsg.cpp b/arduino/libraries/Bluefruit52Lib/src/utility/AdaMsg.cpp new file mode 100755 index 0000000..5c0f43b --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/utility/AdaMsg.cpp @@ -0,0 +1,131 @@ +/**************************************************************************/ +/*! + @file AdaMsg.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "AdaMsg.h" + +void AdaMsg::_init(void) +{ + _dynamic = true; + _waiting = false; + _sem = NULL; + + buffer = NULL; + remaining = xferlen = 0; +} + +AdaMsg::AdaMsg(void) +{ + _init(); +} + +// dynamic mean semaphore is malloced and freed only when in action +void AdaMsg::begin(bool dynamic) +{ + _dynamic = dynamic; + if ( !_dynamic ) + { + _sem = xSemaphoreCreateCounting(10, 0); + } +} + +void AdaMsg::stop(void) +{ + if (!_dynamic) vSemaphoreDelete(_sem); + _init(); +} + +void AdaMsg::prepare(void* buf, uint16_t bufsize) +{ + buffer = (uint8_t*) buf; + remaining = bufsize; + xferlen = 0; +} + +/** + * + * @param ms + * @return -1 if timeout + */ +int32_t AdaMsg::waitUntilComplete(uint32_t ms) +{ + if (_dynamic) + { + _sem = xSemaphoreCreateBinary(); + VERIFY(_sem, -1); + } + + int result = -1; + + _waiting = true; + if ( xSemaphoreTake(_sem, ms2tick(ms) ) ) + { + result = xferlen; + } + _waiting = false; + + if (_dynamic) + { + vSemaphoreDelete(_sem); + _sem = NULL; + } + + return result; +} + +bool AdaMsg::isWaiting(void) +{ + return _waiting; +} + +uint16_t AdaMsg::feed(void* data, uint16_t len) +{ + len = min16(len, remaining); + + // pass NULL to skip copy + if ( data ) memcpy(buffer, data, len); + + buffer += len; + remaining -= len; + xferlen += len; + + return len; +} + +void AdaMsg::complete(void) +{ + if(_sem) xSemaphoreGive(_sem); +} + diff --git a/arduino/libraries/Bluefruit52Lib/src/utility/AdaMsg.h b/arduino/libraries/Bluefruit52Lib/src/utility/AdaMsg.h new file mode 100755 index 0000000..78fd99a --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/utility/AdaMsg.h @@ -0,0 +1,71 @@ +/**************************************************************************/ +/*! + @file AdaMsg.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef ADAMSG_H_ +#define ADAMSG_H_ + +#include "Arduino.h" + +class AdaMsg +{ + private: + bool _dynamic; + volatile bool _waiting; + SemaphoreHandle_t _sem; + + void _init(void); + + public: + uint8_t* buffer; + uint16_t remaining; + volatile uint16_t xferlen; + + AdaMsg(void); + + // dynamic mean semaphore is malloced and freed only when in action + void begin(bool dynamic = true); + void stop(void); + + void prepare(void* buf, uint16_t bufsize); + int32_t waitUntilComplete(uint32_t ms); + bool isWaiting(void); + + uint16_t feed(void* data, uint16_t len); + void complete(void); +}; + + + +#endif /* ADAMSG_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/utility/bonding.cpp b/arduino/libraries/Bluefruit52Lib/src/utility/bonding.cpp new file mode 100755 index 0000000..e9f9289 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/utility/bonding.cpp @@ -0,0 +1,348 @@ +/**************************************************************************/ +/*! + @file bonding.cpp + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include <Arduino.h> +#include "Bluefruit_FileIO.h" +#include "bonding.h" +#include "bluefruit.h" + +#define BOND_DEBUG 0 + +#if (CFG_DEBUG == 1 && BOND_DEBUG == 1) || (CFG_DEBUG >= 2) +#define BOND_LOG(...) LOG_LV1("BOND", __VA_ARGS__) +#else +#define BOND_LOG(...) +#endif + +/*------------------------------------------------------------------*/ +/* Bond Key is saved in following layout + * - Bond Data : 80 bytes + * - Name : variable (including null char) + * - CCCD : variable + * + * Each field has an 1-byte preceding length + *------------------------------------------------------------------*/ +#define SVC_CONTEXT_FLAG (BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS | BLE_GATTS_SYS_ATTR_FLAG_USR_SRVCS) +#define BOND_FNAME_LEN max(sizeof(BOND_FNAME_PRPH), sizeof(BOND_FNAME_CNTR)) + +static void get_fname (char* fname, uint8_t role, uint16_t ediv) +{ + sprintf(fname, (role == BLE_GAP_ROLE_PERIPH) ? BOND_FNAME_PRPH : BOND_FNAME_CNTR, ediv); +} + +static bool bdata_skip_field(File* file) +{ + int len = file->read(); + VERIFY(len > 0); + + file->seek(len + file->position()); + return true; +} + +static void bdata_write(File* file, void const* buffer, uint16_t bufsize) +{ + file->write( (uint8_t) bufsize ); + file->write( (uint8_t const*) buffer, bufsize); +} + +void bond_init(void) +{ + InternalFS.begin(); + + // Create prph and central bond folder if not existed + if ( !InternalFS.exists(BOND_DIR_PRPH) ) InternalFS.mkdir(BOND_DIR_PRPH); + if ( !InternalFS.exists(BOND_DIR_CNTR) ) InternalFS.mkdir(BOND_DIR_CNTR); +} + +/*------------------------------------------------------------------*/ +/* Keys + *------------------------------------------------------------------*/ +static void bond_save_keys_dfr (uint8_t role, uint16_t conn_hdl, bond_keys_t* bkeys) +{ + uint16_t const ediv = (role == BLE_GAP_ROLE_PERIPH) ? bkeys->own_enc.master_id.ediv : bkeys->peer_enc.master_id.ediv; + + char filename[BOND_FNAME_LEN]; + get_fname(filename, role, ediv); + + // delete if file already exists + if ( InternalFS.exists(filename) ) InternalFS.remove(filename); + + File file(filename, FILE_WRITE, InternalFS); + VERIFY(file,); + + //------------- save keys -------------// + bdata_write(&file, bkeys, sizeof(bond_keys_t)); + + //------------- save device name -------------// + char devname[CFG_MAX_DEVNAME_LEN] = { 0 }; + Bluefruit.Gap.getPeerName(conn_hdl, devname, CFG_MAX_DEVNAME_LEN); + + // If couldn't get devname then use peer mac address + if ( !devname[0] ) + { + uint8_t* mac = bkeys->peer_id.id_addr_info.addr; + sprintf(devname, "%02X:%02X:%02X:%02X:%02X:%02X", mac[5], mac[4], mac[3], mac[2], mac[1], mac[0]); + } + + bdata_write(&file, devname, strlen(devname)+1); // save also null char + + BOND_LOG("Saved keys for \"%s\" to file %s ( %d bytes )", devname, filename, file.size()); + + file.close(); +} + +bool bond_save_keys (uint8_t role, uint16_t conn_hdl, bond_keys_t* bkeys) +{ + uint8_t* buf = (uint8_t*) rtos_malloc( sizeof(bond_keys_t) ); + VERIFY(buf); + + memcpy(buf, bkeys, sizeof(bond_keys_t)); + + // queue to execute in Ada Callback thread + ada_callback(buf, bond_save_keys_dfr, role, conn_hdl, buf); + + return true; +} + +bool bond_load_keys(uint8_t role, uint16_t ediv, bond_keys_t* bkeys) +{ + char filename[BOND_FNAME_LEN]; + get_fname(filename, role, ediv); + + File file(filename, FILE_READ, InternalFS); + VERIFY(file); + + int keylen = file.read(); + VERIFY(keylen == sizeof(bond_keys_t)); + + file.read(bkeys, keylen); + file.close(); + + BOND_LOG("Loaded keys from file %s", filename); + + return true; +} + + +/*------------------------------------------------------------------*/ +/* CCCD + *------------------------------------------------------------------*/ +static void bond_save_cccd_dfr (uint8_t role, uint16_t conn_hdl, uint16_t ediv) +{ + uint16_t len=0; + sd_ble_gatts_sys_attr_get(conn_hdl, NULL, &len, SVC_CONTEXT_FLAG); + VERIFY(len, ); + + uint8_t sys_attr[len]; + VERIFY_STATUS(sd_ble_gatts_sys_attr_get(conn_hdl, sys_attr, &len, SVC_CONTEXT_FLAG),); + + char filename[BOND_FNAME_LEN]; + get_fname(filename, role, ediv); + + File file(filename, FILE_WRITE, InternalFS); + VERIFY(file,); + + file.seek(0); // write mode start at the end, seek to beginning + bdata_skip_field(&file); // skip key + bdata_skip_field(&file); // skip name + + bdata_write(&file, sys_attr, len); + + BOND_LOG("Saved CCCD setting to file %s ( offset = %d, len = %d bytes )", filename, file.size() - (len + 1), len); + + file.close(); +} + +bool bond_save_cccd (uint8_t role, uint16_t conn_hdl, uint16_t ediv) +{ + VERIFY(ediv != 0xFFFF); + + // queue to execute in Ada Callback thread + ada_callback(NULL, bond_save_cccd_dfr, role, conn_hdl, ediv); + + return true; +} + +bool bond_load_cccd(uint8_t role, uint16_t conn_hdl, uint16_t ediv) +{ + bool loaded = false; + + if ( ediv != 0xFFFF ) + { + char filename[BOND_FNAME_LEN]; + get_fname(filename, role, ediv); + + File file(filename, FILE_READ, InternalFS); + + if ( file ) + { + bdata_skip_field(&file); // skip key + bdata_skip_field(&file); // skip name + + int len = file.read(); + if ( len > 0 ) + { + uint8_t sys_attr[len]; + + file.read(sys_attr, len); + + if ( ERROR_NONE == sd_ble_gatts_sys_attr_set(conn_hdl, sys_attr, len, SVC_CONTEXT_FLAG) ) + { + loaded = true; + BOND_LOG("Loaded CCCD from file %s ( offset = %d, len = %d bytes )", filename, file.size() - (len + 1), len); + } + } + } + + file.close(); + } + + if ( !loaded ) + { + LOG_LV1("BOND", "CCCD setting not found"); + } + + return loaded; +} + +void bond_print_list(uint8_t role) +{ + char const * dpath = (role == BLE_GAP_ROLE_PERIPH ? BOND_DIR_PRPH : BOND_DIR_CNTR); + + File dir(dpath, FILE_READ, InternalFS); + File file(InternalFS); + + while ( (file = dir.openNextFile(FILE_READ)) ) + { + if ( !file.isDirectory() && bdata_skip_field(&file) ) // skip key + { + int len = file.read(); + if ( len > 0 ) + { + char devname[len]; + file.read(devname, len); + + printf(" %s : %s (%d bytes)\n", file.name(), devname, file.size()); + } + } + + file.close(); + } + + printf("\n"); + + file.close(); + dir.close(); +} + + +bool bond_find_cntr(ble_gap_addr_t* addr, bond_keys_t* bkeys) +{ + bool found = false; + + File dir(BOND_DIR_CNTR, FILE_READ, InternalFS); + File file(InternalFS); + + while ( (file = dir.openNextFile(FILE_READ)) ) + { + // Read bond data of each stored file + int keylen = file.read(); + if ( keylen == sizeof(bond_keys_t) ) + { + file.read((uint8_t*) bkeys, keylen); + + // Compare static address + if ( !memcmp(addr->addr, bkeys->peer_id.id_addr_info.addr, 6) ) + { + found = true; + } + else if ( addr->addr_type == BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE ) + { + // Resolving private address + } + } + + file.close(); + + if ( found ) break; + } + + file.close(); + dir.close(); + + return found; +} + +/*------------------------------------------------------------------*/ +/* DELETE + *------------------------------------------------------------------*/ +void bond_clear_prph(void) +{ + // delete bonds dir + InternalFS.rmdir_r(BOND_DIR_PRPH); + + // Create an empty one + InternalFS.mkdir(BOND_DIR_PRPH); +} + +void bond_clear_cntr(void) +{ + // delete bonds dir + InternalFS.rmdir_r(BOND_DIR_CNTR); + + // Create an empty one + InternalFS.mkdir(BOND_DIR_CNTR); + +} + +void bond_clear_all(void) +{ + // delete bonds dir + InternalFS.rmdir_r(BOND_DIR_PRPH); + InternalFS.rmdir_r(BOND_DIR_CNTR); + + // Create an empty one + InternalFS.mkdir(BOND_DIR_PRPH); + InternalFS.mkdir(BOND_DIR_CNTR); +} + +void bond_remove_key(uint8_t role, uint16_t ediv) +{ + char filename[BOND_FNAME_LEN]; + get_fname(filename, role, ediv); + + InternalFS.remove(filename); +} diff --git a/arduino/libraries/Bluefruit52Lib/src/utility/bonding.h b/arduino/libraries/Bluefruit52Lib/src/utility/bonding.h new file mode 100755 index 0000000..0980369 --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/utility/bonding.h @@ -0,0 +1,74 @@ +/**************************************************************************/ +/*! + @file bonding.h + @author hathach (tinyusb.org) + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ +#ifndef BONDING_H_ +#define BONDING_H_ + +#include "bluefruit_common.h" + +#define BOND_DIR_PRPH "/adafruit/bond_prph" +#define BOND_DIR_CNTR "/adafruit/bond_cntr" + +#define BOND_FNAME_PRPH BOND_DIR_PRPH "/%04x" +#define BOND_FNAME_CNTR BOND_DIR_CNTR "/%04x" + +// Shared keys with bonded device, size = 80 bytes +typedef struct +{ + ble_gap_enc_key_t own_enc; + ble_gap_enc_key_t peer_enc; + ble_gap_id_key_t peer_id; +} bond_keys_t; + +void bond_init(void); +void bond_clear_prph(void); +void bond_clear_cntr(void); +void bond_clear_all(void); + +void bond_remove_key(uint8_t role, uint16_t ediv); + +bool bond_save_keys (uint8_t role, uint16_t conn_hdl, bond_keys_t* bkeys); +bool bond_load_keys(uint8_t role, uint16_t ediv, bond_keys_t* bkeys); + +bool bond_save_cccd (uint8_t role, uint16_t conn_hdl, uint16_t ediv); +bool bond_load_cccd (uint8_t role, uint16_t conn_hdl, uint16_t ediv); + +void bond_print_list(uint8_t role); + +bool bond_find_cntr(ble_gap_addr_t* addr, bond_keys_t* bkeys); + + + +#endif /* BONDING_H_ */ diff --git a/arduino/libraries/Bluefruit52Lib/src/utility/bootloader_util.c b/arduino/libraries/Bluefruit52Lib/src/utility/bootloader_util.c new file mode 100755 index 0000000..470666c --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/utility/bootloader_util.c @@ -0,0 +1,75 @@ +/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + +#include <stdint.h> +#include <string.h> + + +/** + * @brief Function for aborting current application/bootloader jump to to other app/bootloader. + * + * @details This functions will use the address provide to swap the stack pointer and then load + * the address of the reset handler to be executed. It will check current system mode + * (thread/handler) and if in thread mode it will reset into other application. + * If in handler mode \ref isr_abort will be executed to ensure correct exit of handler + * mode and jump into reset handler of other application. + * + * @param[in] start_addr Start address of other application. This address must point to the + initial stack pointer of the application. + * + * @note This function will never return but issue a reset into provided application. + */ +static inline void bootloader_util_reset (uint32_t start_addr) __attribute__ ((optimize("-fomit-frame-pointer"))); +static inline void bootloader_util_reset(uint32_t start_addr) +{ + __asm volatile( + "ldr r0, [%0]\t\n" // Get App initial MSP for bootloader. + "msr msp, r0\t\n" // Set the main stack pointer to the applications MSP. + "ldr r0, [%0, #0x04]\t\n" // Load Reset handler into R0. + + "movs r4, #0xFF\t\n" // Move ones to R4. + "sxtb r4, r4\t\n" // Sign extend R4 to obtain 0xFFFFFFFF instead of 0xFF. + + "mrs r5, IPSR\t\n" // Load IPSR to R5 to check for handler or thread mode. + "cmp r5, #0x00\t\n" // Compare, if 0 then we are in thread mode and can continue to reset handler of bootloader. + "bne isr_abort\t\n" // If not zero we need to exit current ISR and jump to reset handler of bootloader. + + "mov lr, r4\t\n" // Clear the link register and set to ones to ensure no return. + "bx r0\t\n" // Branch to reset handler of bootloader. + + "isr_abort: \t\n" + + "mov r5, r4\t\n" // Fill with ones before jumping to reset handling. Will be popped as LR when exiting ISR. Ensures no return to application. + "mov r6, r0\t\n" // Move address of reset handler to R6. Will be popped as PC when exiting ISR. Ensures the reset handler will be executed when exist ISR. + "movs r7, #0x21\t\n" // Move MSB reset value of xPSR to R7. Will be popped as xPSR when exiting ISR. xPSR is 0x21000000 thus MSB is 0x21. + "rev r7, r7\t\n" // Reverse byte order to put 0x21 as MSB. + "push {r4-r7}\t\n" // Push everything to new stack to allow interrupt handler to fetch it on exiting the ISR. + + "movs r4, #0x00\t\n" // Fill with zeros before jumping to reset handling. We be popped as R0 when exiting ISR (Cleaning up of the registers). + "movs r5, #0x00\t\n" // Fill with zeros before jumping to reset handling. We be popped as R1 when exiting ISR (Cleaning up of the registers). + "movs r6, #0x00\t\n" // Fill with zeros before jumping to reset handling. We be popped as R2 when exiting ISR (Cleaning up of the registers). + "movs r7, #0x00\t\n" // Fill with zeros before jumping to reset handling. We be popped as R3 when exiting ISR (Cleaning up of the registers). + "push {r4-r7}\t\n" // Push zeros (R4-R7) to stack to prepare for exiting the interrupt routine. + + "movs r0, #0xF9\t\n" // Move the execution return command into register, 0xFFFFFFF9. + "sxtb r0, r0\t\n" // Sign extend R0 to obtain 0xFFFFFFF9 instead of 0xF9. + "bx r0\t\n" // No return - Handler mode will be exited. Stack will be popped and execution will continue in reset handler initializing other application. + ".align\t\n" + :: "r" (start_addr) // Argument list for the gcc assembly. start_addr is %0. + : "r0", "r4", "r5", "r6", "r7" // List of register maintained manually. + ); +} + +void bootloader_util_app_start(uint32_t start_addr) +{ + bootloader_util_reset(start_addr); +} |