Skip to content

Commit

Permalink
Support active BLE scans.
Browse files Browse the repository at this point in the history
Active scans send a request to the device when they have received an
advertisement.
  • Loading branch information
floitsch committed Nov 24, 2024
1 parent efeb21c commit e59206a
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 23 deletions.
2 changes: 1 addition & 1 deletion lib/ble/ble.toit
Original file line number Diff line number Diff line change
Expand Up @@ -1084,7 +1084,7 @@ ble-create-central-manager_ adapter-resource:
ble-create-peripheral-manager_ adapter-resource bonding secure-connections:
#primitive.ble.create-peripheral-manager

ble-scan-start_ central-manager duration-us:
ble-scan-start_ central-manager passive duration-us:
#primitive.ble.scan-start

ble-scan-next_ central-manager:
Expand Down
9 changes: 7 additions & 2 deletions lib/ble/remote.toit
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,19 @@ class Central extends Resource_:
Only one scan can run at a time.
If $active is true, then we request a scan response from discovered devices.
Users might need to merge the advertisement data from the scan response with the
advertisement data from the discovery event. Use
$RemoteScannedDevice.is-scan-response to distinguish between the two.
Connections cannot be established while a scan is ongoing.
Stops the scan after the given $duration.
*/
scan [block] --duration/Duration?=null:
scan [block] --duration/Duration?=null --active/bool=false:
duration-us := duration ? (max 0 duration.in-us) : -1
resource-state_.clear-state COMPLETED-EVENT_
ble-scan-start_ resource_ duration-us
ble-scan-start_ resource_ (not active) duration-us
is-macos := system.platform == system.PLATFORM_MACOS
try:
while true:
Expand Down
2 changes: 1 addition & 1 deletion src/primitive.h
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ namespace toit {
PRIMITIVE(create_central_manager, 1) \
PRIMITIVE(close, 1) \
PRIMITIVE(release_resource, 1) \
PRIMITIVE(scan_start, 2) \
PRIMITIVE(scan_start, 3) \
PRIMITIVE(scan_next, 1) \
PRIMITIVE(scan_stop, 1) \
PRIMITIVE(connect, 3) \
Expand Down
2 changes: 1 addition & 1 deletion src/resources/ble_darwin.mm
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,7 @@ - (id)initWithResource:(toit::BleResource*)resource {
}

PRIMITIVE(scan_start) {
ARGS(BleCentralManagerResource, central_manager, int64, duration_us);
ARGS(BleCentralManagerResource, central_manager, bool, passive, int64, duration_us);

Locker locker(central_manager->scan_mutex());
bool active = [central_manager->central_manager() isScanning];
Expand Down
8 changes: 2 additions & 6 deletions src/resources/ble_esp32.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2417,7 +2417,7 @@ PRIMITIVE(close) {
}

PRIMITIVE(scan_start) {
ARGS(BleCentralManagerResource, central_manager, int64, duration_us)
ARGS(BleCentralManagerResource, central_manager, bool, passive, int64, duration_us)

Locker locker(central_manager->group()->mutex());

Expand All @@ -2440,11 +2440,7 @@ PRIMITIVE(scan_start) {
// repeated advertisements from the same device.
// disc_params.filter_duplicates = 1;

/**
* Perform a passive scan. I.e., don't send follow-up scan requests to
* each advertiser.
*/
disc_params.passive = 1;
disc_params.passive = passive ? 1 : 0;

/* Use defaults for the rest of the parameters. */
disc_params.itvl = 0;
Expand Down
67 changes: 55 additions & 12 deletions tests/hw/esp32/ble6-advertise-shared.toit
Original file line number Diff line number Diff line change
Expand Up @@ -62,24 +62,40 @@ main-peripheral:
next-semaphore.down
peripheral.stop-advertise

advertise := : | blocks |
advertise := : | blocks scan-response-blocks |
advertisement = AdvertisementData blocks
peripheral.start-advertise --allow-connections advertisement
scan-response := scan-response-blocks
? AdvertisementData scan-response-blocks
: null
peripheral.start-advertise --allow-connections advertisement --scan-response=scan-response
next-semaphore.down
peripheral.stop-advertise

advertise.call []
advertise.call [] null
advertise.call [
DataBlock.flags --general-discovery --bredr-supported=false,
DataBlock.manufacturer-specific --company-id=COMPANY-ID MANUFACTURER-DATA,
]
DataBlock.flags --general-discovery --bredr-supported=false,
DataBlock.manufacturer-specific --company-id=COMPANY-ID MANUFACTURER-DATA,
]
null
advertise.call [
DataBlock.name "Test",
DataBlock.services-128 [TEST-SERVICE],
]
DataBlock.name "Test",
DataBlock.services-128 [TEST-SERVICE],
]
null
advertise.call [
DataBlock.manufacturer-specific (ByteArray 27: it)
]
DataBlock.manufacturer-specific (ByteArray 27: it)
]
null
advertise.call
[
DataBlock.name "Test",
]
[ // The scan response.
DataBlock.services-128 [TEST-SERVICE],
]




done = true

Expand All @@ -100,8 +116,10 @@ test-data
address/any
characteristic/RemoteCharacteristic
--central/Central
--is-connectable/bool=true [block]:
--is-connectable/bool=true
[block]:
remote-scanned := scan address --central=central
expect-not remote-scanned.is-scan-response
expect-equals address remote-scanned.address
expect-equals is-connectable remote-scanned.is-connectable
block.call remote-scanned.data
Expand Down Expand Up @@ -162,6 +180,31 @@ main-central:
expect-equals (ByteArray 27: it) data
id)

// Check that active/passive scanning works.
central.scan --duration=(Duration --s=2): | device/RemoteScannedDevice |
if device.address == address:
// Without doing an active scan we don't get a scan response.
expect-not device.is-scan-response

advertisement/AdvertisementData? := null
scan-response/AdvertisementData? := null
while true: // Use loop to be able to break out of the block.
central.scan --duration=(Duration --s=3) --active: | device/RemoteScannedDevice |
if device.address == address:
if device.is-scan-response:
scan-response = device.data
else:
advertisement = device.data
if advertisement and scan-response: break
break

expect-equals 1 advertisement.data-blocks.size
expect-equals "Test" advertisement.name
expect-equals 1 scan-response.data-blocks.size
expect-equals [TEST-SERVICE] scan-response.services

characteristic.write #[central-test-counter++]

remote-device.close
central.close
adapter.close
Expand Down

0 comments on commit e59206a

Please sign in to comment.