Skip to content

Conversation

@DTTerastar
Copy link

@DTTerastar DTTerastar commented Nov 9, 2025

  • Introduced m_channelIndex in NimBLEAdvertisedDevice to store the primary advertising channel.
  • Implemented getChannel() method to retrieve the advertising channel.
  • Updated setChannelIndex() method for setting the channel index.
  • Modified handleGapEvent() in NimBLEScan to set the channel index from the discovery event.
  • Added channel_index field to ble_gap_ext_disc_desc and ble_gap_disc_desc structures.
  • Enhanced ble_hs_hci_evt_le_adv_rpt() to handle channel index in advertising reports.

Summary by CodeRabbit

  • New Features

    • BLE devices now report their primary advertising channel (37–39) during discovery; displays 0xFF when unavailable
    • Scan results display the advertising channel for each discovered device
  • Tests & CI/CD

    • Added GitHub Actions workflow for automated PlatformIO example builds on pushes and pull requests

…ctures

- Introduced m_channelIndex in NimBLEAdvertisedDevice to store the primary advertising channel.
- Implemented getChannel() method to retrieve the advertising channel.
- Updated setChannelIndex() method for setting the channel index.
- Modified handleGapEvent() in NimBLEScan to set the channel index from the discovery event.
- Added channel_index field to ble_gap_ext_disc_desc and ble_gap_disc_desc structures.
- Enhanced ble_hs_hci_evt_le_adv_rpt() to handle channel index in advertising reports.
@coderabbitai
Copy link

coderabbitai bot commented Nov 9, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

The PR adds BLE advertising channel tracking to NimBLE by introducing a private channel index member and public getChannel() method to NimBLEAdvertisedDevice. It extends GAP report structures with channel_index fields, populates these in HCI event handlers, and updates the scanning example to display advertising channels.

Changes

Cohort / File(s) Change Summary
NimBLEAdvertisedDevice interface
src/NimBLEAdvertisedDevice.h, src/NimBLEAdvertisedDevice.cpp
Added private member m_channelIndex (initialized to 0xFF), public method getChannel() that maps channel indices 0–2 to BLE channels 37–39, and public setter setChannelIndex()
BLE GAP structures
src/nimble/nimble/host/include/host/ble_gap.h
Added uint8_t channel_index field to ble_gap_ext_disc_desc and ble_gap_disc_desc structs to carry primary advertising channel information
HCI event handling
src/nimble/nimble/host/src/ble_hs_hci_evt.c
Initialize channel_index to 0xFF in LE advertising report paths (le_adv_rpt, le_dir_adv_rpt, le_ext_adv_rpt); conditionally read optional channel index byte after payload with bounds checking; fix pointer arithmetic in LE adv parsing
Device discovery
src/NimBLEScan.cpp
Call setChannelIndex() on discovered devices during GAP discovery handling to populate channel information
GitHub Actions
.github/workflows/platformio-examples.yml
New workflow to build PlatformIO Arduino examples on pushes/PRs affecting src, examples, or library.properties; configures Python 3.11, caches PlatformIO state, and builds each example sketch
Example sketch
examples/NimBLE_Scan_Continuous/NimBLE_Scan_Continuous.ino
Updated scan callback to retrieve and display advertising channel via getChannel(), with fallback message for unknown channels (0xFF)

Sequence Diagram

sequenceDiagram
    participant HCI as HCI Event
    participant Parser as ble_hs_hci_evt.c
    participant GAP as GAP Struct
    participant Scan as NimBLEScan
    participant Device as NimBLEAdvertisedDevice
    participant App as Example App

    HCI->>Parser: LE Advertising Report
    Parser->>Parser: Initialize channel_index = 0xFF
    Parser->>Parser: Check if extra byte after RSSI
    alt Extra byte exists & in bounds
        Parser->>Parser: Read channel_index from report
    end
    Parser->>GAP: Populate ble_gap_disc_desc with channel_index
    GAP->>Scan: Report discovered device
    Scan->>Device: setChannelIndex(channel_index)
    Device->>Device: Store m_channelIndex
    App->>Device: getChannel()
    Device->>Device: Map m_channelIndex (0–2) → BLE channel (37–39)
    Device->>App: Return mapped channel or 0xFF
    App->>App: Display advertising channel in scan results
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • HCI event parsing (ble_hs_hci_evt.c): Requires careful verification of pointer arithmetic, bounds checking logic across three advertising report paths (le_adv_rpt, le_dir_adv_rpt, le_ext_adv_rpt), and the fix from sizeof(rpt) to sizeof(*rpt).
  • Channel mapping logic (NimBLEAdvertisedDevice.cpp): Verify the mapping formula (37 + m_channelIndex) and edge case handling for 0xFF.
  • Integration point (NimBLEScan.cpp): Confirm setChannelIndex is called at the correct location in discovery flow.
  • Workflow definition (.github/workflows/platformio-examples.yml): Standard GitHub Actions syntax; check caching key and build command correctness.

Poem

🐰 Hop through channels 37 to 39,

Advertising whispers now we see!

Where devices dance on BLE waves,

Channel secrets that NimBLE saves. 🔍✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding channel index handling to NimBLEAdvertisedDevice and related structures, which is the core objective of the PR.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch adv-channel

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a828ac and 0b6ddee.

📒 Files selected for processing (1)
  • src/nimble/nimble/host/src/ble_hs_hci_evt.c (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/nimble/nimble/host/src/ble_hs_hci_evt.c
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: build

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

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

P1 Badge Consume optional channel index when parsing legacy reports

The legacy advertising parser advances data by sizeof(rpt) + rpt->data_len + 1 before it checks for an optional channel byte. Because the increment on line 509 never includes the extra byte and sizeof(rpt) is just the pointer size, the subsequent conditional can never detect or skip the channel index. When controllers append the channel index (Bluetooth 5.4), that byte remains in the stream and is treated as the header of the next report, producing corrupted results or a length error. The parser needs to account for the extra byte and advance the cursor accordingly.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 668 to 669
ble_gap_rx_ext_adv_report(&desc);

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 👍 / 👎.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 18947bc and 7c7068d.

📒 Files selected for processing (5)
  • src/NimBLEAdvertisedDevice.cpp (2 hunks)
  • src/NimBLEAdvertisedDevice.h (4 hunks)
  • src/NimBLEScan.cpp (1 hunks)
  • src/nimble/nimble/host/include/host/ble_gap.h (2 hunks)
  • src/nimble/nimble/host/src/ble_hs_hci_evt.c (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/NimBLEAdvertisedDevice.h (1)
src/NimBLEAdvertisedDevice.cpp (2)
  • getChannel (831-837)
  • getChannel (831-831)
🔇 Additional comments (13)
src/nimble/nimble/host/include/host/ble_gap.h (2)

407-408: LGTM!

The addition of channel_index to the extended discovery descriptor is well-placed and documented. The 0xFF sentinel value appropriately indicates unavailable channel information.


440-441: LGTM!

Consistent addition of channel_index to the legacy discovery descriptor, maintaining parity with the extended advertising descriptor.

src/NimBLEScan.cpp (1)

133-133: LGTM!

The channel index is properly propagated from the discovery descriptor to the advertised device, placed logically alongside RSSI and payload handling.

src/NimBLEAdvertisedDevice.h (4)

41-43: LGTM!

Clear documentation of the getChannel() method behavior, correctly identifying the BLE advertising channels (37–39) and the unknown sentinel value.


126-126: LGTM!

Public accessor method properly declared.


156-156: LGTM!

Private setter appropriately implemented as an inline assignment.


172-172: LGTM!

Private member variable appropriately declared with correct type.

src/NimBLEAdvertisedDevice.cpp (2)

39-39: LGTM!

Proper initialization of m_channelIndex to the unknown sentinel value (0xFF) in the constructor.


827-837: LGTM!

The channel index mapping is correct. BLE advertising channels (37, 38, 39) are properly derived from controller indices (0, 1, 2), with appropriate handling of unknown values.

src/nimble/nimble/host/src/ble_hs_hci_evt.c (4)

504-504: LGTM!

Appropriate initialization of channel_index to the unknown sentinel value before processing advertising reports.


543-543: LGTM!

Correct initialization for direct advertising reports, which do not contain channel index information in their standard format.


621-621: LGTM!

Proper initialization of channel_index for each extended advertising report before attempting to read the optional field.


659-666: The review comment's analysis of lines 659-666 is incorrect

The original concerns are based on faulty assumptions:

  1. data_len < 255 check is correct: Per Bluetooth specification, when data payload is less than maximum (255 bytes), the extended report format includes an optional channel index byte. The condition properly identifies when space exists for this extra byte.

  2. Bounds check is valid: The condition (const uint8_t*)channel_ptr < (const uint8_t*)data + len correctly validates that the byte is within the event buffer before reading.

  3. Logic is sound: The code properly reads the channel index when available and within bounds.

However, there are critical issues in the same file that require attention:

  • Lines 468, 509, 518 contain sizeof(rpt) bugs (where rpt is a pointer). These should be sizeof(*rpt) to get the struct size, not the pointer size. This causes severe buffer offset miscalculations when parsing multiple reports.

Likely an incorrect or invalid review comment.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/nimble/nimble/host/src/ble_hs_hci_evt.c (2)

509-509: Critical: sizeof pointer vs sizeof struct

Line 509 uses sizeof(rpt) which returns the size of the pointer (typically 4 or 8 bytes), not the struct adv_report. This causes incorrect pointer arithmetic when advancing to the next report.

Apply this diff:

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

659-670: Critical: Pointer advancement doesn't account for consumed channel byte.

After reading desc.channel_index from *channel_ptr (line 664), the loop advances to the next report at line 670 using only report->data_len. When the channel index byte is present and consumed, this leaves that byte positioned as the start of the next report, corrupting all subsequent report parsing.

Apply this diff to advance past the channel byte when it was read:

+        uint8_t extra_bytes = 0;
         /* 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;
+                extra_bytes = 1;
             }
         }
 
         ble_gap_rx_ext_adv_report(&desc);
 
-        report = (const void *) &report->data[report->data_len];
+        report = (const void *) &report->data[report->data_len + extra_bytes];
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7c7068d and 9a828ac.

📒 Files selected for processing (3)
  • .github/workflows/platformio-examples.yml (1 hunks)
  • examples/NimBLE_Scan_Continuous/NimBLE_Scan_Continuous.ino (2 hunks)
  • src/nimble/nimble/host/src/ble_hs_hci_evt.c (5 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: build
🔇 Additional comments (5)
examples/NimBLE_Scan_Continuous/NimBLE_Scan_Continuous.ino (1)

20-27: LGTM! Clear demonstration of channel reporting.

The callback correctly uses getChannel() and handles both known channels (printing the channel number) and unknown channels (0xFF sentinel) appropriately. This provides a good example for users of the new channel awareness feature.

src/nimble/nimble/host/src/ble_hs_hci_evt.c (3)

504-504: LGTM! Proper default initialization.

Initializing channel_index to 0xFF (unknown) is the correct default before attempting to read the optional channel byte.


543-543: LGTM! Proper default for direct advertising reports.

Direct advertising reports don't include channel index data, so defaulting to 0xFF is appropriate.


621-621: LGTM! Proper default initialization for extended reports.

Initializing channel_index to 0xFF before attempting to read the optional channel byte is correct.

.github/workflows/platformio-examples.yml (1)

1-63: LGTM! Well-structured CI workflow.

The workflow properly:

  • Triggers on relevant file changes (src, examples, library properties)
  • Caches PlatformIO for faster builds
  • Iterates through all Arduino examples with proper error handling
  • Uses appropriate error checking (set -euo pipefail, zero-example detection)

This ensures that examples like NimBLE_Scan_Continuous.ino (which now uses the new channel reporting features) are validated in CI.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants