Files
Aether-Engine/Assets/Plugins/iOS/UnityBluetoothLE.mm
2026-02-20 17:53:43 +01:00

1369 lines
48 KiB
Plaintext

//
// UnityBluetoothLE.h
// Unity-iPhone
//
// Created by Tony Pitman on 03/05/2014.
//
//
#import "UnityBluetoothLE.h"
const char _messageDelimeter = '~';
extern "C" {
UnityBluetoothLE *_unityBluetoothLE = nil;
void _iOSBluetoothLELogString (NSString *message) {
NSLog (message);
}
void _iOSBluetoothLELog (char *message) {
_iOSBluetoothLELogString ([NSString stringWithFormat:@"%s", message]);
}
void _iOSBluetoothLEInitialize (BOOL asCentral, BOOL asPeripheral) {
_unityBluetoothLE = [UnityBluetoothLE new];
[_unityBluetoothLE initialize:asCentral asPeripheral:asPeripheral];
}
void _iOSBluetoothLEDeInitialize () {
if (_unityBluetoothLE != nil) {
[_unityBluetoothLE deInitialize];
[_unityBluetoothLE release];
_unityBluetoothLE = nil;
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", "DeInitialized");
}
}
void _iOSBluetoothLEPauseMessages (BOOL pause) {
if (_unityBluetoothLE != nil)
[_unityBluetoothLE pauseMessages:pause];
}
void _iOSBluetoothLEScanForPeripheralsWithServices (char *serviceUUIDsStringRaw, bool allowDuplicates, bool rssiOnly, bool clearPeripheralList, int recordType) {
if (_unityBluetoothLE != nil)
{
_unityBluetoothLE._rssiOnly = rssiOnly;
NSMutableArray *actualUUIDs = nil;
if (serviceUUIDsStringRaw != nil)
{
NSString *serviceUUIDsString = [NSString stringWithFormat:@"%s", serviceUUIDsStringRaw];
NSArray *serviceUUIDs = [serviceUUIDsString componentsSeparatedByString:@"|"];
if (serviceUUIDs.count > 0)
{
actualUUIDs = [[NSMutableArray alloc] init];
for (NSString* sUUID in serviceUUIDs)
[actualUUIDs addObject:[CBUUID UUIDWithString:sUUID]];
}
}
NSDictionary *options = nil;
if (allowDuplicates)
options = @{ CBCentralManagerScanOptionAllowDuplicatesKey: @YES };
[_unityBluetoothLE scanForPeripheralsWithServices:actualUUIDs options:options clearPeripheralList:clearPeripheralList recordType:recordType];
}
}
void _iOSBluetoothLEStopScan () {
if (_unityBluetoothLE != nil)
[_unityBluetoothLE stopScan];
}
void _iOSBluetoothLERetrieveListOfPeripheralsWithServices (char *serviceUUIDsStringRaw) {
if (_unityBluetoothLE != nil)
{
NSMutableArray *actualUUIDs = nil;
if (serviceUUIDsStringRaw != nil)
{
NSString *serviceUUIDsString = [NSString stringWithFormat:@"%s", serviceUUIDsStringRaw];
NSArray *serviceUUIDs = [serviceUUIDsString componentsSeparatedByString:@"|"];
if (serviceUUIDs.count > 0)
{
actualUUIDs = [[NSMutableArray alloc] init];
for (NSString* sUUID in serviceUUIDs)
[actualUUIDs addObject:[CBUUID UUIDWithString:sUUID]];
}
}
[_unityBluetoothLE retrieveListOfPeripheralsWithServices:actualUUIDs];
}
}
void _iOSBluetoothLEConnectToPeripheral (char *name) {
if (_unityBluetoothLE && name != nil)
[_unityBluetoothLE connectToPeripheral:[NSString stringWithFormat:@"%s", name]];
}
void _iOSBluetoothLEDisconnectPeripheral (char *name) {
if (_unityBluetoothLE && name != nil)
[_unityBluetoothLE disconnectPeripheral:[NSString stringWithFormat:@"%s", name]];
}
void _iOSBluetoothLEReadCharacteristic (char *name, char *service, char *characteristic) {
if (_unityBluetoothLE && name != nil && service != nil && characteristic != nil)
[_unityBluetoothLE readCharacteristic:[NSString stringWithFormat:@"%s", name] service:[NSString stringWithFormat:@"%s", service] characteristic:[NSString stringWithFormat:@"%s", characteristic]];
}
void _iOSBluetoothLEWriteCharacteristic (char *name, char *service, char *characteristic, unsigned char *data, int length, BOOL withResponse) {
if (_unityBluetoothLE && name != nil && service != nil && characteristic != nil && data != nil && length > 0)
[_unityBluetoothLE writeCharacteristic:[NSString stringWithFormat:@"%s", name] service:[NSString stringWithFormat:@"%s", service] characteristic:[NSString stringWithFormat:@"%s", characteristic] data:[NSData dataWithBytes:data length:length] withResponse:withResponse];
}
void _iOSBluetoothLESubscribeCharacteristic (char *name, char *service, char *characteristic) {
if (_unityBluetoothLE && name != nil && service != nil && characteristic != nil)
[_unityBluetoothLE subscribeCharacteristic:[NSString stringWithFormat:@"%s", name] service:[NSString stringWithFormat:@"%s", service] characteristic:[NSString stringWithFormat:@"%s", characteristic]];
}
void _iOSBluetoothLEUnSubscribeCharacteristic (char *name, char *service, char *characteristic) {
if (_unityBluetoothLE && name != nil && service != nil && characteristic != nil)
[_unityBluetoothLE unsubscribeCharacteristic:[NSString stringWithFormat:@"%s", name] service:[NSString stringWithFormat:@"%s", service] characteristic:[NSString stringWithFormat:@"%s", characteristic]];
}
void _iOSBluetoothLEDisconnectAll () {
if (_unityBluetoothLE != nil)
[_unityBluetoothLE disconnectAll];
}
void _iOSBluetoothLERequestMtu (char *name, int mtu) {
if (_unityBluetoothLE != nil)
[_unityBluetoothLE requestMtu:[NSString stringWithFormat:@"%s", name] mtu:mtu];
}
void _iOSBluetoothLEReadRSSI (char *name) {
if (_unityBluetoothLE != nil)
[_unityBluetoothLE readRSSI:[NSString stringWithFormat:@"%s", name]];
}
#if !TARGET_OS_TV
void _iOSBluetoothLEScanForBeacons (char *proximityUUIDsStringRaw) {
if (_unityBluetoothLE != nil)
{
NSMutableArray *actualUUIDs = nil;
if (proximityUUIDsStringRaw != nil)
{
NSString *proximityUUIDsString = [NSString stringWithFormat:@"%s", proximityUUIDsStringRaw];
NSArray *proximityUUIDs = [proximityUUIDsString componentsSeparatedByString:@"|"];
if (proximityUUIDs.count > 0)
{
NSMutableArray<CLBeaconRegion *> *beaconRegions = [[NSMutableArray<CLBeaconRegion *> alloc] init];
for (NSString* sUUID in proximityUUIDs)
{
NSArray *parts = [sUUID componentsSeparatedByString:@":"];
if (parts.count == 2)
{
if (@available(iOS 13.0, *)) {
CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithUUID:[[NSUUID alloc] initWithUUIDString:parts[0]] identifier:parts[1]];
[beaconRegions addObject:beaconRegion];
} else {
CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:[[NSUUID alloc] initWithUUIDString:parts[0]] identifier:parts[1]];
[beaconRegions addObject:beaconRegion];
}
[_unityBluetoothLE scanForBeacons:beaconRegions];
}
else
{
NSString *message = [NSString stringWithFormat:@"Error~iBeacon Scanning missing identifiers"];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
}
}
}
else
{
NSString *message = [NSString stringWithFormat:@"Error~iBeacon Scanning requires proximity UUIDs"];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
}
}
else
{
NSString *message = [NSString stringWithFormat:@"Error~iBeacon Scanning requires proximity UUIDs"];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
}
}
}
void _iOSBluetoothLEStopBeaconScan () {
if (_unityBluetoothLE != nil)
[_unityBluetoothLE stopBeaconScan];
}
void _iOSBluetoothLEPeripheralName (char *newName) {
if (_unityBluetoothLE != nil && newName != nil)
[_unityBluetoothLE peripheralName:[[NSString alloc] initWithUTF8String:newName]];
}
void _iOSBluetoothLECreateService (char *uuid, BOOL primary) {
if (_unityBluetoothLE != nil)
[_unityBluetoothLE createService:[NSString stringWithFormat:@"%s", uuid] primary:primary];
}
void _iOSBluetoothLERemoveService (char *uuid) {
if (_unityBluetoothLE != nil)
[_unityBluetoothLE removeService:[NSString stringWithFormat:@"%s", uuid]];
}
void _iOSBluetoothLERemoveServices () {
if (_unityBluetoothLE != nil)
[_unityBluetoothLE removeServices];
}
void _iOSBluetoothLECreateCharacteristic (char *uuid, int properties, int permissions, unsigned char *data, int length) {
if (_unityBluetoothLE != nil) {
NSData *value = nil;
if (data != nil)
value = [[NSData alloc] initWithBytes:data length:length];
[_unityBluetoothLE createCharacteristic:[NSString stringWithFormat:@"%s", uuid] properties:properties permissions:permissions value:value];
}
}
void _iOSBluetoothLERemoveCharacteristic (char *uuid) {
if (_unityBluetoothLE != nil)
[_unityBluetoothLE removeCharacteristic:[NSString stringWithFormat:@"%s", uuid]];
}
void _iOSBluetoothLERemoveCharacteristics () {
if (_unityBluetoothLE != nil)
[_unityBluetoothLE removeCharacteristics];
}
void _iOSBluetoothLEStartAdvertising () {
if (_unityBluetoothLE != nil)
[_unityBluetoothLE startAdvertising];
}
void _iOSBluetoothLEStopAdvertising () {
if (_unityBluetoothLE != nil)
[_unityBluetoothLE stopAdvertising];
}
void _iOSBluetoothLEUpdateCharacteristicValue (char *uuid, unsigned char *data, int length) {
if (_unityBluetoothLE != nil) {
NSData *value = nil;
if (data != nil)
value = [[NSData alloc] initWithBytes:data length:length];
[_unityBluetoothLE updateCharacteristicValue:[NSString stringWithFormat:@"%s", uuid] value:value];
}
}
#endif
}
@implementation UnityBluetoothLE
@synthesize _peripherals;
@synthesize _rssiOnly;
- (void)initialize:(BOOL)asCentral asPeripheral:(BOOL)asPeripheral
{
_mtu = 20;
_isPaused = FALSE;
_isInitializing = TRUE;
_centralManager = nil;
#if !TARGET_OS_TV
_peripheralManager = nil;
_services = nil;
_characteristics = nil;
_allCharacteristics = nil;
#endif
if (asCentral)
_centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
#if !TARGET_OS_TV
if (asPeripheral)
_peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
_services = [[NSMutableDictionary alloc] init];
_characteristics = [[NSMutableDictionary alloc] init];
_allCharacteristics = [[NSMutableDictionary alloc] init];
#endif
_peripherals = [[NSMutableDictionary alloc] init];
}
- (void)deInitialize
{
if (_backgroundMessages != nil)
{
for (UnityMessage *message in _backgroundMessages)
{
if (message != nil)
{
[message deInitialize];
[message release];
}
}
[_backgroundMessages release];
_backgroundMessages = nil;
}
#if !TARGET_OS_TV
if (_peripheralManager != nil)
[self stopAdvertising];
[self removeCharacteristics];
[self removeServices];
#endif
if (_centralManager != nil)
[self stopScan];
[_peripherals removeAllObjects];
}
- (void)pauseMessages:(BOOL)isPaused
{
if (isPaused != _isPaused) {
if (_backgroundMessages == nil)
_backgroundMessages = [[NSMutableArray alloc] init];
_isPaused = isPaused;
// if we are not paused now since we know we changed state
// that means we were paused so we need to pump the saved
// messages to Unity
if (isPaused) {
if (_backgroundMessages != nil) {
for (UnityMessage *message in _backgroundMessages) {
if (message != nil) {
[message sendUnityMessage];
[message deInitialize];
[message release];
}
}
[_backgroundMessages removeAllObjects];
}
}
}
}
#if !TARGET_OS_TV
- (void)createService:(NSString *)uuid primary:(BOOL)primary
{
CBUUID *cbuuid = [CBUUID UUIDWithString:uuid];
CBMutableService *service = [[CBMutableService alloc] initWithType:cbuuid primary:primary];
NSMutableArray *characteristics = [[NSMutableArray alloc] init];
NSEnumerator *enumerator = [_characteristics keyEnumerator];
id key;
while ((key = [enumerator nextObject]))
{
CBCharacteristic *characteristic = [_characteristics objectForKey:key];
[_characteristics removeObjectForKey:key];
[characteristics addObject:characteristic];
[_allCharacteristics setObject:characteristic forKey:[characteristic UUID]];
}
[_characteristics removeAllObjects];
service.characteristics = characteristics;
[_services setObject:service forKey:cbuuid];
if (_peripheralManager != nil)
{
[_peripheralManager addService:service];
}
}
- (void)removeService:(NSString *)uuid
{
if (_services != nil)
{
if (_peripheralManager != nil)
{
CBMutableService *service = [_services objectForKey:uuid];
if (service != nil)
[_peripheralManager removeService:service];
}
[_services removeObjectForKey:uuid];
}
}
- (void)removeServices
{
if (_services != nil)
{
[_services removeAllObjects];
if (_peripheralManager != nil)
[_peripheralManager removeAllServices];
}
if (_allCharacteristics != nil)
[_allCharacteristics removeAllObjects];
}
- (void)peripheralName:(NSString *)newName
{
_peripheralName = newName;
}
- (void)createCharacteristic:(NSString *)uuid properties:(CBCharacteristicProperties)properties permissions:(CBAttributePermissions)permissions value:(NSData *)value
{
CBUUID *cbuuid = [CBUUID UUIDWithString:uuid];
CBCharacteristic *characteristic = [[CBMutableCharacteristic alloc] initWithType:cbuuid properties:properties value:value permissions:permissions];
[_characteristics setObject:characteristic forKey:cbuuid];
}
- (void)removeCharacteristic:(NSString *)uuid
{
if (_characteristics != nil)
[_characteristics removeObjectForKey:uuid];
}
- (void)removeCharacteristics
{
if (_characteristics != nil)
[_characteristics removeAllObjects];
}
- (void)startAdvertising
{
if (_peripheralManager != nil && _services != nil)
{
NSMutableArray *services = [[NSMutableArray alloc] init];
NSEnumerator *enumerator = [_services keyEnumerator];
id key;
while ((key = [enumerator nextObject]))
{
CBMutableService *service = [_services objectForKey:key];
[services addObject:service.UUID];
}
if (_peripheralName == nil)
_peripheralName = @"";
[_peripheralManager startAdvertising:@{ CBAdvertisementDataServiceUUIDsKey : services, CBAdvertisementDataLocalNameKey : _peripheralName }];
}
}
- (void)stopAdvertising
{
if (_peripheralManager != nil)
{
[_peripheralManager stopAdvertising];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", "StoppedAdvertising");
}
}
- (void)updateCharacteristicValue:(NSString *)uuid value:(NSData *)value
{
if (_allCharacteristics != nil)
{
CBUUID *cbuuid = [CBUUID UUIDWithString:uuid];
CBMutableCharacteristic *characteristic = [_allCharacteristics objectForKey:cbuuid];
if (characteristic != nil)
{
characteristic.value = value;
if (_peripheralManager != nil)
[_peripheralManager updateValue:value forCharacteristic:characteristic onSubscribedCentrals:nil];
}
}
}
- (void)requestMtu:(NSString *)name mtu:(int)mtu
{
_mtu = mtu;
NSString *message = [NSString stringWithFormat:@"MtuChanged~%@~%d", name, _mtu];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String]);
}
- (void)scanForBeacons:(NSArray<CLBeaconRegion *>*)beaconRegions
{
if ([CLLocationManager isRangingAvailable])
{
_locationManager = [CLLocationManager new];
_locationManager.delegate = self;
[_locationManager requestWhenInUseAuthorization];
for (CLBeaconRegion *region in beaconRegions)
[_locationManager startRangingBeaconsInRegion:region];
}
else
{
NSString *message = [NSString stringWithFormat:@"Error~iBeacon Ranging is not available"];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
}
}
- (void) stopBeaconScan
{
if (_locationManager != nil)
{
for (CLBeaconRegion *region in _locationManager.rangedRegions)
[_locationManager stopRangingBeaconsInRegion:region];
[_locationManager release];
_locationManager = nil;
}
}
// beacon delegate implementation
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray<CLBeacon *> *)beacons inRegion:(CLBeaconRegion *)region
{
NSString *message;
for (CLBeacon *beacon in beacons)
{
message = [NSString stringWithFormat:@"DiscoveredBeacon~%@~%ld~%ld~%ld~%ld~%ld", [beacon.proximityUUID.UUIDString stringByReplacingOccurrencesOfString:@"-" withString:@""], beacon.major.longValue, beacon.minor.longValue, (long)beacon.rssi, (long)0, (long)beacon.proximity];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String]);
}
}
#endif
// central delegate implementation
- (void)scanForPeripheralsWithServices:(NSArray *)serviceUUIDs options:(NSDictionary *)options clearPeripheralList:(BOOL)clearPeripheralList recordType:(int)recordType
{
if (_centralManager != nil)
{
recordType = recordType;
if (clearPeripheralList && _peripherals != nil)
[_peripherals removeAllObjects];
[_centralManager scanForPeripheralsWithServices:serviceUUIDs options:options];
}
}
- (void) stopScan
{
if (_centralManager != nil)
[_centralManager stopScan];
}
- (void)retrieveListOfPeripheralsWithServices:(NSArray *)serviceUUIDs
{
if (_centralManager != nil)
{
if (_peripherals != nil)
[_peripherals removeAllObjects];
NSArray * list = [_centralManager retrieveConnectedPeripheralsWithServices:serviceUUIDs];
if (list != nil)
{
for (int i = 0; i < list.count; ++i)
{
CBPeripheral *peripheral = [list objectAtIndex:i];
if (peripheral != nil)
{
NSString *identifier = [[peripheral identifier] UUIDString];
NSString *name = [peripheral name];
NSString *message = [NSString stringWithFormat:@"RetrievedConnectedPeripheral~%@~%@", identifier, name];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String]);
[_peripherals setObject:peripheral forKey:identifier];
}
}
}
}
}
- (void)connectToPeripheral:(NSString *)name
{
if (_peripherals != nil && name != nil)
{
CBPeripheral *peripheral = [_peripherals objectForKey:name];
if (peripheral != nil)
[_centralManager connectPeripheral:peripheral options:nil];
}
}
- (void)disconnectAll
{
if (_peripherals != nil && [_peripherals count] > 0)
{
NSArray* keys = [_peripherals allKeys];
for(NSString* key in keys)
{
CBPeripheral *peripheral = [_peripherals objectForKey:key];
if (peripheral != nil)
[_centralManager cancelPeripheralConnection:peripheral];
}
}
}
- (void)disconnectPeripheral:(NSString *)name
{
if (_peripherals != nil && name != nil)
{
CBPeripheral *peripheral = [_peripherals objectForKey:name];
if (peripheral != nil)
{
[_centralManager cancelPeripheralConnection:peripheral];
}
}
}
- (void)readRSSI:(NSString *)name
{
if (_peripherals != nil && name != nil)
{
CBPeripheral *peripheral = [_peripherals objectForKey:name];
if (peripheral != nil)
{
[peripheral readRSSI];
}
}
}
- (CBCharacteristic *)getCharacteristic:(NSString *)name service:(NSString *)serviceString characteristic:(NSString *)characteristicString
{
CBCharacteristic *returnCharacteristic = nil;
if (name != nil && serviceString != nil && characteristicString != nil && _peripherals != nil)
{
CBPeripheral *peripheral = [_peripherals objectForKey:name];
if (peripheral != nil)
{
CBUUID *serviceUUID = [CBUUID UUIDWithString:serviceString];
CBUUID *characteristicUUID = [CBUUID UUIDWithString:characteristicString];
for (CBService *service in peripheral.services)
{
if ([service.UUID isEqual:serviceUUID])
{
for (CBCharacteristic *characteristic in service.characteristics)
{
if ([characteristic.UUID isEqual:characteristicUUID])
{
returnCharacteristic = characteristic;
}
}
}
}
}
}
return returnCharacteristic;
}
- (void)readCharacteristic:(NSString *)name service:(NSString *)serviceString characteristic:(NSString *)characteristicString
{
if (name != nil && serviceString != nil && characteristicString != nil && _peripherals != nil)
{
CBPeripheral *peripheral = [_peripherals objectForKey:name];
if (peripheral != nil)
{
CBCharacteristic *characteristic = [_unityBluetoothLE getCharacteristic:name service:serviceString characteristic:characteristicString];
if (characteristic != nil)
[peripheral readValueForCharacteristic:characteristic];
}
}
}
- (void)writeCharacteristic:(NSString *)name service:(NSString *)serviceString characteristic:(NSString *)characteristicString data:(NSData *)data withResponse:(BOOL)withResponse
{
if (name != nil && serviceString != nil && characteristicString != nil && _peripherals != nil && data != nil)
{
CBPeripheral *peripheral = [_peripherals objectForKey:name];
if (peripheral != nil)
{
CBCharacteristic *characteristic = [_unityBluetoothLE getCharacteristic:name service:serviceString characteristic:characteristicString];
if (characteristic != nil)
{
CBCharacteristicWriteType type = CBCharacteristicWriteWithoutResponse;
if (withResponse)
type = CBCharacteristicWriteWithResponse;
[self writeCharactersticBytes:peripheral characteristic:characteristic data:data withResponse:type];
}
}
}
}
- (void)subscribeCharacteristic:(NSString *)name service:(NSString *)serviceString characteristic:(NSString *)characteristicString
{
if (name != nil && serviceString != nil && characteristicString != nil && _peripherals != nil)
{
CBPeripheral *peripheral = [_peripherals objectForKey:name];
if (peripheral != nil)
{
CBCharacteristic *characteristic = [_unityBluetoothLE getCharacteristic:name service:serviceString characteristic:characteristicString];
if (characteristic != nil)
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
}
}
- (void)unsubscribeCharacteristic:(NSString *)name service:(NSString *)serviceString characteristic:(NSString *)characteristicString
{
if (name != nil && serviceString != nil && characteristicString != nil && _peripherals != nil)
{
CBPeripheral *peripheral = [_peripherals objectForKey:name];
if (peripheral != nil)
{
CBCharacteristic *characteristic = [_unityBluetoothLE getCharacteristic:name service:serviceString characteristic:characteristicString];
if (characteristic != nil)
[peripheral setNotifyValue:NO forCharacteristic:characteristic];
}
}
}
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
switch (central.state)
{
case CBCentralManagerStateUnsupported:
{
NSLog(@"Central State: Unsupported");
NSString *message = [NSString stringWithFormat:@"Error~Bluetooth LE Not Supported"];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
} break;
case CBCentralManagerStateUnauthorized:
{
NSLog(@"Central State: Unauthorized");
NSString *message = [NSString stringWithFormat:@"Error~Bluetooth LE Not Authorized"];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
} break;
case CBCentralManagerStatePoweredOff:
{
NSLog(@"Central State: Powered Off");
NSString *message = [NSString stringWithFormat:@"Error~Bluetooth LE Powered Off"];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
} break;
case CBCentralManagerStatePoweredOn:
{
NSLog(@"Central State: Powered On");
if (_isInitializing)
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", "Initialized");
_isInitializing = FALSE;
} break;
case CBCentralManagerStateUnknown:
{
NSLog(@"Central State: Unknown");
NSString *message = [NSString stringWithFormat:@"Error~Bluetooth LE Unknown"];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
} break;
default:
{
}
}
}
- (void)centralManager:(CBCentralManager *)central didRetrievePeripherals:(NSArray *)peripherals
{
}
- (void)centralManager:(CBCentralManager *)central didRetrieveConnectedPeripherals:(NSArray *)peripherals
{
}
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
if (error)
{
NSString *message = [NSString stringWithFormat:@"Error~%@", error.description];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
}
}
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
if (_peripherals != nil && peripheral != nil)
{
NSString *name = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey];
if (name == nil)
name = peripheral.name;
if (name == nil)
name = @"No Name";
if (name != nil)
{
NSString *identifier = nil;
NSString *foundPeripheral = [self findPeripheralName:peripheral];
if (foundPeripheral == nil)
identifier = [[peripheral identifier] UUIDString];
else
identifier = foundPeripheral;
NSString *message = nil;
if (advertisementData != nil && [advertisementData objectForKey:CBAdvertisementDataManufacturerDataKey] != nil)
{
NSData* bytes = [advertisementData objectForKey:CBAdvertisementDataManufacturerDataKey];
message = [NSString stringWithFormat:@"DiscoveredPeripheral~%@~%@~%@~%@", identifier, name, RSSI, [UnityBluetoothLE base64StringFromData:bytes length:bytes.length]];
}
else if (RSSI != 0 && _rssiOnly)
{
message = [NSString stringWithFormat:@"DiscoveredPeripheral~%@~%@~%@~", identifier, name, RSSI];
}
else
{
message = [NSString stringWithFormat:@"DiscoveredPeripheral~%@~%@", identifier, name];
}
if (message != nil)
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String]);
[_peripherals setObject:peripheral forKey:identifier];
}
}
}
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
if (_peripherals != nil)
{
NSString *foundPeripheral = [self findPeripheralName:peripheral];
if (foundPeripheral != nil)
{
NSString *message = [NSString stringWithFormat:@"DisconnectedPeripheral~%@", foundPeripheral];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String]);
}
}
}
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
NSString *foundPeripheral = [self findPeripheralName:peripheral];
if (foundPeripheral != nil)
{
NSString *message = [NSString stringWithFormat:@"ConnectedPeripheral~%@", foundPeripheral];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String]);
peripheral.delegate = self;
[peripheral discoverServices:nil];
}
}
- (CBPeripheral *) findPeripheralInList:(CBPeripheral*)peripheral
{
CBPeripheral *foundPeripheral = nil;
NSEnumerator *enumerator = [_peripherals keyEnumerator];
id key;
while ((key = [enumerator nextObject]))
{
CBPeripheral *tempPeripheral = [_peripherals objectForKey:key];
if ([tempPeripheral isEqual:peripheral])
{
foundPeripheral = tempPeripheral;
break;
}
}
return foundPeripheral;
}
- (NSString *) findPeripheralName:(CBPeripheral*)peripheral
{
NSString *foundPeripheral = nil;
NSEnumerator *enumerator = [_peripherals keyEnumerator];
id key;
while ((key = [enumerator nextObject]))
{
CBPeripheral *tempPeripheral = [_peripherals objectForKey:key];
if ([tempPeripheral isEqual:peripheral])
{
foundPeripheral = key;
break;
}
}
return foundPeripheral;
}
// central peripheral delegate implementation
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
if (error)
{
NSString *message = [NSString stringWithFormat:@"Error~%@", error.description];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
}
else
{
NSString *foundPeripheral = [self findPeripheralName:peripheral];
if (foundPeripheral != nil)
{
for (CBService *service in peripheral.services)
{
NSString *message = [NSString stringWithFormat:@"DiscoveredService~%@~%@", foundPeripheral, [service UUID]];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String]);
[peripheral discoverCharacteristics:nil forService:service];
}
}
}
}
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
if (error)
{
NSString *message = [NSString stringWithFormat:@"Error~%@", error.description];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
}
else
{
NSString *foundPeripheral = [self findPeripheralName:peripheral];
if (foundPeripheral != nil)
{
for (CBCharacteristic *characteristic in service.characteristics)
{
NSString *message = [NSString stringWithFormat:@"DiscoveredCharacteristic~%@~%@~%@", foundPeripheral, [service UUID], [characteristic UUID]];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String]);
}
}
}
}
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
if (error)
{
NSString *message = [NSString stringWithFormat:@"Error~%@", error.description];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
}
else
{
NSString *foundPeripheral = [self findPeripheralName:peripheral];
if (foundPeripheral != nil)
{
if (characteristic.value != nil)
{
NSString *message = [NSString stringWithFormat:@"DidUpdateValueForCharacteristic~%@~%@~%@", foundPeripheral, [characteristic UUID], [UnityBluetoothLE base64StringFromData:characteristic.value length:characteristic.value.length]];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String]);
//NSString *message = [UnityBluetoothLE base64StringFromData:characteristic.value length:characteristic.value.length];
//UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothData", [message UTF8String] );
}
}
}
}
- (void)writeCharactersticBytesReset
{
_writeCharacteristicBytes = nil;
_writeCharacteristicLength = 0;
_writeCharacteristicPosition = 0;
_writeCharacteristicBytesToWrite = 0;
_writeCharacteristicWithResponse = CBCharacteristicWriteWithResponse;
_writeCharacteristicRetries = 3;
}
- (void)writeCharactersticBytes:(CBPeripheral *)peripheral characteristic:(CBCharacteristic *)characteristic data:(NSData *)data withResponse:(CBCharacteristicWriteType)withResponse
{
if (_writeCharacteristicBytes == nil && (_mtu == 0 || data.length > _mtu))
{
_writeCharacteristicLength = data.length;
_writeCharacteristicBytes = (unsigned char*)malloc(_writeCharacteristicLength);
memcpy(_writeCharacteristicBytes, [data bytes], _writeCharacteristicLength);
_writeCharacteristicPosition = 0;
_writeCharacteristicWithResponse = withResponse;
if (_mtu == 0)
_writeCharacteristicBytesToWrite = _writeCharacteristicLength;
else
_writeCharacteristicBytesToWrite = _mtu;
}
NSLog(@"write characteristic block");
if (_writeCharacteristicBytes != nil)
{
NSMutableData *newData = [NSMutableData dataWithCapacity:_writeCharacteristicBytesToWrite];
[newData appendBytes:&_writeCharacteristicBytes[_writeCharacteristicPosition] length:_writeCharacteristicBytesToWrite];
data = newData;
NSLog(@"data: %@", data);
}
NSLog(@"writing %ld bytes, %ld with response", data.length, withResponse);
[peripheral writeValue:data forCharacteristic:characteristic type:withResponse];
if (withResponse == CBCharacteristicWriteWithoutResponse)
{
double delayInSeconds = 0.01;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self writeNextPacket:peripheral characteristic:characteristic];
});
}
}
- (void)writeNextPacket:(CBPeripheral *)peripheral characteristic:(CBCharacteristic *)characteristic
{
if (_writeCharacteristicLength > _writeCharacteristicPosition + _writeCharacteristicBytesToWrite)
{
_writeCharacteristicPosition += _writeCharacteristicBytesToWrite;
if (_writeCharacteristicPosition + _writeCharacteristicBytesToWrite > _writeCharacteristicLength)
_writeCharacteristicBytesToWrite = _writeCharacteristicLength - _writeCharacteristicPosition;
NSMutableData *data = [NSMutableData dataWithCapacity:_writeCharacteristicLength];
[data appendBytes:_writeCharacteristicBytes length:_writeCharacteristicLength];
[self writeCharactersticBytes:peripheral characteristic:characteristic data:data withResponse:_writeCharacteristicWithResponse];
}
else
{
[self writeCharactersticBytesReset];
NSString *message = [NSString stringWithFormat:@"DidWriteCharacteristic~%@", characteristic.UUID];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
}
}
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
if (error)
{
NSString *message = [NSString stringWithFormat:@"Error~%@", error.description];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
}
else
{
NSLog(@"%ld bytes written, %ld position, %ld length, %ld with response", _writeCharacteristicBytesToWrite, _writeCharacteristicPosition, _writeCharacteristicLength, _writeCharacteristicWithResponse);
if (_writeCharacteristicBytesToWrite > 0)
{
[self writeNextPacket:peripheral characteristic:characteristic];
}
else
{
NSString *message = [NSString stringWithFormat:@"DidWriteCharacteristic~%@", characteristic.UUID];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
}
}
}
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
if (error)
{
NSString *message = [NSString stringWithFormat:@"Error~%@", error.description];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
}
else
{
NSString *foundPeripheral = [self findPeripheralName:peripheral];
if (foundPeripheral != nil)
{
NSString *message = [NSString stringWithFormat:@"DidUpdateNotificationStateForCharacteristic~%@~%@", foundPeripheral, characteristic.UUID];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
}
}
}
- (void)peripheral:(CBPeripheral *) peripheral didReadRSSI:(NSNumber *)RSSI error:(NSError *)error
{
if (error)
{
NSString *message = [NSString stringWithFormat:@"Error~%@", error.description];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
}
else
{
NSString *foundPeripheral = [self findPeripheralName:peripheral];
if (foundPeripheral != nil)
{
NSString *message = [NSString stringWithFormat:@"DidReadRSSI~%@~%@", foundPeripheral, RSSI];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
}
}
}
#if !TARGET_OS_TV
// peripheral manager delegate implementation
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral
{
_iOSBluetoothLELogString ([NSString stringWithFormat:@"Peripheral State Update: %d", (int)peripheral.state]);
if (_isInitializing && peripheral.state == CBPeripheralManagerStatePoweredOn)
{
_isInitializing = FALSE;
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", "Initialized");
}
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error
{
if (error)
{
NSString *message = [NSString stringWithFormat:@"Error~%@", error.description];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
}
else
{
NSString *message = [NSString stringWithFormat:@"ServiceAdded~%@", service.UUID];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
}
}
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error
{
if (error)
{
NSString *message = [NSString stringWithFormat:@"Error~%@", error.description];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
}
else
{
NSString *message = [NSString stringWithFormat:@"StartedAdvertising"];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
}
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic
{
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic
{
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request
{
BOOL success = FALSE;
if (_peripheralManager != nil)
{
CBMutableCharacteristic *characteristic = [_allCharacteristics objectForKey:request.characteristic.UUID];
if (characteristic != nil)
{
request.value = [characteristic.value subdataWithRange:NSMakeRange(request.offset, characteristic.value.length - request.offset)];
[_peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
success = TRUE;
}
}
if (!success)
[_peripheralManager respondToRequest:request withResult:CBATTErrorAttributeNotFound];
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests
{
BOOL success = FALSE;
if (_peripheralManager != nil)
{
for (int i = 0; i < requests.count; ++i)
{
CBATTRequest *request = [requests objectAtIndex:i];
if (request != nil)
{
CBMutableCharacteristic *characteristic = [_allCharacteristics objectForKey:request.characteristic.UUID];
if (characteristic != nil)
{
characteristic.value = request.value;
NSString *message = [NSString stringWithFormat:@"PeripheralReceivedWriteData~%@~%@", [characteristic UUID], [UnityBluetoothLE base64StringFromData:characteristic.value length:characteristic.value.length]];
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String]);
success = TRUE;
}
else
{
success = FALSE;
break;
}
}
else
{
success = FALSE;
break;
}
}
}
if (success)
[_peripheralManager respondToRequest:[requests objectAtIndex:0] withResult:CBATTErrorSuccess];
else
[_peripheralManager respondToRequest:[requests objectAtIndex:0] withResult:CBATTErrorAttributeNotFound];
}
#endif
- (void)sendUnityMessage:(BOOL)isString message:(NSString *)message
{
if (_isPaused) {
if (_backgroundMessages != nil) {
UnityMessage *unitymessage = [[UnityMessage alloc] init];
if (unitymessage != nil) {
[unitymessage initialize:isString message:message];
[_backgroundMessages addObject:unitymessage];
}
}
}
else {
if (isString)
{
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] );
}
else
{
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothData", [message UTF8String] );
}
}
}
static char base64EncodingTable[64] =
{
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
};
+ (NSString *) base64StringFromData: (NSData *)data length: (int)length
{
unsigned long ixtext, lentext;
long ctremaining;
unsigned char input[3], output[4];
short i, charsonline = 0, ctcopy;
const unsigned char *raw;
NSMutableString *result;
lentext = [data length];
if (lentext < 1)
return @"";
result = [NSMutableString stringWithCapacity: lentext];
raw = (const unsigned char *)[data bytes];
ixtext = 0;
while (true) {
ctremaining = lentext - ixtext;
if (ctremaining <= 0)
break;
for (i = 0; i < 3; i++) {
unsigned long ix = ixtext + i;
if (ix < lentext)
input[i] = raw[ix];
else
input[i] = 0;
}
output[0] = (input[0] & 0xFC) >> 2;
output[1] = ((input[0] & 0x03) << 4) | ((input[1] & 0xF0) >> 4);
output[2] = ((input[1] & 0x0F) << 2) | ((input[2] & 0xC0) >> 6);
output[3] = input[2] & 0x3F;
ctcopy = 4;
switch (ctremaining) {
case 1:
ctcopy = 2;
break;
case 2:
ctcopy = 3;
break;
}
for (i = 0; i < ctcopy; i++)
[result appendString: [NSString stringWithFormat: @"%c", base64EncodingTable[output[i]]]];
for (i = ctcopy; i < 4; i++)
[result appendString: @"="];
ixtext += 3;
charsonline += 4;
if ((length > 0) && (charsonline >= length))
charsonline = 0;
}
return result;
}
#pragma mark Internal
@end
@implementation UnityMessage
- (void)initialize:(BOOL)isString message:(NSString *)message
{
_isString = isString;
_message = [message copy];
}
- (void)deInitialize
{
if (_message != nil)
[_message release];
_message = nil;
}
- (void)sendUnityMessage
{
if (_message != nil) {
if (_isString)
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [_message UTF8String] );
else
UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothData", [_message UTF8String] );
}
}
@end