Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libraries/Bluefruit52Lib/library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=Adafruit Bluefruit nRF52 Libraries
version=0.21.0
version=0.22.0
author=Adafruit
maintainer=Adafruit <info@adafruit.com>
sentence=Arduino library for nRF52-based Adafruit Bluefruit LE modules
Expand Down
212 changes: 177 additions & 35 deletions libraries/Bluefruit52Lib/src/BLEAdvertising.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,51 @@
/* BLEAdvertisingData shared between ADV and ScanResponse
*------------------------------------------------------------------*/
BLEAdvertisingData::BLEAdvertisingData(void)
: _data(NULL), _max_len(0), _count(0)
{
_count = 0;
arrclr(_data);
setMaxLen(BLE_GAP_ADV_SET_DATA_SIZE_MAX);
}

BLEAdvertisingData::~BLEAdvertisingData(void)
{
if (_data) rtos_free(_data);
}

void BLEAdvertisingData::setMaxLen(uint8_t max_len)
{
if (_data && max_len == _max_len) return;

uint8_t* old_data = _data;
uint8_t old_count = _count;

if (old_count)
{
// stop advertising before re-allocate adv buffer
if (Bluefruit.Advertising.isRunning()) Bluefruit.Advertising.stop();
}

_data = (uint8_t*) rtos_malloc(max_len);
if (_data)
{
// Preserve previously added bytes across resize; truncate if shrinking.
uint8_t keep = (old_count <= max_len) ? old_count : max_len;
if (old_data && keep) memcpy(_data, old_data, keep);
if (keep < max_len) memset(_data + keep, 0, max_len - keep);
_max_len = max_len;
_count = keep;
}
else
{
_max_len = 0;
_count = 0;
}

if (old_data) rtos_free(old_data);
}

bool BLEAdvertisingData::addData(uint8_t type, const void* data, uint8_t len)
{
VERIFY( _count + len + 2 <= BLE_GAP_ADV_SET_DATA_SIZE_MAX );
VERIFY( _count + len + 2 <= _max_len );

uint8_t* adv_data = &_data[_count];

Expand Down Expand Up @@ -177,19 +214,34 @@ bool BLEAdvertisingData::addService(BLEClientService& service)
*/
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)
{
// Need room for at least [len][type][1 char]
VERIFY(_count + 2 < _max_len);
uint8_t* adv_slot = &_data[_count];
const uint8_t remaining = _max_len - _count - 2;

// Try writing the name directly into _data. If it fits, sd returns NRF_SUCCESS
// with name_len = bytes copied. If it doesn't fit, sd returns NRF_ERROR_DATA_SIZE
// without copying and sets name_len to the full device name length.
uint16_t name_len = remaining;
uint32_t err = sd_ble_gap_device_name_get(adv_slot + 2, &name_len);

uint8_t type;
uint8_t len;
if (err == NRF_SUCCESS) {
type = BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME;
len = (uint8_t) name_len;
} else {
// Truncate: fetch full name into a temp buffer, then copy what fits.
uint8_t buftmp[name_len];
VERIFY_STATUS(sd_ble_gap_device_name_get(buftmp, &name_len), false);
memcpy(adv_slot + 2, buftmp, remaining);
type = BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME;
len = BLE_GAP_ADV_SET_DATA_SIZE_MAX - (_count+2);
len = remaining;
}

VERIFY( addData(type, name, len) );
adv_slot[0] = len + 1;
adv_slot[1] = type;
_count += 2 + len;

return type == BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME;
}
Expand Down Expand Up @@ -231,7 +283,7 @@ uint8_t* BLEAdvertisingData::getData(void)

bool BLEAdvertisingData::setData(uint8_t const * data, uint8_t count)
{
VERIFY( data && (count <= BLE_GAP_ADV_SET_DATA_SIZE_MAX) );
VERIFY( data && (count <= _max_len) );

memcpy(_data, data, count);
_count = count;
Expand All @@ -242,7 +294,7 @@ bool BLEAdvertisingData::setData(uint8_t const * data, uint8_t count)
void BLEAdvertisingData::clearData(void)
{
_count = 0;
arrclr(_data);
if (_data) memset(_data, 0, _max_len);
}

/*------------------------------------------------------------------*/
Expand Down Expand Up @@ -275,6 +327,16 @@ void BLEAdvertising::setFastTimeout(uint16_t sec)
void BLEAdvertising::setType(uint8_t adv_type)
{
_type = adv_type;
if (isExtended())
{
// Connectable extended PDUs are capped at 238 bytes; other extended PDUs at 255.
setMaxLen(isConnectable() ? BLE_GAP_ADV_SET_DATA_SIZE_EXTENDED_CONNECTABLE_MAX_SUPPORTED
: BLE_GAP_ADV_SET_DATA_SIZE_EXTENDED_MAX_SUPPORTED);
}
else
{
setMaxLen(BLE_GAP_ADV_SET_DATA_SIZE_MAX);
}
}

/**
Expand All @@ -296,6 +358,22 @@ void BLEAdvertising::setIntervalMS(uint16_t fast, uint16_t slow)
setInterval(MS1000TO625(fast), MS1000TO625(slow));
}

void BLEAdvertising::setMaxEvents(uint8_t maxEvents)
{
_max_events = maxEvents;
}

void BLEAdvertising::setFilter(uint8_t filter)
{
_filter = filter;
}

void BLEAdvertising::setPhy(uint8_t phy)
{
_primary_phy = phy;
_secondary_phy = phy;
}

/**
* Get current active interval
* @return Either slow or fast interval in unit of 0.625 ms
Expand Down Expand Up @@ -324,6 +402,42 @@ bool BLEAdvertising::isRunning(void)
return _running;
}

bool BLEAdvertising::isScannable(void)
{
return _type == BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED
|| _type == BLE_GAP_ADV_TYPE_NONCONNECTABLE_SCANNABLE_UNDIRECTED
|| _type == BLE_GAP_ADV_TYPE_EXTENDED_NONCONNECTABLE_SCANNABLE_UNDIRECTED
|| _type == BLE_GAP_ADV_TYPE_EXTENDED_NONCONNECTABLE_SCANNABLE_DIRECTED;
}

bool BLEAdvertising::isConnectable(void)
{
return _type == BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED
|| _type == BLE_GAP_ADV_TYPE_CONNECTABLE_NONSCANNABLE_DIRECTED_HIGH_DUTY_CYCLE
|| _type == BLE_GAP_ADV_TYPE_CONNECTABLE_NONSCANNABLE_DIRECTED
|| _type == BLE_GAP_ADV_TYPE_EXTENDED_CONNECTABLE_NONSCANNABLE_UNDIRECTED
|| _type == BLE_GAP_ADV_TYPE_EXTENDED_CONNECTABLE_NONSCANNABLE_DIRECTED;
}

bool BLEAdvertising::isDirected(void)
{
return _type == BLE_GAP_ADV_TYPE_CONNECTABLE_NONSCANNABLE_DIRECTED_HIGH_DUTY_CYCLE
|| _type == BLE_GAP_ADV_TYPE_CONNECTABLE_NONSCANNABLE_DIRECTED
|| _type == BLE_GAP_ADV_TYPE_EXTENDED_CONNECTABLE_NONSCANNABLE_DIRECTED
|| _type == BLE_GAP_ADV_TYPE_EXTENDED_NONCONNECTABLE_SCANNABLE_DIRECTED
|| _type == BLE_GAP_ADV_TYPE_EXTENDED_NONCONNECTABLE_NONSCANNABLE_DIRECTED;
}

bool BLEAdvertising::isExtended(void)
{
return _type == BLE_GAP_ADV_TYPE_EXTENDED_CONNECTABLE_NONSCANNABLE_UNDIRECTED
|| _type == BLE_GAP_ADV_TYPE_EXTENDED_CONNECTABLE_NONSCANNABLE_DIRECTED
|| _type == BLE_GAP_ADV_TYPE_EXTENDED_NONCONNECTABLE_SCANNABLE_UNDIRECTED
|| _type == BLE_GAP_ADV_TYPE_EXTENDED_NONCONNECTABLE_SCANNABLE_DIRECTED
|| _type == BLE_GAP_ADV_TYPE_EXTENDED_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED
|| _type == BLE_GAP_ADV_TYPE_EXTENDED_NONCONNECTABLE_NONSCANNABLE_DIRECTED;
}

bool BLEAdvertising::setBeacon(BLEBeacon& beacon)
{
return beacon.start(*this);
Expand All @@ -347,25 +461,17 @@ bool BLEAdvertising::_start(uint16_t interval, uint16_t timeout) {
.interval = interval , // advertising interval (in units of 0.625 ms)
.duration = (uint16_t) (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 ,
.max_adv_evts = _max_events , // can be used for fast/slow mode
.channel_mask = { 0, 0, 0, 0, 0 } , // 40 channel, set 1 to disable, e.g. { 0, 0, 0, 0, 0xA0 } for not primary 37 and 38
.filter_policy = _filter ,

.primary_phy = BLE_GAP_PHY_AUTO , // 1 Mbps will be used
.secondary_phy = BLE_GAP_PHY_AUTO , // 1 Mbps will be used
.primary_phy = (isExtended() ? _primary_phy : (uint8_t)BLE_GAP_PHY_AUTO) ,
.secondary_phy = _secondary_phy ,
// , .set_id, .scan_req_notification
};

switch(_type) {
case BLE_GAP_ADV_TYPE_CONNECTABLE_NONSCANNABLE_DIRECTED_HIGH_DUTY_CYCLE:
case BLE_GAP_ADV_TYPE_CONNECTABLE_NONSCANNABLE_DIRECTED:
case BLE_GAP_ADV_TYPE_EXTENDED_CONNECTABLE_NONSCANNABLE_DIRECTED:
case BLE_GAP_ADV_TYPE_EXTENDED_NONCONNECTABLE_SCANNABLE_DIRECTED:
case BLE_GAP_ADV_TYPE_EXTENDED_NONCONNECTABLE_NONSCANNABLE_DIRECTED:
adv_para.p_peer_addr = &_peer_addr;
break;

default: break;
if (isDirected()) {
adv_para.p_peer_addr = &_peer_addr;
}

// stop first if current running since we may change advertising data/params
Expand All @@ -375,10 +481,34 @@ bool BLEAdvertising::_start(uint16_t interval, uint16_t timeout) {

// gap_adv long-live is required by SD v6
static ble_gap_adv_data_t gap_adv;
gap_adv.adv_data.p_data = _data;
gap_adv.adv_data.len = _count;
gap_adv.scan_rsp_data.p_data = Bluefruit.ScanResponse.getData();
gap_adv.scan_rsp_data.len = Bluefruit.ScanResponse.count();

// no advertising data supported?
// https://infocenter.nordicsemi.com/topic/com.nordic.infocenter.s140.api.v7.3.0/group___b_l_e___g_a_p___a_d_v___t_y_p_e_s.html
if ( _type == BLE_GAP_ADV_TYPE_CONNECTABLE_NONSCANNABLE_DIRECTED
|| _type == BLE_GAP_ADV_TYPE_CONNECTABLE_NONSCANNABLE_DIRECTED_HIGH_DUTY_CYCLE
|| _type == BLE_GAP_ADV_TYPE_EXTENDED_NONCONNECTABLE_SCANNABLE_UNDIRECTED
|| _type == BLE_GAP_ADV_TYPE_EXTENDED_NONCONNECTABLE_SCANNABLE_DIRECTED )
{
gap_adv.adv_data.p_data = nullptr;
gap_adv.adv_data.len = 0;
}
else
{
gap_adv.adv_data.p_data = _data;
gap_adv.adv_data.len = _count;
}

// no scan response data required?
if (!isScannable())
{
gap_adv.scan_rsp_data.p_data = nullptr;
gap_adv.scan_rsp_data.len = 0;
}
else
{
gap_adv.scan_rsp_data.p_data = Bluefruit.ScanResponse.getData();
gap_adv.scan_rsp_data.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 );
Expand Down Expand Up @@ -479,9 +609,21 @@ void BLEAdvertising::_eventHandler(ble_evt_t* evt)
if (_stop_cb) ada_callback(NULL, 0, _stop_cb);
}
}
}else
{
if (evt->evt.gap_evt.params.adv_set_terminated.reason == BLE_GAP_EVT_ADV_SET_TERMINATED_REASON_LIMIT_REACHED)
{
_running = false;

// Stop advertising
Bluefruit._stopConnLed(); // stop blinking

// invoke stop callback
if (_stop_cb) ada_callback(NULL, 0, _stop_cb);
}
}
break;

default: break;
}
}
}
30 changes: 28 additions & 2 deletions libraries/Bluefruit52Lib/src/BLEAdvertising.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,23 @@ class Advertisable
class BLEAdvertisingData
{
protected:
uint8_t _data[BLE_GAP_ADV_SET_DATA_SIZE_MAX];
uint8_t _count;
uint8_t* _data;
uint8_t _max_len;
uint8_t _count;

public:
BLEAdvertisingData(void);
~BLEAdvertisingData(void);

// Class owns a heap buffer; disable copy to prevent shallow-copy / double-free.
BLEAdvertisingData(const BLEAdvertisingData&) = delete;
BLEAdvertisingData& operator=(const BLEAdvertisingData&) = delete;

// Resize the internal buffer (heap-allocated). Defaults to BLE_GAP_ADV_SET_DATA_SIZE_MAX (31).
// For extended advertising use BLE_GAP_ADV_SET_DATA_SIZE_EXTENDED_CONNECTABLE_MAX_SUPPORTED (238)
// or BLE_GAP_ADV_SET_DATA_SIZE_EXTENDED_MAX_SUPPORTED (255).
// Reallocates the buffer; any previously added data is discarded.
void setMaxLen(uint8_t max_len);

/*------------- Adv Data -------------*/
bool addData(uint8_t type, const void* data, uint8_t len);
Expand Down Expand Up @@ -120,6 +132,8 @@ class BLEAdvertising : public BLEAdvertisingData

BLEAdvertising(void);

// Sets the advertising type and resizes the internal buffer accordingly
// (extended types -> larger buffer, legacy types -> 31-byte buffer).
void setType(uint8_t adv_type);
void setFastTimeout(uint16_t sec);

Expand All @@ -129,6 +143,10 @@ class BLEAdvertising : public BLEAdvertisingData
void setInterval (uint16_t fast, uint16_t slow);
void setIntervalMS(uint16_t fast, uint16_t slow);

void setMaxEvents(uint8_t maxEvents);
void setFilter(uint8_t filter);
void setPhy(uint8_t phy);

uint16_t getInterval(void);

bool setBeacon(BLEBeacon& beacon);
Expand All @@ -138,6 +156,10 @@ class BLEAdvertising : public BLEAdvertisingData
void setPeerAddress(const ble_gap_addr_t& peer_addr);

bool isRunning(void);
bool isScannable(void);
bool isConnectable(void);
bool isDirected(void);
bool isExtended(void);

void restartOnDisconnect(bool enable);
bool start(uint16_t timeout = 0);
Expand All @@ -153,6 +175,10 @@ class BLEAdvertising : public BLEAdvertisingData
private:
uint8_t _hdl;
uint8_t _type;
uint8_t _max_events = 0; // initially time limited
uint8_t _filter = BLE_GAP_ADV_FP_ANY;
uint8_t _primary_phy = BLE_GAP_PHY_AUTO;
uint8_t _secondary_phy = BLE_GAP_PHY_AUTO;
bool _start_if_disconnect;
bool _running;
ble_gap_addr_t _peer_addr; //! Target address for an ADV_DIRECT_IND advertisement
Expand Down
Loading