2020-03-29 17:44:20 -06:00
/*
* NimBLEScan.cpp
*
* Created: on Jan 24 2020
* Author H2zero
2020-05-13 22:03:56 -06:00
*
2020-03-29 17:44:20 -06:00
* Originally:
*
* BLEScan.cpp
*
* Created on: Jul 1, 2017
* Author: kolban
*/
#include "sdkconfig.h"
#if defined(CONFIG_BT_ENABLED)
2020-05-13 22:03:56 -06:00
#include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
2020-03-29 17:44:20 -06:00
#include "NimBLEScan.h"
#include "NimBLEDevice.h"
#include "NimBLELog.h"
#include <string>
static const char * LOG_TAG = "NimBLEScan" ;
/**
* @brief Scan constuctor.
*/
NimBLEScan :: NimBLEScan () {
2020-07-01 17:26:44 -06:00
m_own_addr_type = 0 ;
2020-03-29 17:44:20 -06:00
m_scan_params . filter_policy = BLE_HCI_SCAN_FILT_NO_WL ;
m_scan_params . passive = 1 ; // If set, don’ t send scan requests to advertisers (i.e., don’ t request additional advertising data).
m_scan_params . itvl = 0 ; // This is defined as the time interval from when the Controller started its last LE scan until it begins the subsequent LE scan. (units=0.625 msec)
m_scan_params . window = 0 ; // The duration of the LE scan. LE_Scan_Window shall be less than or equal to LE_Scan_Interval (units=0.625 msec)
m_scan_params . limited = 0 ; // If set, only discover devices in limited discoverable mode.
2020-06-26 15:44:40 -06:00
m_scan_params . filter_duplicates = 0 ; // If set, the controller ignores all but the first advertisement from each device.
2020-03-29 17:44:20 -06:00
m_pAdvertisedDeviceCallbacks = nullptr ;
2021-01-10 21:54:32 -07:00
m_ignoreResults = false ;
2020-03-29 17:44:20 -06:00
m_wantDuplicates = false ;
2020-06-21 22:07:01 -06:00
m_pTaskData = nullptr ;
2020-03-29 17:44:20 -06:00
}
2020-07-28 20:57:33 -06:00
/**
* @brief Scan destructor, release any allocated resources.
*/
NimBLEScan ::~ NimBLEScan () {
clearResults ();
}
2020-03-29 17:44:20 -06:00
/**
* @brief Handle GAP events related to scans.
* @param [in] event The event type for this event.
* @param [in] param Parameter data for this event.
*/
/*STATIC*/ int NimBLEScan :: handleGapEvent ( ble_gap_event * event , void * arg ) {
2020-05-13 22:03:56 -06:00
2020-03-29 17:44:20 -06:00
NimBLEScan * pScan = ( NimBLEScan * ) arg ;
2020-05-13 22:03:56 -06:00
2020-03-29 17:44:20 -06:00
switch ( event -> type ) {
case BLE_GAP_EVENT_DISC : {
2021-01-10 21:54:32 -07:00
if ( pScan -> m_ignoreResults ) {
NIMBLE_LOGE ( LOG_TAG , "Scan op in progress - ignoring results" );
2020-05-13 22:03:56 -06:00
return 0 ;
}
2020-03-29 17:44:20 -06:00
NimBLEAddress advertisedAddress ( event -> disc . addr );
// Examine our list of ignored addresses and stop processing if we don't want to see it or are already connected
if ( NimBLEDevice :: isIgnored ( advertisedAddress )) {
NIMBLE_LOGI ( LOG_TAG , "Ignoring device: address: %s" , advertisedAddress . toString (). c_str ());
return 0 ;
}
2020-05-13 22:03:56 -06:00
NimBLEAdvertisedDevice * advertisedDevice = nullptr ;
2020-05-17 20:21:35 -06:00
// If we've seen this device before get a pointer to it from the vector
for ( auto & it : pScan -> m_scanResults . m_advertisedDevicesVector ) {
if ( it -> getAddress () == advertisedAddress ) {
advertisedDevice = it ;
break ;
}
2020-03-29 17:44:20 -06:00
}
2020-05-17 20:21:35 -06:00
// If we haven't seen this device before; create a new instance and insert it in the vector.
2020-03-29 17:44:20 -06:00
// Otherwise just update the relevant parameters of the already known device.
if ( advertisedDevice == nullptr ){
advertisedDevice = new NimBLEAdvertisedDevice ();
advertisedDevice -> setAddress ( advertisedAddress );
advertisedDevice -> setAdvType ( event -> disc . event_type );
2020-05-17 20:21:35 -06:00
pScan -> m_scanResults . m_advertisedDevicesVector . push_back ( advertisedDevice );
2020-03-29 17:44:20 -06:00
NIMBLE_LOGI ( LOG_TAG , "NEW DEVICE FOUND: %s" , advertisedAddress . toString (). c_str ());
}
else {
NIMBLE_LOGI ( LOG_TAG , "UPDATING PREVIOUSLY FOUND DEVICE: %s" , advertisedAddress . toString (). c_str ());
}
2020-05-13 22:03:56 -06:00
advertisedDevice -> setRSSI ( event -> disc . rssi );
2020-07-28 20:09:54 -06:00
if ( event -> disc . length_data > 0 ) {
advertisedDevice -> parseAdvertisement ( event -> disc . data , event -> disc . length_data );
}
2020-05-29 18:26:41 -06:00
advertisedDevice -> m_timestamp = time ( nullptr );
2020-03-29 17:44:20 -06:00
if ( pScan -> m_pAdvertisedDeviceCallbacks ) {
2020-07-01 17:26:44 -06:00
if ( pScan -> m_wantDuplicates || ! advertisedDevice -> m_callbackSent ) {
// If not active scanning report the result to the listener.
if ( pScan -> m_scan_params . passive || event -> disc . event_type == BLE_HCI_ADV_TYPE_ADV_NONCONN_IND ) {
advertisedDevice -> m_callbackSent = true ;
pScan -> m_pAdvertisedDeviceCallbacks -> onResult ( advertisedDevice );
// Otherwise wait for the scan response so we can report all of the data at once.
} else if ( event -> disc . event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP ) {
advertisedDevice -> m_callbackSent = true ;
pScan -> m_pAdvertisedDeviceCallbacks -> onResult ( advertisedDevice );
}
2020-03-29 17:44:20 -06:00
}
}
return 0 ;
}
case BLE_GAP_EVENT_DISC_COMPLETE : {
NIMBLE_LOGD ( LOG_TAG , "discovery complete; reason=%d" ,
event -> disc_complete . reason );
2020-05-13 22:03:56 -06:00
2020-03-29 17:44:20 -06:00
if ( pScan -> m_scanCompleteCB != nullptr ) {
pScan -> m_scanCompleteCB ( pScan -> m_scanResults );
}
2020-05-13 22:03:56 -06:00
2020-06-21 22:07:01 -06:00
if ( pScan -> m_pTaskData != nullptr ) {
pScan -> m_pTaskData -> rc = event -> disc_complete . reason ;
xTaskNotifyGive ( pScan -> m_pTaskData -> task );
}
2020-03-29 17:44:20 -06:00
return 0 ;
}
default :
return 0 ;
}
} // gapEventHandler
/**
* @brief Should we perform an active or passive scan?
* The default is a passive scan. An active scan means that we will wish a scan response.
* @param [in] active If true, we perform an active scan otherwise a passive scan.
*/
void NimBLEScan :: setActiveScan ( bool active ) {
if ( active ) {
m_scan_params . passive = 0 ;
} else {
m_scan_params . passive = 1 ;
}
} // setActiveScan
2020-07-01 17:26:44 -06:00
/**
* @brief Set whether or not the BLE controller should only report results
* from devices it has not already seen.
2020-07-30 20:16:58 -06:00
* @param [in] enabled If true, scanned devices will only be reported once.
2020-07-01 17:26:44 -06:00
* @details The controller has a limited buffer and will start reporting
* dupicate devices once the limit is reached.
*/
2020-07-23 20:18:41 -06:00
void NimBLEScan :: setDuplicateFilter ( bool enabled ) {
m_scan_params . filter_duplicates = enabled ;
2020-07-01 17:26:44 -06:00
} // setDuplicateFilter
/**
* @brief Set whether or not the BLE controller only report scan results
* from devices advertising in limited discovery mode, i.e. directed advertising.
2020-07-30 20:16:58 -06:00
* @param [in] enabled If true, only limited discovery devices will be in scan results.
2020-07-01 17:26:44 -06:00
*/
2020-07-23 20:18:41 -06:00
void NimBLEScan :: setLimitedOnly ( bool enabled ) {
m_scan_params . limited = enabled ;
2020-07-01 17:26:44 -06:00
} // setLimited
/**
* @brief Sets the scan filter policy.
* @param [in] filter Can be one of:
2020-07-08 19:27:26 -06:00
* * BLE_HCI_SCAN_FILT_NO_WL (0)
* Scanner processes all advertising packets (white list not used) except\n
2020-07-01 17:26:44 -06:00
* directed, connectable advertising packets not sent to the scanner.
2020-07-08 19:27:26 -06:00
* * BLE_HCI_SCAN_FILT_USE_WL (1)
* Scanner processes advertisements from white list only. A connectable,\n
2020-07-01 17:26:44 -06:00
* directed advertisment is ignored unless it contains scanners address.
2020-07-08 19:27:26 -06:00
* * BLE_HCI_SCAN_FILT_NO_WL_INITA (2)
* Scanner process all advertising packets (white list not used). A\n
2020-07-01 17:26:44 -06:00
* connectable, directed advertisement shall not be ignored if the InitA
* is a resolvable private address.
2020-07-08 19:27:26 -06:00
* * BLE_HCI_SCAN_FILT_USE_WL_INITA (3)
* Scanner process advertisements from white list only. A connectable,\n
2020-07-01 17:26:44 -06:00
* directed advertisement shall not be ignored if the InitA is a
* resolvable private address.
*/
void NimBLEScan :: setFilterPolicy ( uint8_t filter ) {
m_scan_params . filter_policy = filter ;
} // setFilterPolicy
2020-03-29 17:44:20 -06:00
/**
* @brief Set the call backs to be invoked.
* @param [in] pAdvertisedDeviceCallbacks Call backs to be invoked.
* @param [in] wantDuplicates True if we wish to be called back with duplicates. Default is false.
*/
2020-07-01 17:26:44 -06:00
void NimBLEScan :: setAdvertisedDeviceCallbacks ( NimBLEAdvertisedDeviceCallbacks * pAdvertisedDeviceCallbacks ,
bool wantDuplicates ) {
m_wantDuplicates = wantDuplicates ;
2020-03-29 17:44:20 -06:00
m_pAdvertisedDeviceCallbacks = pAdvertisedDeviceCallbacks ;
} // setAdvertisedDeviceCallbacks
/**
* @brief Set the interval to scan.
2020-07-08 19:27:26 -06:00
* @param [in] intervalMSecs The scan interval (how often) in milliseconds.
2020-03-29 17:44:20 -06:00
*/
void NimBLEScan :: setInterval ( uint16_t intervalMSecs ) {
m_scan_params . itvl = intervalMSecs / 0.625 ;
} // setInterval
/**
* @brief Set the window to actively scan.
* @param [in] windowMSecs How long to actively scan.
*/
void NimBLEScan :: setWindow ( uint16_t windowMSecs ) {
m_scan_params . window = windowMSecs / 0.625 ;
} // setWindow
2020-07-23 20:18:41 -06:00
/**
* @brief Get the status of the scanner.
* @return true if scanning or scan starting.
*/
bool NimBLEScan :: isScanning () {
2021-01-10 21:54:32 -07:00
return ble_gap_disc_active ();
2020-07-23 20:18:41 -06:00
}
2020-03-29 17:44:20 -06:00
/**
* @brief Start scanning.
* @param [in] duration The duration in seconds for which to scan.
* @param [in] scanCompleteCB A function to be called when scanning has completed.
2020-07-08 19:27:26 -06:00
* @param [in] is_continue Set to true to save previous scan results, false to clear them.
2020-03-29 17:44:20 -06:00
* @return True if scan started or false if there was an error.
*/
bool NimBLEScan :: start ( uint32_t duration , void ( * scanCompleteCB )( NimBLEScanResults ), bool is_continue ) {
NIMBLE_LOGD ( LOG_TAG , ">> start(duration=%d)" , duration );
2020-05-13 22:03:56 -06:00
2020-03-29 17:44:20 -06:00
// Save the callback to be invoked when the scan completes.
2020-05-13 22:03:56 -06:00
m_scanCompleteCB = scanCompleteCB ;
2020-03-29 17:44:20 -06:00
// Save the duration in the case that the host is reset so we can reuse it.
m_duration = duration ;
2020-05-13 22:03:56 -06:00
2020-03-29 17:44:20 -06:00
// If 0 duration specified then we assume a continuous scan is desired.
if ( duration == 0 ){
duration = BLE_HS_FOREVER ;
}
else {
2021-01-10 21:54:32 -07:00
// convert duration to milliseconds
duration = duration * 1000 ;
2020-03-29 17:44:20 -06:00
}
2020-05-13 22:03:56 -06:00
2021-01-10 21:54:32 -07:00
// Set the flag to ignore the results while we are deleting the vector
2020-03-29 17:44:20 -06:00
if ( ! is_continue ) {
2021-01-10 21:54:32 -07:00
m_ignoreResults = true ;
2020-03-29 17:44:20 -06:00
}
2020-05-13 22:03:56 -06:00
2021-01-10 21:54:32 -07:00
int rc = ble_gap_disc ( m_own_addr_type , duration , & m_scan_params ,
NimBLEScan :: handleGapEvent , this );
2020-05-13 22:03:56 -06:00
2021-01-10 21:54:32 -07:00
switch ( rc ) {
case 0 :
if ( ! is_continue ) {
clearResults ();
}
break ;
case BLE_HS_EALREADY :
break ;
case BLE_HS_EBUSY :
NIMBLE_LOGE ( LOG_TAG , "Unable to scan - connection in progress." );
break ;
case BLE_HS_ETIMEOUT_HCI :
case BLE_HS_EOS :
case BLE_HS_ECONTROLLER :
case BLE_HS_ENOTSYNCED :
NIMBLE_LOGC ( LOG_TAG , "Unable to scan - Host Reset" );
break ;
default :
NIMBLE_LOGE ( LOG_TAG , "Error initiating GAP discovery procedure; rc=%d, %s" ,
rc , NimBLEUtils :: returnCodeToString ( rc ));
break ;
}
m_ignoreResults = false ;
NIMBLE_LOGD ( LOG_TAG , "<< start()" );
if ( rc != 0 || rc != BLE_HS_EALREADY ) {
2020-03-29 17:44:20 -06:00
return false ;
}
return true ;
} // start
/**
* @brief Start scanning and block until scanning has been completed.
* @param [in] duration The duration in seconds for which to scan.
2020-07-08 19:27:26 -06:00
* @param [in] is_continue Set to true to save previous scan results, false to clear them.
* @return The NimBLEScanResults.
2020-03-29 17:44:20 -06:00
*/
NimBLEScanResults NimBLEScan :: start ( uint32_t duration , bool is_continue ) {
2020-06-21 22:07:01 -06:00
if ( duration == 0 ) {
NIMBLE_LOGW ( LOG_TAG , "Blocking scan called with duration = forever" );
2020-03-29 17:44:20 -06:00
}
2020-06-21 22:07:01 -06:00
ble_task_data_t taskData = { nullptr , xTaskGetCurrentTaskHandle (), 0 , nullptr };
m_pTaskData = & taskData ;
if ( start ( duration , nullptr , is_continue )) {
ulTaskNotifyTake ( pdTRUE , portMAX_DELAY );
}
m_pTaskData = nullptr ;
2020-03-29 17:44:20 -06:00
return m_scanResults ;
} // start
/**
* @brief Stop an in progress scan.
2020-07-08 19:27:26 -06:00
* @return True if successful.
2020-03-29 17:44:20 -06:00
*/
2020-06-21 22:07:01 -06:00
bool NimBLEScan :: stop () {
2020-03-29 17:44:20 -06:00
NIMBLE_LOGD ( LOG_TAG , ">> stop()" );
2020-05-13 22:03:56 -06:00
2020-03-29 17:44:20 -06:00
int rc = ble_gap_disc_cancel ();
if ( rc != 0 && rc != BLE_HS_EALREADY ) {
NIMBLE_LOGE ( LOG_TAG , "Failed to cancel scan; rc=%d \n " , rc );
2020-06-21 22:07:01 -06:00
return false ;
2020-03-29 17:44:20 -06:00
}
2020-07-12 20:35:10 -06:00
if ( rc != BLE_HS_EALREADY && m_scanCompleteCB != nullptr ) {
2020-03-29 17:44:20 -06:00
m_scanCompleteCB ( m_scanResults );
}
2020-05-13 22:03:56 -06:00
2020-06-21 22:07:01 -06:00
if ( m_pTaskData != nullptr ) {
xTaskNotifyGive ( m_pTaskData -> task );
}
2020-05-13 22:03:56 -06:00
2020-03-29 17:44:20 -06:00
NIMBLE_LOGD ( LOG_TAG , "<< stop()" );
2020-06-21 22:07:01 -06:00
return true ;
2020-03-29 17:44:20 -06:00
} // stop
2020-07-08 19:27:26 -06:00
/**
* @brief Delete peer device from the scan results vector.
* @param [in] address The address of the device to delete from the results.
* @details After disconnecting, it may be required in the case we were connected to a device without a public address.
*/
2020-05-10 07:21:46 -06:00
void NimBLEScan :: erase ( const NimBLEAddress & address ) {
2020-03-29 17:44:20 -06:00
NIMBLE_LOGI ( LOG_TAG , "erase device: %s" , address . toString (). c_str ());
2020-05-17 20:21:35 -06:00
2020-05-18 06:47:44 -06:00
for ( auto it = m_scanResults . m_advertisedDevicesVector . begin (); it != m_scanResults . m_advertisedDevicesVector . end (); ++ it ) {
2020-05-17 20:21:35 -06:00
if (( * it ) -> getAddress () == address ) {
delete * it ;
m_scanResults . m_advertisedDevicesVector . erase ( it );
break ;
}
}
2020-03-29 17:44:20 -06:00
}
/**
2020-07-08 19:27:26 -06:00
* @brief If the host reset the scan will have stopped so we should set the flag as stopped.
2020-03-29 17:44:20 -06:00
*/
void NimBLEScan :: onHostReset () {
}
/**
* @brief Get the results of the scan.
* @return NimBLEScanResults object.
*/
NimBLEScanResults NimBLEScan :: getResults () {
return m_scanResults ;
}
/**
* @brief Clear the results of the scan.
*/
void NimBLEScan :: clearResults () {
2020-05-17 20:21:35 -06:00
for ( auto & it : m_scanResults . m_advertisedDevicesVector ) {
delete it ;
2020-03-29 17:44:20 -06:00
}
2020-05-17 20:21:35 -06:00
m_scanResults . m_advertisedDevicesVector . clear ();
2020-03-29 17:44:20 -06:00
}
/**
* @brief Dump the scan results to the log.
*/
void NimBLEScanResults :: dump () {
NIMBLE_LOGD ( LOG_TAG , ">> Dump scan results:" );
for ( int i = 0 ; i < getCount (); i ++ ) {
NIMBLE_LOGI ( LOG_TAG , "- %s" , getDevice ( i ). toString (). c_str ());
}
} // dump
/**
2020-07-08 19:27:26 -06:00
* @brief Get the count of devices found in the last scan.
2020-03-29 17:44:20 -06:00
* @return The number of devices found in the last scan.
*/
int NimBLEScanResults :: getCount () {
2020-05-17 20:21:35 -06:00
return m_advertisedDevicesVector . size ();
2020-03-29 17:44:20 -06:00
} // getCount
/**
* @brief Return the specified device at the given index.
* The index should be between 0 and getCount()-1.
* @param [in] i The index of the device.
* @return The device at the specified index.
*/
NimBLEAdvertisedDevice NimBLEScanResults :: getDevice ( uint32_t i ) {
2020-05-17 20:21:35 -06:00
return * m_advertisedDevicesVector [ i ];
}
2020-05-22 20:13:52 -06:00
/**
* @brief Get iterator to the beginning of the vector of advertised device pointers.
* @return An iterator to the beginning of the vector of advertised device pointers.
*/
std :: vector < NimBLEAdvertisedDevice *>:: iterator NimBLEScanResults :: begin () {
return m_advertisedDevicesVector . begin ();
}
/**
* @brief Get iterator to the end of the vector of advertised device pointers.
* @return An iterator to the end of the vector of advertised device pointers.
*/
std :: vector < NimBLEAdvertisedDevice *>:: iterator NimBLEScanResults :: end () {
return m_advertisedDevicesVector . end ();
}
2020-05-17 20:21:35 -06:00
/**
2020-07-08 19:27:26 -06:00
* @brief Get a pointer to the specified device at the given address.
2020-05-17 20:21:35 -06:00
* If the address is not found a nullptr is returned.
* @param [in] address The address of the device.
* @return A pointer to the device at the specified address.
*/
NimBLEAdvertisedDevice * NimBLEScanResults :: getDevice ( const NimBLEAddress & address ) {
for ( size_t index = 0 ; index < m_advertisedDevicesVector . size (); index ++ ) {
if ( m_advertisedDevicesVector [ index ] -> getAddress () == address ) {
return m_advertisedDevicesVector [ index ];
}
2020-03-29 17:44:20 -06:00
}
2020-05-17 20:21:35 -06:00
return nullptr ;
2020-03-29 17:44:20 -06:00
}
2020-05-13 22:03:56 -06:00
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
2020-03-29 17:44:20 -06:00
#endif /* CONFIG_BT_ENABLED */