Skip to content
Open
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
63 changes: 63 additions & 0 deletions .github/workflows/platformio-examples.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Build Arduino examples

on:
push:
paths:
- 'src/**'
- 'examples/**'
- 'library.properties'
- '.github/workflows/platformio-examples.yml'
pull_request:
paths:
- 'src/**'
- 'examples/**'
- 'library.properties'
- '.github/workflows/platformio-examples.yml'
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Cache PlatformIO
uses: actions/cache@v4
with:
path: ~/.platformio
key: platformio-${{ runner.os }}-${{ hashFiles('library.properties') }}
restore-keys: |
platformio-${{ runner.os }}-

- name: Install PlatformIO Core
run: pip install --upgrade platformio

- name: Build examples
shell: bash
run: |
set -euo pipefail
shopt -s nullglob
sketches=(examples/*/*.ino)

if [ ${#sketches[@]} -eq 0 ]; then
echo "No examples found"
exit 1
fi

for sketch in "${sketches[@]}"; do
sketch_dir=$(dirname "$sketch")
sketch_name=$(basename "$sketch_dir")
echo "::group::Building ${sketch_name}"
pio ci \
--board esp32dev \
--lib="." \
"$sketch"
echo "::endgroup::"
done
13 changes: 12 additions & 1 deletion examples/NimBLE_Scan_Continuous/NimBLE_Scan_Continuous.ino
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
* This example will scan forever while consuming as few resources as possible
* and report all advertisments on the serial monitor.
*
* The scan callback prints the primary advertising channel for each
* advertisement when available, demonstrating the use of
* NimBLEAdvertisedDevice::getChannel().
*
* Created: on January 31 2021
* Author: H2zero
*
Expand All @@ -13,7 +17,14 @@ NimBLEScan* pBLEScan;

class MyAdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks {
void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
Serial.printf("Advertised Device: %s \n", advertisedDevice->toString().c_str());
uint8_t channel = advertisedDevice->getChannel();
if (channel != 0xFF) {
Serial.printf("Advertised Device on channel %u: %s \n",
channel, advertisedDevice->toString().c_str());
} else {
Serial.printf("Advertised Device on unknown channel: %s \n",
advertisedDevice->toString().c_str());
}
}
};

Expand Down
14 changes: 14 additions & 0 deletions src/NimBLEAdvertisedDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ NimBLEAdvertisedDevice::NimBLEAdvertisedDevice() :
m_callbackSent = false;
m_timestamp = 0;
m_advLength = 0;
m_channelIndex = 0xFF;
} // NimBLEAdvertisedDevice


Expand Down Expand Up @@ -823,6 +824,19 @@ time_t NimBLEAdvertisedDevice::getTimestamp() {
} // getTimestamp


/**
* @brief Get the primary advertising channel.
* @return The advertising channel (37, 38, or 39) when available, or 0xFF when unknown.
*/
uint8_t NimBLEAdvertisedDevice::getChannel() {
// Map controller channel index (0-2) to BLE advertising channels (37-39)
if (m_channelIndex <= 2) {
return 37 + m_channelIndex;
}
return 0xFF;
} // getChannel


/**
* @brief Get the length of the payload advertised by the device.
* @return The size of the payload in bytes.
Expand Down
6 changes: 6 additions & 0 deletions src/NimBLEAdvertisedDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ class NimBLEScan;
*
* When we perform a %BLE scan, the result will be a set of devices that are advertising. This
* class provides a model of a detected device.
*
* The getChannel() method returns the primary advertising channel (37, 38, or 39) on which the
* advertisement was received, or 0xFF if the channel information is not available from the controller.
*/
class NimBLEAdvertisedDevice {
public:
Expand Down Expand Up @@ -120,6 +123,7 @@ class NimBLEAdvertisedDevice {
size_t getPayloadLength();
uint8_t getAddressType();
time_t getTimestamp();
uint8_t getChannel();
bool isAdvertisingService(const NimBLEUUID &uuid);
bool haveAppearance();
bool haveManufacturerData();
Expand Down Expand Up @@ -149,6 +153,7 @@ class NimBLEAdvertisedDevice {
void setAdvType(uint8_t advType, bool isLegacyAdv);
void setPayload(const uint8_t *payload, uint8_t length, bool append);
void setRSSI(int rssi);
void setChannelIndex(uint8_t channel) { m_channelIndex = channel; }
#if CONFIG_BT_NIMBLE_EXT_ADV
void setSetId(uint8_t sid) { m_sid = sid; }
void setPrimaryPhy(uint8_t phy) { m_primPhy = phy; }
Expand All @@ -164,6 +169,7 @@ class NimBLEAdvertisedDevice {
time_t m_timestamp;
bool m_callbackSent;
uint8_t m_advLength;
uint8_t m_channelIndex;
#if CONFIG_BT_NIMBLE_EXT_ADV
bool m_isLegacyAdv;
uint8_t m_sid;
Expand Down
1 change: 1 addition & 0 deletions src/NimBLEScan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ NimBLEScan::~NimBLEScan() {

advertisedDevice->m_timestamp = time(nullptr);
advertisedDevice->setRSSI(disc.rssi);
advertisedDevice->setChannelIndex(disc.channel_index);
advertisedDevice->setPayload(disc.data, disc.length_data, (isLegacyAdv &&
event_type == BLE_HCI_ADV_RPT_EVTYPE_SCAN_RSP));

Expand Down
6 changes: 6 additions & 0 deletions src/nimble/nimble/host/include/host/ble_gap.h
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,9 @@ struct ble_gap_ext_disc_desc {
* set (BLE_ADDR_ANY otherwise).
*/
ble_addr_t direct_addr;

/** Primary advertising channel index (0xFF if unavailable) */
uint8_t channel_index;
};
#endif

Expand Down Expand Up @@ -433,6 +436,9 @@ struct ble_gap_disc_desc {
* event type (BLE_ADDR_ANY otherwise).
*/
ble_addr_t direct_addr;

/** Primary advertising channel index (0xFF if unavailable) */
uint8_t channel_index;
};

struct ble_gap_repeat_pairing {
Expand Down
19 changes: 17 additions & 2 deletions src/nimble/nimble/host/src/ble_hs_hci_evt.c
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ ble_hs_hci_evt_le_adv_rpt_first_pass(const void *data, unsigned int len)
rpt = data;

len -= sizeof(*rpt) + 1;
data += sizeof(rpt) + 1;
data += sizeof(*rpt) + 1;

if (rpt->data_len > len) {
return BLE_HS_ECONTROLLER;
Expand Down Expand Up @@ -501,18 +501,23 @@ ble_hs_hci_evt_le_adv_rpt(uint8_t subevent, const void *data, unsigned int len)
data += sizeof(*ev);

desc.direct_addr = *BLE_ADDR_ANY;
desc.channel_index = 0xFF;

for (i = 0; i < ev->num_reports; i++) {
rpt = data;

data += sizeof(rpt) + rpt->data_len + 1;
data += sizeof(*rpt) + rpt->data_len + 1;

desc.event_type = rpt->type;
desc.addr.type = rpt->addr_type;
memcpy(desc.addr.val, rpt->addr, BLE_DEV_ADDR_LEN);
desc.length_data = rpt->data_len;
desc.data = rpt->data;
desc.rssi = rpt->data[rpt->data_len];
/* Channel index follows RSSI if available */
if ((const uint8_t*)data - (const uint8_t*)rpt > sizeof(*rpt) + rpt->data_len + 1) {
desc.channel_index = rpt->data[rpt->data_len + 1];
}

ble_gap_rx_adv_report(&desc);
}
Expand All @@ -535,6 +540,7 @@ ble_hs_hci_evt_le_dir_adv_rpt(uint8_t subevent, const void *data, unsigned int l
/* Data fields not present in a direct advertising report. */
desc.data = NULL;
desc.length_data = 0;
desc.channel_index = 0xFF;

for (i = 0; i < ev->num_reports; i++) {
desc.event_type = ev->reports[i].type;
Expand Down Expand Up @@ -612,6 +618,7 @@ ble_hs_hci_evt_le_ext_adv_rpt(uint8_t subevent, const void *data,
report = &ev->reports[0];
for (i = 0; i < ev->num_reports; i++) {
memset(&desc, 0, sizeof(desc));
desc.channel_index = 0xFF;

desc.props = (report->evt_type) & 0x1F;
if (desc.props & BLE_HCI_ADV_LEGACY_MASK) {
Expand Down Expand Up @@ -649,6 +656,14 @@ ble_hs_hci_evt_le_ext_adv_rpt(uint8_t subevent, const void *data,
desc.prim_phy = report->pri_phy;
desc.sec_phy = report->sec_phy;
desc.periodic_adv_itvl = report->periodic_itvl;
/* Channel index may follow the data if available */
if (report->data_len < 255) {
const uint8_t *channel_ptr = &report->data[report->data_len];
/* Verify we're not reading past the event data */
if ((const uint8_t*)channel_ptr < (const uint8_t*)data + len) {
desc.channel_index = *channel_ptr;
}
}

ble_gap_rx_ext_adv_report(&desc);

Comment on lines 668 to 669

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Skip channel index byte when advancing extended reports

After reading desc.channel_index, the extended advertising loop still advances to the next report with report = (const void *)&report->data[report->data_len]; (lines 668‑670). When a controller actually appends the channel index immediately after the data, this pointer calculation leaves that byte in front of the next report so every subsequent iteration interprets the channel byte as part of the next header, yielding corrupted descriptors. The iterator should advance by the extra byte whenever one was consumed.

Useful? React with 👍 / 👎.

Expand Down