aboutsummaryrefslogtreecommitdiffstats
path: root/arduino/libraries/Bluefruit52Lib/src/BLEScanner.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'arduino/libraries/Bluefruit52Lib/src/BLEScanner.cpp')
-rwxr-xr-xarduino/libraries/Bluefruit52Lib/src/BLEScanner.cpp427
1 files changed, 427 insertions, 0 deletions
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;
+ }
+}