Pixels Plugin for Unity
Enable communications with Pixels dice using Bluetooth Low Energy.
Loading...
Searching...
No Matches
UnityBridge.h
Go to the documentation of this file.
1
8#import "SGBleTypes.h"
9#import "SGBleUtils.h"
10
11#include <cstdint>
12#include <cstring>
13#include <limits>
14
17using peripheral_id_t = const char *;
18
20using request_index_t = std::uint32_t;
21
23using characteristic_index_t = std::uint32_t;
24
26using characteristic_property_t = std::uint64_t;
27
29typedef void (*BluetoothStateUpdateCallback)(int state);
30
32typedef void (*DiscoveredPeripheralCallback)(const char *advertisementDataJson);
33
35typedef void (*RequestStatusCallback)(request_index_t requestIndex, int errorCode);
36
38typedef void (*PeripheralConnectionEventCallback)(request_index_t requestIndex, peripheral_id_t peripheralId, int connectionEvent, int reason);
39
41typedef void (*RssiReadCallback)(request_index_t requestIndex, int rssi, int errorCode);
42
44typedef void (*ValueReadCallback)(request_index_t requestIndex, const void *data, size_t length, int errorCode);
45
46namespace internal
47{
48
49typedef void (^CompletionHandler)(NSError *error);
50typedef void (^ValueReadHandler)(SGBlePeripheralQueue *peripheral, CBCharacteristic *characteristic, NSError *error);
51
52static const int otherErrorsMask = 0x80000000;
53static const int unexpectedError = otherErrorsMask;
54static const int invalidPeripheralIdErrorCode = otherErrorsMask | 1;
55
56inline int toErrorCode(NSError *error)
57{
58 if (!error)
59 {
60 return 0;
61 }
62 else if (error.domain == CBErrorDomain)
63 {
64 // CoreBluetooth error (zero is CBErrorUnknown)
65 return -1 - (int)error.code;
66 }
67 else if (error.domain == CBATTErrorDomain)
68 {
69 // Protocol error (zero is success)
70 return (int)error.code;
71 }
72 else if (error.domain == sgBleGetErrorDomain())
73 {
74 // One of our own error
75 return otherErrorsMask | (0x100 + (int)error.code);
76 }
77 else
78 {
79 // Any other error
80 return unexpectedError;
81 }
82}
83
84inline CompletionHandler toCompletionHandler(RequestStatusCallback onRequestStatus, request_index_t requestIndex)
85{
86 return ^(NSError *error) {
87 if (onRequestStatus)
88 onRequestStatus(requestIndex, toErrorCode(error));
89 };
90}
91
92inline ValueReadHandler toValueReadHandler(ValueReadCallback onValueRead, request_index_t requestIndex)
93{
94 ValueReadHandler handler = nil;
95 if (onValueRead)
96 {
97 handler = ^(SGBlePeripheralQueue *peripheral, CBCharacteristic *characteristic, NSError *error) {
98 NSData *data = characteristic.value;
99 onValueRead(requestIndex, data.bytes, data.length, toErrorCode(error));
100 };
101 }
102 return handler;
103}
104
105// Convert c-string to array of CBUUID
106inline NSArray<CBUUID *> *toCBUUIDArray(const char *serviceUuids)
107{
108 NSMutableArray<CBUUID *> *arr = nil;
109 if (serviceUuids)
110 {
111 NSArray<NSString *> *servicesList = [[NSString stringWithUTF8String:serviceUuids] componentsSeparatedByString:@","];
112 if (servicesList.count > 0)
113 {
114 arr = [NSMutableArray<CBUUID *> arrayWithCapacity:servicesList.count];
115 for (NSString *uuidStr in servicesList)
116 {
117 CBUUID *uuid = [CBUUID UUIDWithString:uuidStr];
118 if (uuid != nil)
119 {
120 [arr addObject:uuid];
121 }
122 //else TODO error
123 }
124 }
125 }
126 return arr;
127}
128
129inline NSString *toUuidsString(NSArray<CBAttribute *> *attributes)
130{
131 NSMutableString *uuids = [[NSMutableString alloc] initWithCapacity:36 * attributes.count]; // A UUID has 36 characters including the dashes
132 for (CBService *attr in attributes)
133 {
134 if (uuids.length > 0)
135 {
136 [uuids appendString:@","];
137 }
138 [uuids appendString:attr.UUID.UUIDString.lowercaseString];
139 }
140 return uuids;
141}
142
143inline const char *allocateCStr(NSString *str)
144{
145 char *cStr = NULL;
146 if (str)
147 {
148 const char *utf8CStr = [str UTF8String];
149 cStr = (char *)malloc(strlen(utf8CStr) + 1);
150 std::strcpy(cStr, utf8CStr);
151 }
152 return cStr;
153}
154
155inline NSString *toJsonStr(CBUUID *uuid)
156{
157 return uuid.UUIDString.lowercaseString;
158}
159
160inline void appendToJsonStr(NSMutableString *jsonStr, NSArray<CBUUID *> *uuids)
161{
162 [jsonStr appendString:@"["];
163 NSUInteger len = uuids.count;
164 for (NSUInteger i = 0; i < len; i++)
165 {
166 if (i)
167 {
168 [jsonStr appendString:@","];
169 }
170 [jsonStr appendFormat:@"\"%@\"", toJsonStr(uuids[i])];
171 }
172 [jsonStr appendString:@"]"];
173}
174
175inline void appendToJsonStr(NSMutableString *jsonStr,
176 std::uint8_t *bytes,
177 NSUInteger start,
178 NSUInteger end)
179{
180 [jsonStr appendString:@"["];
181 for (NSUInteger i = start; i < end; i++)
182 {
183 if (i > start)
184 {
185 [jsonStr appendString:@","];
186 }
187 [jsonStr appendFormat:@"%d", bytes[i]];
188 }
189 [jsonStr appendString:@"]"];
190}
191
192
193inline void appendToJsonStr(NSMutableString *jsonStr, NSData *data)
194{
195 std::uint8_t *bytes = (std::uint8_t *)data.bytes;
196 appendToJsonStr(jsonStr, bytes, 0, data.length);
197}
198
199inline NSString *advertisementDataToJsonString(const char *systemId, NSDictionary<NSString *, id> *advertisementData, NSNumber *RSSI)
200{
201 // Get the different bits of advertising data
202 NSData *manufacturerData = advertisementData[CBAdvertisementDataManufacturerDataKey];
203 NSString *localName = advertisementData[CBAdvertisementDataLocalNameKey];
204 NSDictionary<CBUUID *, NSData *> *servicesData = advertisementData[CBAdvertisementDataServiceDataKey];
205 NSArray<CBUUID *> *serviceUUIDs = advertisementData[CBAdvertisementDataServiceUUIDsKey];
206 NSArray<CBUUID *> *overflowServiceUUIDs = advertisementData[CBAdvertisementDataOverflowServiceUUIDsKey];
207 NSNumber *txPowerLevel = advertisementData[CBAdvertisementDataTxPowerLevelKey];
208 NSNumber *isConnectable = advertisementData[CBAdvertisementDataIsConnectable];
209 NSArray<CBUUID *> *solicitedServiceUUIDs = advertisementData[CBAdvertisementDataSolicitedServiceUUIDsKey];
210
211 NSMutableString *jsonStr = [NSMutableString new];
212 [jsonStr appendFormat:@"{\"systemId\":\"%s\",", systemId];
213 if (manufacturerData && manufacturerData.length >= 2)
214 {
215 // Only one manufacturer
216 [jsonStr appendString:@"\"manufacturersData\":["];
217 std::uint8_t *bytes = (std::uint8_t *)manufacturerData.bytes;
218 uint16_t companyId = bytes[1] | ((uint16_t)bytes[0] << 8);
219 [jsonStr appendFormat:@"{\"companyId\":%d,", companyId];
220 [jsonStr appendString:@"\"data\":"];
221 appendToJsonStr(jsonStr, bytes, 2, manufacturerData.length);
222 [jsonStr appendString:@"}],"];
223 }
224 if (localName)
225 {
226 [jsonStr appendFormat:@"\"name\":\"%@\",", localName];
227 }
228 if (isConnectable.boolValue)
229 {
230 [jsonStr appendString:@"\"isConnectable\":true,"];
231 }
232 if (servicesData && servicesData.count)
233 {
234 [jsonStr appendString:@"\"servicesData\":["];
235 bool first = true;
236 // Iterate services
237 for (CBUUID *uuid in servicesData)
238 {
239 if (!first)
240 {
241 [jsonStr appendString:@","];
242 }
243 first = false;
244 [jsonStr appendFormat:@"{\"uuid\":\"%@\",", toJsonStr(uuid)];
245 [jsonStr appendString:@"\"data\":"];
246 appendToJsonStr(jsonStr, [servicesData objectForKey:uuid]);
247 [jsonStr appendString:@"}"];
248 }
249 [jsonStr appendString:@"],"];
250 }
251 if (serviceUUIDs && serviceUUIDs.count)
252 {
253 [jsonStr appendString:@"\"services\":"];
254 appendToJsonStr(jsonStr, serviceUUIDs);
255 [jsonStr appendString:@","];
256 }
257 if (overflowServiceUUIDs && overflowServiceUUIDs.count)
258 {
259 [jsonStr appendString:@"\"overflowServices\":"];
260 appendToJsonStr(jsonStr, overflowServiceUUIDs);
261 [jsonStr appendString:@","];
262 }
263 if (solicitedServiceUUIDs && solicitedServiceUUIDs.count)
264 {
265 [jsonStr appendString:@"\"solicitedServices\":"];
266 appendToJsonStr(jsonStr, solicitedServiceUUIDs);
267 [jsonStr appendString:@","];
268 }
269 if (txPowerLevel)
270 {
271 [jsonStr appendFormat:@"\"txPowerLevel\":%@,", txPowerLevel];
272 }
273 [jsonStr appendFormat:@"\"rssi\":%@", RSSI];
274 [jsonStr appendString:@"}"];
275 return jsonStr;
276}
277
278SGBleCentralManagerDelegate *getCentral();
279
280NSMutableDictionary<CBPeripheral *, SGBlePeripheralQueue *> *getPeripherals();
281
282inline const char *getPeripheralId(CBPeripheral *peripheral)
283{
284 return [[peripheral.identifier UUIDString] UTF8String];
285}
286
287inline CBPeripheral *getCBPeripheral(peripheral_id_t peripheralId)
288{
289 CBPeripheral *peripheral = nil;
290 if (peripheralId)
291 {
292 NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:[NSString stringWithUTF8String:peripheralId]];
293 peripheral = [getCentral() peripheralForIdentifier:uuid];
294 }
295 return peripheral;
296}
297
298inline const char *getPeripheralId(SGBlePeripheralQueue *peripheral)
299{
300 return [[peripheral.peripheral.identifier UUIDString] UTF8String];
301}
302
303inline SGBlePeripheralQueue *getSGBlePeripheralQueue(peripheral_id_t peripheralId)
304{
305 return [getPeripherals() objectForKey:getCBPeripheral(peripheralId)];
306}
307
308inline SGBlePeripheralQueue *getSGBlePeripheralQueue(peripheral_id_t peripheralId, RequestStatusCallback onRequestStatus, request_index_t requestIndex)
309{
310 SGBlePeripheralQueue *sgPeripheral = getSGBlePeripheralQueue(peripheralId);
311 if (!sgPeripheral && onRequestStatus)
312 {
313 onRequestStatus(requestIndex, invalidPeripheralIdErrorCode);
314 }
315 return sgPeripheral;
316}
317
318inline SGBlePeripheralQueue *getSGBlePeripheralQueue(peripheral_id_t peripheralId, RssiReadCallback onRssiRead, request_index_t requestIndex)
319{
320 SGBlePeripheralQueue *sgPeripheral = getSGBlePeripheralQueue(peripheralId);
321 if (!sgPeripheral && onRssiRead)
322 {
323 onRssiRead(std::numeric_limits<int>::min(), requestIndex, invalidPeripheralIdErrorCode);
324 }
325 return sgPeripheral;
326}
327
328inline SGBlePeripheralQueue *getSGBlePeripheralQueue(peripheral_id_t peripheralId, ValueReadCallback onValueRead, request_index_t requestIndex)
329{
330 SGBlePeripheralQueue *sgPeripheral = getSGBlePeripheralQueue(peripheralId);
331 if (!sgPeripheral && onValueRead)
332 {
333 onValueRead(requestIndex, nullptr, 0, invalidPeripheralIdErrorCode);
334 }
335 return sgPeripheral;
336}
337
338inline CBService *getService(peripheral_id_t peripheralId, const char *serviceUuidStr)
339{
340 if (peripheralId && serviceUuidStr)
341 {
342 CBUUID *serviceUuid = [CBUUID UUIDWithString:[NSString stringWithUTF8String:serviceUuidStr]];
343 CBPeripheral *peripheral = getCBPeripheral(peripheralId);
344 for (CBService *service in peripheral.services)
345 {
346 if ([serviceUuid isEqual:service.UUID])
347 {
348 return service;
349 }
350 }
351 }
352 return nil;
353}
354
355inline CBCharacteristic *getCharacteristic(peripheral_id_t peripheralId, const char *serviceUuidStr, const char *characteristicUuidStr, characteristic_index_t instanceIndex)
356{
357 CBService *service = getService(peripheralId, serviceUuidStr);
358 if (service && characteristicUuidStr)
359 {
360 CBUUID *characteristicUuid = [CBUUID UUIDWithString:[NSString stringWithUTF8String:characteristicUuidStr]];
361 for (CBCharacteristic *characteristic in service.characteristics)
362 {
363 if ([characteristicUuid isEqual:characteristic.UUID])
364 {
365 if (instanceIndex == 0)
366 {
367 return characteristic;
368 }
369 else
370 {
371 --instanceIndex;
372 }
373 }
374 }
375 }
376 return nil;
377}
378
379} // namespace internal
Definition of the SGBleCentralManagerDelegate class.
Definition of the SGBlePeripheralQueue class.
Definition of SGBleConnectionEvent and SGBleConnectionEventReason enumerations.
A few internal functions.
std::uint32_t request_index_t
Type for the unique index of a BLE request given to this library.
Definition UnityBridge.h:20
void(* PeripheralConnectionEventCallback)(request_index_t requestIndex, peripheral_id_t peripheralId, int connectionEvent, int reason)
Callback notifying of a change of a peripheral connection state, with the reason for the change.
Definition UnityBridge.h:38
void(* RequestStatusCallback)(request_index_t requestIndex, int errorCode)
Callback notifying of the status of a BLE request.
Definition UnityBridge.h:35
void(* ValueReadCallback)(request_index_t requestIndex, const void *data, size_t length, int errorCode)
Callback notifying of the value read from a peripheral's characteristic.
Definition UnityBridge.h:44
const char * peripheral_id_t
Type for peripheral id which is the zero terminated string of the UUID assigned by the system for the...
Definition UnityBridge.h:17
void(* DiscoveredPeripheralCallback)(const char *advertisementDataJson)
Callback notifying of the discovery of a BLE peripheral, with its advertisement data as a JSON string...
Definition UnityBridge.h:32
std::uint64_t characteristic_property_t
Type for the standard BLE properties of characteristics.
Definition UnityBridge.h:26
void(* RssiReadCallback)(request_index_t requestIndex, int rssi, int errorCode)
Callback notifying of the RSSI value read from a peripheral.
Definition UnityBridge.h:41
std::uint32_t characteristic_index_t
Type for the index of a characteristic instance in a service.
Definition UnityBridge.h:23
void(* BluetoothStateUpdateCallback)(int state)
Callback notifying of a change of the host device Bluetooth state, for example radio turned on or off...
Definition UnityBridge.h:29
Implementation of CBCentralManagerDelegate protocol. Stores and notifies of discovered Bluetooth Low ...
Definition SGBleCentralManagerDelegate.h:29
Implementation of the CBPeripheralDelegate protocol. Queues up operations to be performed with a Blue...
Definition SGBlePeripheralQueue.h:44
CBPeripheral * peripheral
Gets the CBPeripheral object for this peripheral.
Definition SGBlePeripheralQueue.h:77