C++ Pixels Library For Windows
Enable communications with Pixels dice using Bluetooth Low Energy.
Loading...
Searching...
No Matches
Scanner.h
Go to the documentation of this file.
1
6#pragma once
7
8#include "BleTypes.h"
9#include "ScannedPeripheral.h"
10
12{
20 class Scanner final
21 {
22 using BluetoothLEAdvertisementWatcher = winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementWatcher;
23 using BluetoothLEAdvertisementReceivedEventArgs = winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementReceivedEventArgs;
24 using BluetoothLEAdvertisementWatcherStoppedEventArgs = winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementWatcherStoppedEventArgs;
25
26 // Watcher and its event tokens
27 BluetoothLEAdvertisementWatcher _watcher{ nullptr };
28 winrt::event_token _receivedToken{};
29 winrt::event_token _stoppedToken{};
30
31 // List of user required services
32 std::vector<winrt::guid> _requestedServices{};
33
34 // Discovered peripherals
35 std::mutex _peripheralsMtx{};
36 std::map<bluetooth_address_t, std::shared_ptr<const ScannedPeripheral>> _peripherals{};
37
38 // User callback for discovered peripherals
39 std::function<void(std::shared_ptr<const ScannedPeripheral>)> _onPeripheralDiscovered{};
40
41 public:
53 std::function<void(std::shared_ptr<const ScannedPeripheral>)> peripheralDiscovered,
54 std::vector<winrt::guid> services = std::vector<winrt::guid>{})
55 :
56 _watcher{},
57 _requestedServices{ services },
58 _onPeripheralDiscovered{ peripheralDiscovered }
59 {
60 using namespace winrt::Windows::Devices::Bluetooth::Advertisement;
61
62 // We want to receive all advertisement packets
63 _watcher.AllowExtendedAdvertisements(true);
64
65 // Send scan requests packets
66 _watcher.ScanningMode(BluetoothLEScanningMode::Active);
67
68 // We must subscribe to both Received and Stopped for the watcher to work
69 _receivedToken = _watcher.Received({ this, &Scanner::onReceived });
70 _stoppedToken = _watcher.Stopped({ this, &Scanner::onStopped });
71
72 // Starts scanning
73 _watcher.Start();
74 }
75
83 void copyDiscoveredPeripherals(std::vector<std::shared_ptr<const ScannedPeripheral>>& outDiscoveredPeripherals)
84 {
85 std::lock_guard lock{ _peripheralsMtx };
86 outDiscoveredPeripherals.reserve(outDiscoveredPeripherals.size() + _peripherals.size());
87 for (auto& [_, p] : _peripherals)
88 {
89 outDiscoveredPeripherals.emplace_back(p);
90 }
91 }
92
97 {
98 if (_watcher)
99 {
100 _watcher.Received(_receivedToken);
101 _watcher.Stopped(_stoppedToken);
102 _watcher.Stop();
103 _watcher = nullptr;
104 }
105 }
106
107 private:
108 // Called by the watcher for each received advertisement packet
109 void onReceived(
110 BluetoothLEAdvertisementWatcher const& /*watcher*/,
111 BluetoothLEAdvertisementReceivedEventArgs const& args)
112 {
113 using namespace winrt::Windows::Devices::Bluetooth::Advertisement;
114
115 std::wstring name{};
116 std::vector<winrt::guid> services{};
117 std::vector<ManufacturerData> manufacturersData{};
118 std::vector<ServiceData> servicesData{};
119 std::vector<AdvertisementData> advertisingData{};
120
121 auto advertisement = args.Advertisement();
122 if (advertisement)
123 {
124 // Get name
125 name = advertisement.LocalName();
126
127 // Get services
128 auto serv = advertisement.ServiceUuids();
129 if (serv)
130 {
131 services.reserve(serv.Size());
132 for (const auto& uuid : serv)
133 {
134 services.push_back(uuid);
135 }
136 }
137
138 // Get manufacturer-specific data sections
139 auto manufDataList = advertisement.ManufacturerData();
140 if (manufDataList)
141 {
142 manufacturersData.reserve(manufDataList.Size());
143 for (const auto& manuf : manufDataList)
144 {
145 manufacturersData.emplace_back(ManufacturerData{ manuf.CompanyId(), manuf.Data() });
146 }
147 }
148
149 // Get raw data sections
150 auto advDataList = advertisement.DataSections();
151 if (advDataList)
152 {
153 advertisingData.reserve(advDataList.Size());
154 for (const auto& adv : advDataList)
155 {
156 advertisingData.emplace_back(AdvertisementData{ adv.DataType(), adv.Data() });
157
158 // Check if it's a service data
159 if (adv.DataType() == 0x16) // Service Data - 16-bit UUID
160 {
161 servicesData.emplace_back(ServiceData{ adv.Data() });
162 }
163 }
164 }
165 }
166
167 // Get TX power
168 int txPower = 0;
169 if (args.TransmitPowerLevelInDBm())
170 {
171 txPower = args.TransmitPowerLevelInDBm().Value();
172 }
173
174 switch (args.AdvertisementType())
175 {
176 case BluetoothLEAdvertisementType::ConnectableUndirected:
177 case BluetoothLEAdvertisementType::ConnectableDirected:
178 case BluetoothLEAdvertisementType::ScannableUndirected:
179 case BluetoothLEAdvertisementType::NonConnectableUndirected:
180 {
181 // We got a fresh advertisement packet, create a new DiscoveredPeripheral
182 std::shared_ptr<const ScannedPeripheral> peripheral{
183 new ScannedPeripheral(
184 args.Timestamp(),
185 args.BluetoothAddress(),
186 name,
187 args.IsConnectable(),
188 args.RawSignalStrengthInDBm(),
189 txPower,
190 services,
191 manufacturersData,
192 servicesData,
193 advertisingData) };
194
195 {
196 std::lock_guard lock{ _peripheralsMtx };
197 _peripherals[peripheral->address()] = peripheral;
198 }
199 notify(peripheral);
200 }
201 break;
202
203 case BluetoothLEAdvertisementType::ScanResponse:
204 {
205 std::shared_ptr<const ScannedPeripheral> updatedPeripheral{};
206 {
207 std::lock_guard lock{ _peripheralsMtx };
208 auto it = _peripherals.find(args.BluetoothAddress());
209 if (it != _peripherals.end())
210 {
211 // We got an advertisement packet in response to a scan request send after receiving
212 // an initial advertisement packet, update the existing DiscoveredPeripheral
213 updatedPeripheral.reset(
214 new ScannedPeripheral(
215 args.Timestamp(),
216 *it->second,
217 name,
218 args.RawSignalStrengthInDBm(),
219 txPower,
220 services,
221 manufacturersData,
222 servicesData,
223 advertisingData));
224
225 _peripherals[updatedPeripheral->address()] = updatedPeripheral;
226 }
227 }
228 if (updatedPeripheral)
229 {
230 notify(updatedPeripheral);
231 }
232 }
233 break;
234 }
235 }
236
237 // Notify user code if peripheral advertise required services
238 void notify(const std::shared_ptr<const ScannedPeripheral>& peripheral)
239 {
240 if (_onPeripheralDiscovered &&
241 (_requestedServices.empty() || Internal::isSubset(_requestedServices, peripheral->services())))
242 {
243 _onPeripheralDiscovered(peripheral);
244 }
245 }
246
247 // Called by the watcher when scanning is stopped
248 void onStopped(
249 BluetoothLEAdvertisementWatcher const& /*watcher*/,
250 BluetoothLEAdvertisementWatcherStoppedEventArgs const& /*args*/)
251 {
252 }
253 };
254}
Common types used across the Systemic::BluetoothLE namespace.
Definition of the ManufacturerData, AdvertisementData and DiscoveredPeripheral classes.
Implements scanning of Bluetooth Low Energy (BLE) peripherals. It stores and notifies of discovered p...
Definition: Scanner.h:21
~Scanner()
Stops the scan and destroys the Scanner instance.
Definition: Scanner.h:96
Scanner(std::function< void(std::shared_ptr< const ScannedPeripheral >)> peripheralDiscovered, std::vector< winrt::guid > services=std::vector< winrt::guid >{})
Initializes a new instance of Scanner and immediately starts scanning for BLE peripherals.
Definition: Scanner.h:52
void copyDiscoveredPeripherals(std::vector< std::shared_ptr< const ScannedPeripheral > > &outDiscoveredPeripherals)
Copy the discovered peripherals to the given std::vector.
Definition: Scanner.h:83
A collection of C++ classes that provides a simplified access to Bluetooth Low Energy peripherals.
Definition: BleTypes.h:38