C++ Pixels Library For Windows
Enable communications with Pixels dice using Bluetooth Low Energy.
Loading...
Searching...
No Matches
Peripheral.h
Go to the documentation of this file.
1
6#pragma once
7
8#include "BleTypes.h"
9
11{
12 class Service;
13
15 enum class ConnectionEvent
16 {
19
22
25
27 Ready,
28
31
34 };
35
38 {
40 Unknown = -1,
41
43 Success = 0,
44
47
50
52 Timeout,
53
56
59
62 };
63
85 class Peripheral : public std::enable_shared_from_this<Peripheral>
86 {
87 using BluetoothLEDevice = winrt::Windows::Devices::Bluetooth::BluetoothLEDevice;
88 using GattSession = winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattSession;
89
90 // The Bluetooth address
91 const bluetooth_address_t _address{};
92
93 // The user callback for connection events
94 const std::function<void(ConnectionEvent, ConnectionEventReason)> _onConnectionEvent{};
95
96 // Device and session
97 BluetoothLEDevice _device{ nullptr };
98 GattSession _session{ nullptr };
99 winrt::event_token _connectionStatusChangedToken{};
100
101 // Services
102 std::unordered_map<winrt::guid, std::shared_ptr<Service>> _services{};
103
104 // The ready state
105 volatile bool _isReady{};
106
107 // Connection
108 mutable std::recursive_mutex _connectOpMtx{}; // Connection mutex
109 volatile size_t _connectCounter{}; // Incremented every time connect or disconnect is called
110 volatile bool _connecting{}; // Whether we are trying to connect
111
112 // Queue of connection events to notify to user
113 std::vector<std::tuple<ConnectionEvent, ConnectionEventReason>> _connectionEventsQueue{};
114
115 Peripheral(bluetooth_address_t bluetoothAddress, const std::function<void(ConnectionEvent, ConnectionEventReason)>& onConnectionEvent)
116 : _address{ bluetoothAddress }, _onConnectionEvent{ onConnectionEvent }
117 {
118 assert(bluetoothAddress); // TODO check arguments
119 assert(onConnectionEvent);
120 _connectionEventsQueue.reserve(16);
121 }
122
123 public:
126
135 static std::shared_ptr<Peripheral> create(
136 bluetooth_address_t bluetoothAddress,
137 const std::function<void(ConnectionEvent, ConnectionEventReason)>& onConnectionEvent)
138 {
139 return std::shared_ptr<Peripheral>{ new Peripheral(bluetoothAddress, onConnectionEvent) };
140 }
141
146 {
147 disconnect();
148 }
149
153
165 std::future<BleRequestStatus> connectAsync(
166 std::vector<winrt::guid> requiredServices = std::vector<winrt::guid>{},
167 bool maintainConnection = false);
168
175 {
176 internalDisconnect(ConnectionEventReason::Success);
177 notifyQueuedConnectionEvents(); // TODO not getting those events!
178 }
179
184
191 {
192 return _address;
193 }
194
202 bool isConnected() const
203 {
204 using namespace winrt::Windows::Devices::Bluetooth;
205
206 auto dev = safeGetDevice();
207 return dev ? (dev.ConnectionStatus() == BluetoothConnectionStatus::Connected) : false;
208 }
209
218 bool isReady() const
219 {
220 return _isReady;
221 }
222
227
233 const winrt::hstring deviceId() const
234 {
235 auto dev = safeGetDevice();
236 return dev ? dev.DeviceId() : winrt::hstring{};
237 }
238
244 const winrt::hstring name() const
245 {
246 auto dev = safeGetDevice();
247 return dev ? dev.Name() : winrt::hstring{};
248 }
249
255 uint16_t mtu() const
256 {
257 // TODO is the lock needed?
258 std::lock_guard lock{ _connectOpMtx };
259 return _session ? _session.MaxPduSize() : 0;
260 }
261
266
273 std::shared_ptr<Service> getDiscoveredService(const winrt::guid& uuid) const
274 {
275 std::lock_guard lock{ _connectOpMtx };
276 auto it = _services.find(uuid);
277 return it != _services.end() ? it->second : nullptr;
278 }
279
285 void copyDiscoveredServices(std::vector<std::shared_ptr<Service>>& outServices) const
286 {
287 std::lock_guard lock{ _connectOpMtx };
288 outServices.reserve(outServices.size() + _services.size());
289 for (auto& [_, s] : _services)
290 {
291 outServices.emplace_back(s);
292 }
293 }
294
296
297 private:
298 // Get the device object in a thread safe manner
299 BluetoothLEDevice safeGetDevice() const
300 {
301 // TODO is the lock needed?
302 std::lock_guard lock{ _connectOpMtx };
303 return _device;
304 }
305
306 // Take the lock and release device and session, be sure to call notifyQueuedConnectionEvents() afterwards
307 void internalDisconnect(ConnectionEventReason reason, bool fromDevice = false);
308
309 // Notify user code with pending connection event
310 void notifyQueuedConnectionEvents()
311 {
312 // TODO optimize for case with 1 or 2 elements in queue
313 decltype(_connectionEventsQueue) queue{};
314 {
315 std::lock_guard lock{ _connectOpMtx };
316 queue = _connectionEventsQueue;
317 _connectionEventsQueue.clear();
318 }
319 if (_onConnectionEvent)
320 {
321 for (auto& [ev, reason] : queue)
322 {
323 _onConnectionEvent(ev, reason);
324 }
325 }
326 }
327
328 // Called by the device when the connection status changes
329 void onDeviceConnectionStatusChanged(BluetoothLEDevice device, winrt::Windows::Foundation::IInspectable _)
330 {
331 using namespace winrt::Windows::Devices::Bluetooth;
332
333 if (device.ConnectionStatus() == BluetoothConnectionStatus::Disconnected)
334 {
335 bool linkLoss = (_session != nullptr) && _session.MaintainConnection();
336 internalDisconnect(linkLoss ? ConnectionEventReason::LinkLoss : ConnectionEventReason::Timeout, true);
337 notifyQueuedConnectionEvents();
338 }
339 else
340 {
341 // Connected event is raised in connectAsync() after it has successfully retrieved the services
342 }
343 }
344 };
345}
Common types used across the Systemic::BluetoothLE namespace.
Represents a Bluetooth Low Energy (BLE) peripheral.
Definition: Peripheral.h:86
const winrt::hstring name() const
Gets the name of the peripheral.
Definition: Peripheral.h:244
bluetooth_address_t address() const
Gets the Bluetooth address of the peripheral.
Definition: Peripheral.h:190
const winrt::hstring deviceId() const
Gets the device id assigned by the system for the peripheral.
Definition: Peripheral.h:233
std::shared_ptr< Service > getDiscoveredService(const winrt::guid &uuid) const
Gets the Service instance with the given UUID.
Definition: Peripheral.h:273
void copyDiscoveredServices(std::vector< std::shared_ptr< Service > > &outServices) const
Copy the discovered services to the given std::vector.
Definition: Peripheral.h:285
bool isConnected() const
Indicates whether the peripheral is connected.
Definition: Peripheral.h:202
static std::shared_ptr< Peripheral > create(bluetooth_address_t bluetoothAddress, const std::function< void(ConnectionEvent, ConnectionEventReason)> &onConnectionEvent)
Initializes a new instance of Peripheral with the given Bluetooth address and a callback for notifyin...
Definition: Peripheral.h:135
std::future< BleRequestStatus > connectAsync(std::vector< winrt::guid > requiredServices=std::vector< winrt::guid >{}, bool maintainConnection=false)
Connects to the BLE peripheral.
uint16_t mtu() const
Gets the Maximum Transmission Unit (MTU).
Definition: Peripheral.h:255
void disconnect()
Immediately disconnects the peripheral.
Definition: Peripheral.h:174
bool isReady() const
Indicates whether the peripheral is ready.
Definition: Peripheral.h:218
~Peripheral()
Disconnects and destroys the Peripheral instance.
Definition: Peripheral.h:145
A collection of C++ classes that provides a simplified access to Bluetooth Low Energy peripherals.
Definition: BleTypes.h:38
std::uint64_t bluetooth_address_t
Type for a Bluetooth address.
Definition: BleTypes.h:40
@ Canceled
The request was canceled.
@ Success
The request completed successfully.
@ NotSupported
The request failed because of the operation is not supported by the peripheral.
@ Timeout
The request did not succeed after the timeout period.
@ Disconnected
The request was aborted because the peripheral got disconnected.
ConnectionEvent
Peripheral connection events.
Definition: Peripheral.h:16
@ Disconnecting
Raised at the beginning of a user initiated disconnect.
@ Connected
Raised once the peripheral is connected, just before services are being discovered.
@ Connecting
Raised at the beginning of the connect sequence and is followed either by Connected or FailedToConnec...
@ Ready
Raised after a Connected event, once the required services have been discovered.
@ FailedToConnect
Raised when the peripheral fails to connect, the reason of failure is also given.
ConnectionEventReason
Peripheral connection event reasons.
Definition: Peripheral.h:38
@ LinkLoss
Peripheral was disconnected while in "auto connect" mode.
@ Success
The disconnect was initiated by user.
@ Unknown
The disconnect happened for an unknown reason.
@ Timeout
Peripheral didn't responded in time.
@ AdpaterOff
The local device Bluetooth adapter is off.