Initiate EASession Fail -- EAAccessory Doesn't Have Protocols

4.2k views Asked by At

I'm working on an iOS app communicate with Bluetooth 2.1. App connects to BT when it becomes active. It works fine if app goes to background and becomes active again.

But I just noticed a problem:

If I turn BT module power off, app get a notification where I do the following:

  - (void)accessoryDidDisconnect:(EAAccessory *)accessory
    {
        NSLog(@"EAController::accessoryDidDisconnect:");
        _selectedAccessory = nil;
        UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Alert" message:@"Lost connection. " delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
        [alert show];
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        [defaults setBool:YES forKey:@"Lost Communication"];
        [self closeSession];
    }

Then I turn the module power back on, go to Settings -> Bluetooth, connect BT module to iPhone, active app (come back from background), EASession can not be initiated:

if (_session == nil)
    {
        NSLog(@"EAController::openSession");
        [_selectedAccessory setDelegate:self];
        _session = [[EASession alloc] initWithAccessory:[self selectedAccessory] forProtocol:_protocolString];

        if (_session)
        {
                    // Set up delegate........
        }
        else
        {
            NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
            [defaults setBool:NO forKey:Unit_Has_Connection_UserDefault_Key];
            NSLog(@"creating session failed");
        }
    }

Initiate _accessoryList and _selectedAccessory:

_accessoryList = [[NSMutableArray alloc] initWithArray:[[EAAccessoryManager sharedAccessoryManager] connectedAccessories]];
_selectedAccessory = [_accessoryList objectAtIndex:0];

I found out _accessoryList has the accessory I'm using but it's "Protocols" is empty.

$12 = 0x1dd58050 <__NSArrayI 0x1dd58050>(
<EAAccessory: 0x1dd1dce0> {
  connected:YES
  connectionID:XXX
  name: XXX
  manufacturer: XXX
  modelNumber: XXX
  serialNumber: 
  firmwareRevision: XXX
  hardwareRevision: XXX
  macAddress: XXX
  protocols: (
)
  delegate: (null)
}
)

If I kill the app and restart, it works fine.

Does anyone know how to solve this?

This question is similar to this one , but happens in different situations and the answer to that question doesn't seem to solve my problem.

4

There are 4 answers

0
user1491987 On BEST ANSWER

After days of searching for solution, I finally solved this problem(Or I hope I solved it.). I'm posting my answer here so if anyone has the same problem can have an idea.

You can never trust EAAccessoryManager, it may have ghost accessory. So initiate _accessoryList using the following command doesn't always work.

_accessoryList = [[NSMutableArray alloc] initWithArray:[[EAAccessoryManager sharedAccessoryManager] connectedAccessories]];

If you initiate an accessoryList with a ghost accessory, for sure you cannot successfully initiate an EASession.

The right way is to use EAAccessory Notification, which described in this answer, but it didn't say how to do it in detail.

First, you need to create post two notifications:

[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(accessoryConnected:) name:EAAccessoryDidConnectNotification object:nil];
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(accessoryDisconnected:) name:EAAccessoryDidConnectNotification object:nil];

[[EAAccessoryManager sharedAccessoryManager]registerForLocalNotifications];

Then in accessoryConnected and accessoryDisconnected, you can do the following.

- (void)accessoryConnected:(NSNotification *)notification
{
    NSLog(@"EAController::accessoryConnected");

    EAAccessory *connectedAccessory = [[notification userInfo] objectForKey:EAAccessoryKey];
    [[self accessoryList] addObject:connectedAccessory];
    if ([_accessoryList count])
    {
        _selectedAccessory = [_accessoryList objectAtIndex:0];
        NSArray *protocolStrings = [_selectedAccessory protocolStrings];
        if ([protocolStrings count]) {
            self.protocolString = [protocolStrings objectAtIndex:0];
            [self openSession];
        }
    }
}


- (void)accessoryDisconnected:(NSNotification *)notification
{
    EAAccessory *disconnectedAccessory = [[notification userInfo] objectForKey:EAAccessoryKey];

    int disconnectedAccessoryIndex = 0;
    for(EAAccessory *accessory in [self accessoryList]) {
        if ([disconnectedAccessory connectionID] == [accessory connectionID]) {
            break;
        }
        disconnectedAccessoryIndex++;
    }

    if (disconnectedAccessoryIndex < [[self accessoryList] count]) {
        [[self accessoryList ] removeObjectAtIndex:disconnectedAccessoryIndex];
    } else {
        NSLog(@"could not find disconnected accessory in accessory list");
    }
    NSLog(@"_accessory did disconnect: %@",_accessoryList);

}
1
CFreymarc On

I discovered that when an EAAccessory reconnects, you see two refreshes of the accessory object. The first has no protocol strings, while a few seconds later, the same accessory (with the same connection ID) reappears with protocol strings. Structured my code s.t. it ignores any accessory with no protocols strings per Apple's recommended techniques.

0
Håkan Berg On

I've also had issues reconnecting to my accessory when returning from background, especially after let's say 8h+ idle. Elaborating with relying only on the EAAccessoryDidConnectNotification/Disconnect (instead of the EAAccessoryManager that can give ghost devices) looks to work fine most of time

However, if two apps are using our accessory it might glitch once the phone/pad is woken up. The first app going to foreground will eventually receive the connect notification. But when swapping in the second app - it will only receive the disconnect notification, never the connect. Here I'm quite stuck to determine if we do have a connected accessory since I can't trust the EAAccessoryManager.

0
deepax11 On

I believe your accessory must have protocol defined along with MFI (Made for iPhone, ipAd and iPod touch) chip to support the communication between iDevice & accessory. You need to check your firmware team as well as apple MFI program. The protocol need to be added in "Supported external accessory protocols" filed of application plist file.