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