Reprinted please indicate the source
Author: Pony
I 've been learning IOS for a while, so it's time to get something done. Some time ago I studied things related to IOS Bluetooth communication and shared some of the research results.
Project Background
The device is a financial card reader that communicates with the iphone through Bluetooth. Apps on mobile phones control the card reader to execute some actions by sending different commands (via Bluetooth), such as reading magnetic stripe cards and reading financial IC cards. The following figures are easy to understand:
<喎?http: www.bkjia.com kf ware vc " target="_blank" class="keylink"> VcD4KPHA + PGJyPgo8L3A + CjxwPjxpbWcgc3JjPQ = "http://www.2cto.com/uploadfile/Collfiles/20140524/201405240909454.jpg" alt = "\">
After reading the pictures above, you should probably know what this is.
IOS Bluetooth Introduction
The Bluetooth protocol has evolved from 1.0 to 4.0. The latest 4.0 is known for its low power consumption. Therefore, it is also called BLE ).
IOS has two frameworks that support Bluetooth and peripherals. One is ExternalAccessory. It was supported since ios3.0 and is also a relatively large mode that was used before the iPhone 4S came out. However, there is a bad thing about it. External Accessory needs to receive MFI certification from Apple.
Another framework is CoreBluetooth, which is introduced in this article. It is supported in iPhone 4S and is used to communicate with BLE devices (because its APIs are based on BLE ). This does not require MFI, and many bluetooth devices now support 4.0. Therefore, it is also a recommended development method for IOS.
Introduction to tricorebluetooth
The core of CoreBluetooth is peripheral and central, which can be understood as peripherals and centers. They have a set of related APIs and classes, as shown in:
If the device you want to program is central, most of them will be used, and vice versa. In our example, the financial card reader is peripheral and our iphone is central, so I will use most of the classes on the left. There are also many examples of peripheral programming, such as using an ipad to communicate with an iphone, the ipad can be considered central, and the iphone side is peripheral, in this case, you need to use the classes on the right side of the iphone.
4. Services and features
It is necessary to describe the concept first. What is service and characteristic )?
Each Bluetooth 4.0 device presents itself through services and features. A device must contain one or more services. Each Service contains several features. A feature is the smallest unit of external interaction. For example, A Bluetooth 4.0 device uses feature A to describe its factory information and Feature B to send and receive data.
Services and features are uniquely identified by UUID. If you are not clear about the UUID concept, google. The International Bluetooth organization is a typical device (such as a device that measures heartbeat and blood pressure) it specifies the standard service UUID (the UUID with many features is not listed here), as follows:
#define BLE_UUID_ALERT_NOTIFICATION_SERVICE 0x1811 #define BLE_UUID_BATTERY_SERVICE 0x180F #define BLE_UUID_BLOOD_PRESSURE_SERVICE 0x1810 #define BLE_UUID_CURRENT_TIME_SERVICE 0x1805 #define BLE_UUID_CYCLING_SPEED_AND_CADENCE 0x1816 #define BLE_UUID_DEVICE_INFORMATION_SERVICE 0x180A #define BLE_UUID_GLUCOSE_SERVICE 0x1808 #define BLE_UUID_HEALTH_THERMOMETER_SERVICE 0x1809 #define BLE_UUID_HEART_RATE_SERVICE 0x180D #define BLE_UUID_HUMAN_INTERFACE_DEVICE_SERVICE 0x1812 #define BLE_UUID_IMMEDIATE_ALERT_SERVICE 0x1802 #define BLE_UUID_LINK_LOSS_SERVICE 0x1803 #define BLE_UUID_NEXT_DST_CHANGE_SERVICE 0x1807 #define BLE_UUID_PHONE_ALERT_STATUS_SERVICE 0x180E #define BLE_UUID_REFERENCE_TIME_UPDATE_SERVICE 0x1806 #define BLE_UUID_RUNNING_SPEED_AND_CADENCE 0x1814 #define BLE_UUID_SCAN_PARAMETERS_SERVICE 0x1813 #define BLE_UUID_TX_POWER_SERVICE 0x1804 #define BLE_UUID_CGM_SERVICE 0x181A
Of course there are still many devices not in this standard list, such as the financial card reader I use. Hardware vendors of Bluetooth devices usually provide various services and characteristics functions in their devices, such as which functions are used for interaction (read/write ), which can obtain module information (read-only.
5. Implementation Details
To achieve complete communication as a center, follow these steps:
Create a central role-scan peripherals-connect to peripherals-scan services and features in peripherals-perform data interaction with peripherals) -disconnect ).
1. Create a central role
First, you must include the header file of CoreBluetooth in the header file of my own class and inherit the two Protocols. The Code is as follows:
#import
CBCentralManager *manager;manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
2 scan peripherals (discover)
The Code is as follows:
[manager scanForPeripheralsWithServices:nil options:options];
This parameter can also specify the UUID of a specific peripheral, So theoretically this central will only discover this specific device, but I found in my actual test that, if you cannot find any device by passing parameters with a specific UUID, the Code is as follows:
NSArray *uuidArray = [NSArray arrayWithObjects:[CBUUID UUIDWithString:@"1800"],[CBUUID UUIDWithString:@"180A"],[CBUUID UUIDWithString:@"1CB2D155-33A0-EC21-6011-CD4B50710777"],[CBUUID UUIDWithString:@"6765D311-DD4C-9C14-74E1-A431BBFD0652"],nil]; [manager scanForPeripheralsWithServices:uuidArray options:options];
The reason is unclear. It is suspected that it is related to the broadcast package of the device.
3 connect to peripherals)
After scanning 4.0 of devices, the system will use the callback function to tell us the device information, and then we can connect to the corresponding device. The Code is as follows:
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{ if(![_dicoveredPeripherals containsObject:peripheral]) [_dicoveredPeripherals addObject:peripheral]; NSLog(@"dicoveredPeripherals:%@", _dicoveredPeripherals);}
// Connect to the specified device-(BOOL) connect :( CBPeripheral *) peripheral {NSLog (@ "connect start"); _ testPeripheral = nil; [manager connectPeripheral: peripheral options: [NSDictionary dictionaryWithObject: [NSNumber numberWithBool: YES] forKey: timeout]; // enable a timer to monitor connection timeout. connectTimer = [NSTimer scheduledTimerWithTimeInterval: 5.0f target: self selector: @ selector (connectTimeout :) userInfo: peripheral repeats: NO]; return (YES );}
4. Scan for services and features (discover) in peripherals)
Similarly, when the connection is successful, the system will tell us through the callback function, and then we will scan all the services and features under the device in this callback. The Code is as follows:
-(Void) centralManager :( CBCentralManager *) central didConnectPeripheral :( CBPeripheral *) peripheral {[connectTimer invalidate]; // stop clock NSLog (@ "Did connect to peripheral: % @", peripheral); _ testPeripheral = peripheral; [peripheral setDelegate: self]; [peripheral discoverServices: nil];}
Services and features in a device are often many. In most cases, we only care about a few of them. Therefore, we usually care about the services and feature callbacks, for example, the following code:
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{ NSLog(@"didDiscoverServices"); if (error) { NSLog(@"Discovered services for %@ with error: %@", peripheral.name, [error localizedDescription]); if ([self.delegate respondsToSelector:@selector(DidNotifyFailConnectService:withPeripheral:error:)]) [self.delegate DidNotifyFailConnectService:nil withPeripheral:nil error:nil]; return; } for (CBService *service in peripheral.services) { if ([service.UUID isEqual:[CBUUID UUIDWithString:UUIDSTR_ISSC_PROPRIETARY_SERVICE]]) { NSLog(@"Service found with UUID: %@", service.UUID); [peripheral discoverCharacteristics:nil forService:service]; isVPOS3356 = YES; break; } }}
-(Void) peripheral :( CBPeripheral *) peripheral failed :( CBService *) service error :( NSError *) error {if (error) {NSLog (@ "Discovered characteristics for % @ with error: % @ ", service. UUID, [error localizedDescription]); if ([self. delegate respondsToSelector: @ selector (didpolicyfailconnectchar: withPeripheral: error :)]) [self. delegate didpolicyfailconnectchar: nil withPeripheral: nil error: nil]; return ;}for (CBCharacteristic * characteristic in service. characteristics) {if ([characteristic. UUID isEqual: [CBUUID UUIDWithString: UUIDSTR_ISSC_TRANS_TX]) {NSLog (@ "Discovered read characteristics: % @ for service: % @", characteristic. UUID, service. UUID); _ readCharacteristic = characteristic; // Save the read feature if ([self. delegate respondsToSelector: @ selector (DidFoundReadChar :)]) [self. delegate DidFoundReadChar: characteristic]; break ;}for (CBCharacteristic * characteristic in service. characteristics) {if ([characteristic. UUID isEqual: [CBUUID UUIDWithString: UUIDSTR_ISSC_TRANS_RX]) {NSLog (@ "Discovered write characteristics: % @ for service: % @", characteristic. UUID, service. UUID); _ writeCharacteristic = characteristic; // Save the written feature if ([self. delegate respondsToSelector: @ selector (DidFoundWriteChar :)]) [self. delegate DidFoundWriteChar: characteristic]; break ;}} if ([self. delegate respondsToSelector: @ selector (DidFoundCharacteristic: withPeripheral: error :)]) [self. delegate DidFoundCharacteristic: nil withPeripheral: nil error: nil];}
I believe you have noticed that all callback functions start with "did" and you do not need to call these functions. After the conditions are met, the system automatically calls them.
5. Data Interaction with peripherals)
Sending data is simple. We can encapsulate the following function:
// Write data-(void) writeChar :( NSData *) data {[_ testPeripheral writeValue: data forCharacteristic: _ writeCharacteristic type: CBCharacteristicWriteWithResponse];}
_ TestPeripheral and _ writeCharacteristic are the previously stored Device objects and read/write features.
Then we can call it externally. For example, when I want to trigger a card swipe, set the data packet and then call the sending function:
-(void)msrRead{ unsigned char command[512] = {0}; unsigned char *pTmp; int nSendLen = 0; unsigned char ucCrc[3] = {0}; _commandType = COMMAND_MSR_READ; pTmp = command; *pTmp = 0x02;//startpTmp++; *pTmp = 0xc1;//main cmdpTmp++; *pTmp = 0x07;//sub cmdpTmp++; nSendLen = 2; *pTmp = nSendLen/256;pTmp++;*pTmp = nSendLen%256;pTmp++; *pTmp = 0x00;//sub cmdpTmp++; *pTmp = 0x00;//sub cmdpTmp++; Crc16CCITT(command+1,pTmp-command-1,ucCrc);memcpy(pTmp,ucCrc,2); NSData *data = [[NSData alloc] initWithBytes:&command length:9]; NSLog(@"send data:%@", data); [g_BLEInstance.recvData setLength:0]; [g_BLEInstance writeChar:data];}
There are two types of Data reading: reading directly and subscribe ). The differences between the two can also be understood from the name. In actual use, the specific application scenario and the attributes of features are required. What are the attributes of a feature? A feature has a properties field (characteristic. properties), which is an integer value and has the following definitions:
enum { CBCharacteristicPropertyBroadcast = 0x01, CBCharacteristicPropertyRead = 0x02, CBCharacteristicPropertyWriteWithoutResponse = 0x04, CBCharacteristicPropertyWrite = 0x08, CBCharacteristicPropertyNotify = 0x10, CBCharacteristicPropertyIndicate = 0x20, CBCharacteristicPropertyAuthenticatedSignedWrites = 0x40, CBCharacteristicPropertyExtendedProperties = 0x80, };
For example, the properties value of the feature you want to interact with is 0x10, indicating that you can only receive data in subscription mode. Here I use the subscription method. The code for starting the subscription is as follows:
// Listener device-(void) startSubscribe {[_ testPeripheral setpolicyvalue: YES forCharacteristic: _ readCharacteristic];}
When the device returns data, a system callback is also used to notify me, as shown below:
-(Void) peripheral :( CBPeripheral *) peripheral didUpdateValueForCharacteristic :( CBCharacteristic *) characteristic error :( NSError *) error {if (error) {NSLog (@ "Error updating value for characteristic % @ error: % @", characteristic. UUID, [error localizedDescription]); if ([_ response respondsToSelector: @ selector (didpolicyreaderror :)]) [_ nvididpolicyreaderror: error]; return;} [_ recvData appendData: characteristic. value]; if ([_ recvData length]> = 5) // received length {unsigned char * buffer = (unsigned char *) [_ recvData bytes]; int nLen = buffer [3] * 256 + buffer [4]; if ([_ recvData length] = (nLen + 3 + 2 + 2) {// After receiving, notify the proxy to do things if ([_ mainMenuDelegate respondsToSelector: @ selector (didpolicyreaddata)]) [_ mainMenuDelegate didpolicyreaddata];}
6. disconnect)
This is relatively simple. You only need an API. The Code is as follows:
// Actively disConnect the device-(void) disConnect {if (_ testPeripheral! = Nil) {NSLog (@ "disConnect start"); [manager cancelPeripheralConnection: _ testPeripheral] ;}}
6. Results presentation
The last few screenshots show that the UI is not modified. The main focus is on the function, which enables reading the track information and interacting with the financial IC card with APDU.