For iOS Applications to interact with third party hardware devices over serial Bluetooth need MFI certification if they its not part of iOS Supported Bluetooth Profiles – https://support.apple.com/en-in/HT204387. Hardware manufacturers should certify their device by enrolling to Apple’s MFI program – https://developer.apple.com/programs/MFI/
For iOS Application developers to interact with these MFI certified devices, there is support in iOS SDK. The External Accessory framework provides support for communicating with external hardware connected to an iOS-based device using Bluetooth (MFI certified).
Below are the steps which explain how to use External Accessory Framework and integrate with MFI hardware devices.
1. Including the External Accessory Framework in Your Project
To use the features of the External Accessory framework, you must add External Accessory framework to your Xcode project and link against it in any relevant targets. To access the classes and headers of the framework, include an import statement – #import at the top of any relevant source files.
2. Declaring the Protocols Your App Supports
The External Accessory framework provides support for communicating with external hardware connected to an iOS-based device using Bluetooth. Applications that support external accessories must be sure to configure their Info.plist file correctly. Specifically, you must include the UI Supported External Accessory Protocols key to declare the specific hardware protocols your application supports. These keys should be same as the protocols to be supported by the hardware; else your device might not be listed.
3. Communicating with an Accessory
An app communicates with an accessory by creating an EA Session object for managing the accessory interactions. Session objects work with the underlying system to transfer data packets to and from the accessory. Data transfer in your app occurs through NS Input Stream and NS Output Stream objects, which are vended by the session object once the connection is made. To receive data from the accessory, monitor the input stream using a custom delegate object. To send data to the accessory, write data packets to the output stream. The format of the incoming and outgoing data packets is determined by the protocol you use to communicate with the accessory.
In order to list hardware device, we need to code in View Controller.h&.m
#import
#import "EASessionStreamManager.h"
@interface ViewController : UIViewController
@property (strong,nonatomic) EASession *session;
In .m file.
(void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//Here we registering notification when device get connected and disconnected
[[EAAccessoryManager sharedAccessoryManager] registerForLocalNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(accessoryConnected:)
name:EAAccessoryDidConnectNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(accessoryDisconnected:)
name:EAAccessoryDidDisconnectNotification
object:nil];
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(showBTTalbe) userInfo:nil repeats:NO];
// here we calling showBTTalbe method for listing bluetooth devices
}
-(void)showBTTalbe
{
[[EAAccessoryManager sharedAccessoryManager] showBluetoothAccessoryPickerWithNameFilter:nil completion:^(NSError *error) {
if (error) {
NSLog(@"error :%@", error);
}
else{
NSLog(@"You make it! Well done!!!");
}
}];
}
// This method get called when device get connected.
- (void)accessoryConnected:(NSNotification *)notification
{
NSLog(@"EAController::accessoryConnected");
self.session = nil;
EAAccessory *accessory = [[notification userInfo] objectForKey:EAAccessoryKey];
if (accessory)
{
self.session = [[EASession alloc] initWithAccessory:accessory
forProtocol:@"com.AmpedRFTech.Demo"];
//It creates a session for the designated protocol and configures the input and output streams of the session.
if (self.session)
{
[[self.session inputStream] setDelegate:self];
[[self.session inputStream] scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[[self.session inputStream] open]; //Here
[[self.session outputStream] setDelegate:self];
[[self.session outputStream] scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[[self.session outputStream] open];
}
}
}
This method get called when device get disconnected.
- (void)accessoryDisconnected:(NSNotification *)notification
{
EAAccessory *disconnectedAccessory = [[notification userInfo] objectForKey:EAAccessoryKey];
NSLog(@"Disconnected");
}
// This is line used to write command on device
[[self.session outputStream] write:â€ÂYour command†maxLength:"Command length"];
// Handle communications from the streams.This method responds to events from both input and output streams of the accessory
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
NSMutableData *_data;
NSMutableString *fullDataString = [[NSMutableString alloc] init];
switch (eventCode)
{
case NSStreamEventNone:
NSLog(@"NSStreamEventNone");
break;
case NSStreamEventOpenCompleted:
NSLog(@"NSStreamEventOpenCompleted");
break;
case NSStreamEventHasBytesAvailable:
NSLog(@"NSStreamEventHasBytesAvailable");
//Here we read data from input stream when command written on the hardware device.
@try {
if(!_data) {
_data = [NSMutableData data];
}
uint8_t buf[1024];
unsigned int len = 0;
len = [(NSInputStream *)aStream read:buf maxLength:1024];
if(len)
{
[_data appendBytes:(const void *)buf length:len];
NSLog(@"%@",_data);
}
else
{
NSLog(@"no buffer!");
}
}
@catch (NSException *exception) {
NSLog(@"%@",[exception description]);
}
@finally {
}
break;
case NSStreamEventHasSpaceAvailable:
NSLog(@"NSStreamEventHasSpaceAvailable");
break;
case NSStreamEventErrorOccurred: // Do something for Error event
break;
case NSStreamEventEndEncountered: // Do something for End event
break;
default:
NSLog(@"default");
break;
}
}