The question is not for BLE device, its just normal bluetooth device.
currently my code works like this,
I call the function :
[[EAAccessoryManager sharedAccessoryManager] showBluetoothAccessoryPickerWithNameFilter:nil completion:^(NSError *error)
{
}];
}
and it opens the popup with list of available Bluetooth device, then i click on my desired device and get an object and go ahead.
Is there any way that i can skip this picker step and directly get an object of my device?
No, it's not possible to connect without pairing first. The first time, you must have the user pair with the device either from the Settings app or from the picker. After the first pairing, however, you can skip the picker and get an EAAccesory * for your accessory, if the accessory is already connected to the iOS device. Here is how you can query the list of connected accessories:
NSArray<EAAccessory *> *connectedAccessories = [EAAccessoryManager sharedAccessoryManager].connectedAccessories;
for (EAAccessory *accessory in connectedAccessories) {
// Implement needed filter to recognize your device.
// You can use for instance accessory.protocolStrings
// The MAC address is available with [accessory valueForKey:#"macAddress"]
}
With the EAAccessory framework you can't initiate a connection to a device programmatically. For subsequent connections, you can have your device reconnect to the last connected device (if you control the firmware). This will trigger the EAAccessoryDidConnectNotification if your app is in the foreground, otherwise it will queue the notification and update the list of connected accessories.
Related
I'm trying to get a list of available connected peripherals on iOS. What I do first is grab everything that's connected from the sharedAccessoryManager.
But now I want to filter by accessories that are available for my specific protocol and are not currently in session with another app.
The goal is to have multiple apps that can connect to the same kind of accessory, but I want to avoid attempting to start a session with accessories already in session with one of the apps in the background.
Would the best way to do this just open an EASession for each relevant device and immediately closing it, noting whether or not the initWithAccessory returns nil? e.g.
for (EAAccessory *accessory in [[EAAccessoryManager sharedAccessoryManager] connectedAccessories])
{
EASession *session = [[EASession alloc] initWithAccessory:accessory forProtocol:#"myprotocol"];
if (session) {
// close the EASession
session = nil;
// do stuff to save the accessory and report
// to the user that it is available to have a session started
}
}
Are there any problems that might arise from testing session opens for every device? Do I need to clean up the input/output streams too?
The problem seems to be that I start communicating with the accessories, which I don't want to do, instead just check if they're available.
This is exactly what you should get by checking the protocolStrings list. Your Info.plist key will have already filtered out any accessories you don't support at all. But protocolStrings indicates the list of protocols the device is ready to support now:
When deciding whether to connect to an accessory, you should always first check the accessory’s declared protocols in the protocolStrings array. This list indicates the types of data the accessory is capable of processing at that moment, which may not be the full list of protocols for which the accessory is designed. For example, an accessory that is connected but not yet authenticated will report no supported protocols until authentication is successful. Don't connect to the accessory unless and until the list includes the protocol you intend to use.
It's up to the device to accurately report the list of protocols it currently supports. In my experience, constructing an EASession does not guarantee that the session is actually available. For example, if you try to create two sessions to the same device and the same protocol in the same process (which is invalid), the session will be created, but you'll see errors when you try to create the streams.
Our app is using WLAN to communicate with a wireless device. When our app is installed in iOS 10. Sometimes, udp socket doesn't work. The reason for that is, in iOS 10 they added a new setting or permission under your app that allows the user to switch on or off the user of WLAN or cellular data.
The following would appear in the settings of the app:
When I tap on the Wireless... It will bring me to this UI:
After allowing WLAN use. The app would work fine.
Now, the problem is, sometimes, or in some devices running iOS 10, the settings that I just showed you doesn't appear(I am referring to the setting shown on the first image). So, is there anything I can do to make that settings always appear? It seems that sometimes iOS system doesn't recognize that my app is using wireless data. And it would result in my app would never get to use WLAN forever.
There is no user permission to use WIFI in iOS10.
The application in your screenshot (BSW SMART KIT) is using Wireless Accessories (WAC), i.e. is able to connect to wireless speakers. To accomplish this the Wireless Accessory Configuration capability is required. This is what you can dis/enable in the systems settings (your screenshot).
The switch in the settings shows up after connecting to a device via WIFI through WAC. You can see this behaviour in your sample app (BSW SMART KIT) too.
This sample code might let you get the idea. Detailed information in Apples documentation.
After a time of researching. I ended up seeking help with Apple Code Level Support. Apple states that this problem would most probably occur when you reskin your app. They say that probably it's because of the Image UUID of the main app and the reskinned app are the same. When you install both of them in your phone, the system will treat the reskinned app as the same app compared to the main app. So, if the one app fails to access WLAN, then it will also affect the other one. According to them, this appears to be a bug in iOS. And currently, they don't have any solution for the developers. This is the radar bug number:
What I did to somehow lessen the occurrence of the problem is to add tracking to the Network Restriction by using the following code.
- (void)startCheckingNetworkRestriction
{
__weak AppDelegate *weakSelf = self;
_monitor = [[CTCellularData alloc] init];
_monitor.cellularDataRestrictionDidUpdateNotifier = ^(CTCellularDataRestrictedState state)
{
[[NSOperationQueue mainQueue] addOperationWithBlock:^
{
NSString * statusStr;
switch(state)
{
case kCTCellularDataRestrictedStateUnknown:
{
statusStr = #"restriction status:Unknown";
}
break;
case kCTCellularDataRestricted:
{
statusStr = #"restriction status:restricted";
[weakSelf performUrlSession];
}
break;
case kCTCellularDataNotRestricted:
{
statusStr = #"restriction status:not restricted";
}
break;
default:
{
abort();
}
break;
}
NSLog(#"Restriction state: %#", statusStr);
}];
};
}
Take note that you have to import CoreTelephony to do this.
#import CoreTelephony;
when I detect that the network is restricted. I will open a URL session to force internet access attempt and would hope that the restriction alert dialog would pop out. Once the alert is pop out, then the WLAN Access Settings that I was talking about would definitely appear under the settings. There are times that this doesn't work. If it happens, then you'll just have to rename the bundle ID, and make a couple of changes to your code and then rebuild it a couple of times (Well, that's what I did when I was experimenting this). Reinstalling the app won't do a thing. Restarting and resetting the phone won't do either.
Hope this helps.
I am writing an iOS-based program that interacts with a Bluetooth device via the External Accessory Framework. I would like to determine ahead of time if Bluetooth is even enabled before attempting to connect. Unfortunately, I don't see anything in the External Accessory Framework documentation that allows me to do this.
After checking the docs for the [EAAccessoryManager][1], the closest I can find is to check the [connectedAccessories][1] list to see if any devices are currently connected. However, this doesn't directly indicate the Bluetooth adapter's status.
There are plenty of examples on SO pertaining to Core Bluetooth and Bluetooth LE. I'm specifically looking for a solution related to the External Accessory Framework.
This is not possible with the ExternalAccessory framework. You should use CoreBluetooth, which can give you the information you need on devices that have BLE hardware, i.e. everything released after 2011. The fact that you are using ExternalAccessory for communication with your device does not prevent you from also using CoreBluetooth just for the purpose of knowing whether Bluetooth is turned on.
For older devices there is no way to get this info with public APIs, however if you are not intending to publish your app on the App Store you can use the private BluetoothManager framework.
To get the info with CoreBluetooth you will need to instanciate a CBCentralManager instance, for example like this :
centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];
and then implement the following delegate method to get the relevant info :
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
BOOL bleAvailable = central.state != CBCentralManagerStateUnsupported;
if (bleAvailable) {
BOOL bluetoothTurnedOn = central.state != CBCentralManagerStatePoweredOff;
// Do something with the info
}
else {
// Out of luck... We won't be able to determine whether BT is on or off
}
}
I need to implement a self bluetooth app to connect with all bluetooth devices to iPhone. I know it is not possible with CoreBluetooth framework.
I use private API and added header files of DeviceManager and BluetoothManager to private frameworks and downloaded BeeTee Project from here
This app runs and finds all bluetooth device near me but when I have tried to connect to a device by this code:
[self.bluetoothManager connectDevice:bluetoothDevice];
and this
[bluetoothDevice connect];
When a cell is selected, Both of above codes request to connect but BTM returns this message:
BeeTee[5473:60b] BTM: connection to service 0xffffffff on device "Nokia 500" F4:xx:xx:xx:xx:xx failed with error 109
What is error 109? Which would be set service number?
I guess I should pair devices before connecting but how can I do that?
I am just guessing, but I think the problem is that the BluetoothManager.framework is made for the External Accessory Program by Apple. And this allows (among others) SPP Bluetooth connection to certificated devices. But there is the problem: you need to have a device with a authentication chip inside.
I don't know on which level/layer Apple implemented the authentication, but I fear the did it one layer under the private framework BeeTee is using.
UPDATE: Maybe this is helpful for you:
BluetoothManager *bluetoothManager = //...
[bluetoothManager setDevicePairingEnabled:YES];
[bluetoothManager connectDevice:bluetoothDevice withServices:0x00002000];
Credits
BluetoothManager *bluetoothManager = //...
[bluetoothManager setDevicePairingEnabled:YES];
[btManager setPincode:#"111111" forDevice:bluetoothDevice.deviceRef];
//where 111111 is your device PIN
[bluetoothManager connectDevice:bluetoothDevice withServices:0x00002000];
What I am trying to do
I am trying to connect my app to a Bluetooth LE device which needs to be paired.
Current behaviour
There is no problem without pairing the device and my iPhone application. I am able to connect, reconnect and read/write characteristics without any problem.
But, if the device need to be paired, I am only able to read/write characteristics the first time, right after the pairing popup confirmation. The next time, I discover and connect the app to my device, but I don't have the rights to read/write characteristics data because (I guess) I am not using the pairing information.
Finally...
After spending few hours searching around the web with no luck here are my questions :
How can I connect my app to a Bluetooth LE device from my iPhone app using the pairing data stored in my phone? Am I missing something?
Is it possible that it is not an IOS problem because if pairing data are present in the phone for the connecting device, it is automatically used?
Is there someone with experience with Bluetooth LE and IOS to help me?
Update 2013-10-27
I have discovered that you can't read a protected characteristic by pairing authentication right after that the characteristic has been discovered if a pairing exists (no confirmation popup). No problem with non-protected characteristic! I don't know exactly why is happening, but the behavior is that the IOS app never receive answers from the device.
So if the first reading is done after, it doesn't cause problem. Here is the code I am using to discover characteristics with the data reading in comment.
- (void) peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error;
{
NSArray *characteristics = [service characteristics];
CBCharacteristic *characteristic;
if (peripheral != servicePeripheral) {
NSLog(#"Wrong Peripheral.\n");
return ;
}
if (service != batteryService) {
NSLog(#"Wrong Service.\n");
return ;
}
if (error != nil) {
NSLog(#"Error %#\n", error);
return ;
}
for (characteristic in characteristics) {
NSLog(#"discovered characteristic %#", [characteristic UUID]);
if ([[characteristic UUID] isEqual:[CBUUID UUIDWithString:kBatteryCharacteristicUUIDString]]) { // Bat
NSLog(#"Discovered Bat Characteristic");
batteryCharacteristic = [characteristic retain];
//--> generate problem when pairing exists between IOS app and device
//[peripheral readValueForCharacteristic:batteryCharacteristic];
}
}
}
You don't have to do anything in your app for pairing management.
If your app runs in LE Central mode, and the peripheral sends an Insufficient Authentication error code in response to a read / write request, iOS will automatically pair with your device and will retry the request.
If you disconnect from the device, and later reconnect again, the peripheral needs to send the Insufficient Authentication error code again for the iPhone to restart encryption. Again, you don't have to do anything special in your app here.
If your app runs in LE Peripheral mode, things are a bit different. When you set up your GATT database, make sure to set correct flags for both the CBAttributePermissions and CBCharacteristicProperties. This will tell iOS that it should send the Insufficient Authentication error code itself, if it is not paired. It is then the responsibility of the central device to start the encryption process.
In the Bluetooth Accessory Design Guidelines for Apple Products, further restrictions are described.
Your accessory needs the capability to resolve private Bluetooth addresses. The iPhone will change its public Bluetooth address every now and then, and only paired devices will have the correct key to resolve that public address and recognize the iPhone.
"Section 3.9 Pairing" is also interesting.
Note that if you pair without man-in-the-middle (MITM) protection, your peripheral can use the resulting key to resolve the private Bluetooth address of the iPhone. However, you won't be able to encrypt the channel.
Pairing with MITM protection on iOS involves entering a PIN code that is displayed by the remote device. Out-of-band (OOB) pairing where you send pairing data over an external channel is not supported by iOS as far as I know (at least there's no public APIs to set OOB data).
Long story short: if you have only a "Pair" / "Cancel" pairing, you cannot encrypt the LE channel but only recognize the iPhone in future connections. The nice thing is that you can still recognize the iPhone even if you unpair it on the iPhone side, and even after restoring the iPhone firmware ;-).
Regarding LE encryption in general: it's not secure anyways (see http://eprint.iacr.org/2013/309).