Het Core Bluetooth (CB) -raamwerk biedt de hulpmiddelen die uw iOS-apps nodig hebben om te communiceren met apparaten die zijn uitgerust met Bluetooth low energy (BTLE) -technologie. Deze tutorial begeleidt je bij de evolutie van CB van iOS 5 naar iOS 7. Je leert ook hoe je een Core Bluetooth-centrale en -randapparatuur kunt configureren, hoe je ertussen kunt communiceren en wat de beste programmeermethoden zijn wanneer je met een CB werkt..
De Core Bluetooth-zelfstudies zijn verdeeld in twee delen. De eerste behandelt het theoretische aspect van Core Bluetooth, terwijl deze tutorial een complete praktische les is. Je vindt de volledige broncode bij dit bericht.
Het doel van deze zelfstudie is om u te leren hoe u het Core Bluetooth-framework gebruikt. We hebben een voorbeeldbroncode gemaakt die uw leven gemakkelijker zal maken en omzeilen de configuratie van het project en de weergave. Download de voorbeeldcode aan het begin van deze pagina.
We gaan ervan uit dat je de basisprincipes van Xcode en iOS kent, omdat we ons alleen op de Core Bluetooth-gegevens zullen concentreren. De voorbeeldcode bevat het volgende:
ViewController
met twee knoppenCBCentralManagerViewController
waarmee een aangepaste iBeacon wordt gemaaktCBPeripheralViewController
die de iBeacon ontvangt en inherente informatieDIENSTEN
header-bestand met enkele variabelen om te gebruiken in de hele app.Alle weergaven zijn al aanwezig en correct gedefinieerd. U hoeft alleen de code toe te voegen voor het Core Bluetooth-proces. Open het project, voer het uit en speel met de objecten om vertrouwd te raken met de code.
De SERVICES.h
bestand bevat twee unieke UUID's. Die werden gegenereerd met behulp van de opdracht terminal uuidgen
. Je moet ze naar je app genereren, of je kunt ze gebruiken.
Merk op dat deze les twee iOS-apparaten nodig heeft om goed te werken. Rennen
het project en je ziet een soortgelijke interface:
In deze tutorial centreert u de CBCentralManagerViewController
klasse. De eerste stap is om de twee protocollen toe te voegen die de CBCentralManager
en CBPeripheral
. De verklaring van die protocollen definieert methoden (daarover later meer). Jouw interface
zou zo moeten zijn:
@interface CBCentralManagerViewController: UIViewController < CBCentralManagerDelegate, CBPeripheralDelegate>
Nu moet u drie eigenschappen definiëren: CBCentralManager
, CBPeripheral
, en NSMutableData
. De eerste twee liggen voor de hand, en de laatste wordt gebruikt voor het opslaan van informatie die wordt gedeeld tussen apparaten.
@property (strong, nonatomic) CBCentralManager * centralManager; @property (strong, nonatomic) CBPeripheral * detectedPeripheral; @property (strong, nonatomic) NSMutableData * data;
Op dit punt kunt u omschakelen naar het implementatiebestand. U zult een waarschuwing zien, maar voordat u dit oplost, moet u de centralManger
en de gegevens
voorwerpen. Je zou moeten beginnen met centralManager
met een zelfgemachtigde en zonder wachtrij. Gebruik de viewDidLoad
methode en het resultaat zou er ongeveer zo uit moeten zien:
_centralManager = [[CBCentralManager alloc] initWithDelegate: self queue: nil]; _data = [[NSMutableData alloc] init];
Om het waarschuwingsprobleem op te lossen, moet u de - (void) centralManagerDidUpdateState: (CBCentralManager *) centraal
methode.
Het is een vereiste protocolmethode. Het controleert de status van het apparaat en handelt dienovereenkomstig. Er zijn verschillende mogelijke toestanden en in uw toepassing moet u altijd controleren op hen. De staten zijn:
Als u deze toepassing bijvoorbeeld uitvoert op een apparaat zonder Bluetooth 4.0, krijgt u de CBCentralManagerStateUnsupported
code. Hier ga je voor de CBCentralManagerStatePoweredOn
en wanneer het zich voordoet, begin je te scannen naar apparaten. Gebruik daarvoor de scanForPeripheralsWithServices
methode. Als u nul als het eerste argument doorgeeft, de CBCentralManager
begint naar elke dienst te zoeken. Hier gebruikt u de UUID die is opgeslagen in de SERVICES.h
.
De volledige methode is:
- (void) centralManagerDidUpdateState: (CBCentralManager *) central // Je moet alle scenario's testen als (central.state! = CBCentralManagerStatePoweredOn) return; if (central.state == CBCentralManagerStatePoweredOn) // Scannen naar apparaten [_centralManager scanForPeripheralsWithServices: @ [[CBUUID UUIDWithString: TRANSFER_SERVICE_UUID]] opties: @ CBCentralManagerScanOptionAllowDuplicatesKey: @YES]; NSLog (@ "Scanning started");
Op dit moment zoekt uw app naar andere apparaten. Maar ondanks het feit dat er geen of geen beschikbaar is, krijgt u geen informatie. We kunnen dat oplossen. Je zou het moeten toevoegen - (void) centralManager: (CBCentralManager *) central didDiscoverPeripheral: (CBPeripheral *) peripheral advertisementData: (NSDictionary *) advertisementData RSSI: (NSNumber *) RSSI
methode. Het wordt gebeld wanneer een apparaat wordt ontdekt. U programmeert het echter alleen om te reageren op randapparatuur die reclame voor de TRANSFER_SERVICE_UUID
.
Daarnaast zullen we het nieuwe cachesysteem gebruiken en het apparaat opslaan voor toekomstig gebruik en snellere communicatie. De volledige broncode is als volgt:
- (void) centralManager: (CBCentralManager *) central didDiscoverPeripheral: (CBPeripheral *) peripheral advertisementData: (NSDictionary *) advertisementData RSSI: (NSNumber *) RSSI NSLog (@ "Discovered% @ at% @", peripheral.name, RSSI) ; if (_discoveredPeripheral! = peripheral) // Bewaar een lokale kopie van het randapparaat, zodat CoreBluetooth er niet vanaf komt _discoveredPeripheral = peripheral; // En sluit NSLog aan (@ "Verbinden met randapparaat% @", randapparaat); [_centralManager connectPeripheral: randapparatuuropties: nul];
De verbinding met dat apparaat kan mislukken. We moeten dat scenario behandelen met een specifieke methode genaamd: - (void) centralManager: (CBCentralManager *) central didFailToConnectPeripheral: (CBPeripheral *) peripheral error: (NSError *) fout
. Voeg het toe en informeer de gebruiker over die fout.
- (void) centralManager: (CBCentralManager *) central didFailToConnectPeripheral: (CBPeripheral *) peripheral error: (NSError *) error NSLog (@ "Failed to connect"); [zelfopruiming];
U zult een waarschuwing opmerken, aangezien de opruimen
methode is nog niet gedeclareerd. Laten we het verklaren! Op dit punt vindt u de broncode van de methode mogelijk gecompliceerd. We zullen het later echter wel uitleggen. Je moet er aan het eind van de tutorial naar terugkeren voor een volledig begrip.
Met deze methode annuleert u alle abonnementen op een extern apparaat (als die er zijn) of verbreekt u rechtstreeks verbreekt als dat niet het geval is. Het volgt de services, vervolgens de kenmerken en verwijdert de bindingen. De volledige methode is:
- (void) opruimen // Kijk of we geabonneerd zijn op een kenmerk op het randapparaat als (_ontdekte peripheral.services! = nihil) voor (CBService * -service in _detectedPeripheral.services) if (service.characteristics! = nil) voor (CBCaracteristic * kenmerk in service.characteristics) if ([kenmerk.UUID isEqual: [CBUUID UUIDWithString: TRANSFER_CHARACTERISTIC_UUID]]) if (characteristic.isNotifying) [_doverspecifieke setNotifyValue: NO voorKarakteristiek: kenmerk]; terug te keren; [_centralManager cancelPeripheralConnection: _discoveredPeripheral];
In aanmerking nemend dat we succesvol verbonden zijn met het apparaat, moeten we nu de services en kenmerken ervan ontdekken. U moet de - (void) centralManager: (CBCentralManager *) central didConnectPeripheral: (CBPeripheral *) randapparatuur
. Nadat de verbinding tot stand is gebracht, stopt u het scanproces. Wis vervolgens de gegevens die we mogelijk hebben ontvangen. Zorg er vervolgens voor dat u de callbacks voor het zoeken krijgt en zoek uiteindelijk naar services die overeenkomen met uw UUID (TRANSFER_SERVICE_UUID
). Hier is de code:
- (void) centralManager: (CBCentralManager *) central didConnectPeripheral: (CBPeripheral *) peripheral NSLog (@ "Connected"); [_centralManager stopScan]; NSLog (@ "Scanning stopped"); [_data setLength: 0]; peripheral.delegate = zelf; [peripheral discoverServices: @ [[[CBUUID UUIDWithString: TRANSFER_SERVICE_UUID]]];
Op dit punt begint het randapparaat zijn gedelegeerde te waarschuwen met verschillende callbacks. Een van die callbacks is de - (void) peripheral: (CBPeripheral *) peripheral didDiscoverServices: (NSError *) fout
. Het wordt gebruikt om de kenmerken van een bepaalde dienst te ontdekken. Niet dat je altijd moet controleren of die methode een fout oplevert. Als er geen fout wordt gevonden, zou u de kenmerken moeten vinden die u nodig hebt, in dit geval de TRANSFER_CHARACTERISTIC_UUID
. Hier is de volledige methode:
- (void) peripheral: (CBPeripheral *) peripheral didDiscoverServices: (NSError *) error if (error) [self cleanup]; terug te keren; for (CBService * service in peripheral.services) [perifere discoverCharacteristics: @ [[CBUUID UUIDWithString: TRANSFER_CHARACTERISTIC_UUID]] forService: service]; // Ontdek andere kenmerken
Op dit punt als alles correct is, is de overdrachtskenmerk ontdekt. Nogmaals, een gedelegeerde methode wordt genoemd: - (void) peripheral: (CBPeripheral *) peripheral didDiscoverCharacteristicsForService: (CBService *) servicefout: (NSError *) fout
. Zodra dit is gevonden, wil je je erop abonneren, waardoor je CBCentralManager
ontvang de gegevens van dat randapparaat.
Nogmaals, u moet omgaan met fouten (indien aanwezig). Je kunt een sprong van geloof maken en je direct abonneren op het kenmerk. U moet echter door de array van kenmerken lopen en controleren of de karakteristiek de juiste is. Als dat zo is, abonneer je er dan op. Zodra dit is voltooid, hoeft u alleen maar te wachten tot de gegevens binnenkomen (een andere methode). De volledige methode is hieronder.
- (void) peripheral: (CBPeripheral *) peripheral didDiscoverCharacteristicsForService: (CBService *) servicefout: (NSError *) error if (error) [self cleanup]; terug te keren; for (CBCreferistic * kenmerk in service.characteristics) if ([kenmerk.UUID isEqual: [CBUUID UUIDWithString: TRANSFER_CHARACTERISTIC_UUID]]) [perifere setNotifyValue: YES voorKarakteristiek: kenmerk];
Telkens wanneer het randapparaat nieuwe gegevens verzendt, gebruikt de perifere deelnemer de - (void) peripheral: (CBPeripheral *) peripheral didUpdateValueForCharacteristic: (CBCharacteristic *) karakteristieke fout: (NSError *) fout
methode. Het tweede argument bevat de eigenschap die u kunt lezen.
In eerste instantie maakt u een NSString
om de karakteristieke waarde op te slaan. Vervolgens controleert u of de ontvangen gegevens compleet zijn of dat er meer worden afgeleverd. Tegelijkertijd zal je je updaten tekstweergave
zodra nieuwe gegevens zijn ontvangen. Nadat alle gegevens zijn voltooid, kunt u de karakteristiek loskoppelen en de verbinding met het apparaat verbreken (hoewel u verbonden kunt blijven).
Merk op dat u na de inkomende gegevens kunt loskoppelen of wachten op andere gegevens. Met deze terugbelfunctie weten we of er meer gegevens zijn ontvangen via een melding over het kenmerk. De volledige bron is hieronder:
- (void) peripheral: (CBPeripheral *) peripheral didUpdateValueForCharacteristic: (CBCharacteristic *) karakteristieke fout: (NSError *) error if (error) NSLog (@ "Error"); terug te keren; NSString * stringFromData = [[NSString-allocatie] initWithData: kenmerk.value-codering: NSUTF8StringEncoding]; // Hebben we alles wat we nodig hebben? if ([stringFromData isEqualToString: @ "EOM"]) [_textview setText: [[NSString-allocatie] initWithData: self.data-codering: NSUTF8StringEncoding]]; [peripheral setNotifyValue: NO forCharacteristic: characteristic]; [_centralManager cancelPeripheralConnection: peripheral]; [_data appendData: characteristic.value];
Bovendien is er een methode die ervoor zorgt dat de CBCentral
weet wanneer een meldingsstatus voor een gegeven kenmerk verandert. Het is erg belangrijk om het te volgen om te begrijpen wanneer een kenmerkende status verandert (update app-waarden). De methode is: - (void) peripheral: (CBPeripheral *) peripheral didUpdateNotificationStateForCharacteristic: (CBCharacteristic *) karakteristieke fout: (NSError *) fout
. Controleer of de karakteristieke melding is gestopt. Als dit het geval is, moet u de verbinding verbreken:
- (void) peripheral: (CBPeripheral *) peripheral didUpdateNotificationStateForCharacteristic: (CBCharacteristic *) karakteristieke fout: (NSError *) error if (! [characteristic.UUID isEqual: [CBUUID UUIDWithString: TRANSFER_CHARACTERISTIC_UUID]]) return; if (characteristic.isNotifying) NSLog (@ "Melding begon op% @", kenmerk); else // Melding is gestopt [_centralManager cancelPeripheralConnection: peripheral];
Als de verbinding tussen apparaten wordt verbroken, moet u uw lokale kopie van het randapparaat opruimen. Gebruik daarvoor de - (void) centralManager: (CBCentralManager *) central didDisconnectPeripheral: (CBPeripheral *) peripheral error: (NSError *) fout
methode. Deze methode is eenvoudig en stelt het randapparaat op nul. Bovendien kunt u het scannen van het apparaat opnieuw starten of de app (of een andere) verlaten. In dit voorbeeld start u het scanproces opnieuw.
- (void) centralManager: (CBCentralManager *) central didDisconnectPeripheral: (CBPeripheral *) peripheral error: (NSError *) error _discoveredPeripheral = nil; [_centralManager scanForPeripheralsWithServices: @ [[[CBUUID UUIDWithString: TRANSFER_SERVICE_UUID]] opties: @ CBCentralManagerScanOptionAllowDuplicatesKey: @YES];
Ten slotte is nog een extra stap vereist. Telkens wanneer het beeld verdwijnt, moet u het scanproces stoppen. In de viewWillDisappear: (BOOL) geanimeerde
methode die u moet toevoegen:
[_centralManager stopScan];
Jij kan Rennen
de app, maar je hebt de perifere app nodig om bepaalde gegevens te ontvangen. De volgende afbeelding presenteert de laatste interface van de CBCentralManager
.
In deze tutorial centreert u de CBPeripheralViewController
klasse. De eerste stap is om twee protocollen toe te voegen: CBPeripheralManagerDelegate en UITextViewDelegate. Jouw interface
zou er nu moeten uitzien als:
@interface CBPeripheralViewController: UIViewController < CBPeripheralManagerDelegate, UITextViewDelegate>
U moet nu vier eigenschappen definiëren: CBPeripheralManager
, CBMutableCharacteristic
, NSData
, en NSInterger
. De eerste twee vertegenwoordigen de perifere manager en zijn kenmerken, terwijl de derde de gegevens zijn die zullen worden verzonden. De laatste vertegenwoordigt de gegevensindex.
@property (strong, nonatomic) CBPeripheralManager * peripheralManager; @property (strong, nonatomic) CBMutableCharacteristic * transferCharacteristic; @property (strong, nonatomic) NSData * dataToSend; @property (nonatomic, readwrite) NSInteger sendDataIndex;
Schakel nu over naar het implementatiebestand. Onze eerste stap is om de _peripheralManager
en configureer het om te beginnen met adverteren. De serviceaankondiging moet beginnen met de bovengenoemde UUID voor de service. Jouw viewDidLoad
zou er als volgt uit moeten zien:
- (void) viewDidLoad [super viewDidLoad]; _peripheralManager = [[CBPeripheralManager alloc] initWithDelegate: self queue: nil]; [_peripheralManager startAdvertising: @ CBAdvertisementDataServiceUUIDsKey: @ [[[CBUUID UUIDWithString: TRANSFER_SERVICE_UUID]]];
Je zou een waarschuwing moeten zien. Om het te repareren, verklaar de - (void) peripheralManagerDidUpdateState: (CBPeripheralManager *) randapparaat
protocol methode. Gelijkwaardig aan CBCentralManager
je zou alle app-toestanden moeten controleren en testen. Als de staat is CBPeripheralManagerStatePoweredOn
je zou je service en karakteristieken moeten bouwen en definiëren (een van de ware kenmerken van iOS 7).
Elke service en elk kenmerk moet worden geïdentificeerd door een unieke UUID. Merk op dat het derde argument van de methode init niets uitmaakt. Hiermee wordt verklaard dat de gegevens die moeten worden uitgewisseld later worden gedefinieerd. Dit wordt meestal gedaan wanneer u de gegevens dynamisch wilt maken. Als u een statische waarde wilt verzenden, kunt u deze hier aangeven.
De eigenschappen bepalen hoe de kenmerkwaarde kan worden gebruikt en er zijn verschillende mogelijke waarden:
Voor een volledig begrip van die eigenschappen, moet u de CBC-kenmerkende klassenreferentie controleren.
Het laatste argument van de init zijn de lees-, schrijf- en coderingsrechten voor een kenmerk. Nogmaals, er zijn verschillende mogelijke waarden:
Nadat het kenmerk het heeft gedefinieerd, is het nu tijd om de service te definiëren met behulp van de CBMutableService
. Merk op dat de service moet worden gedefinieerd met de TRANSFER_CHARACTERISTIC_UUID
. Voeg het kenmerk toe aan de service en voeg het vervolgens toe aan de randbeheerfunctie. De volledige methode is hieronder:
- (void) peripheralManagerDidUpdateState: (CBPeripheralManager *) peripheral if (peripheral.state! = CBPeripheralManagerStatePoweredOn) return; if (peripheral.state == CBPeripheralManagerStatePoweredOn) self.transferCharacteristic = [[CBMutableCharacteristic alloc] initWithType: [CBUUID UUIDWithString: TRANSFER_CHARACTERISTIC_UUID] eigenschappen: CBCharacteristicPropertyNotify waarde: nil permissions: CBAttributePermissionsReadable]; CBMutableService * transferService = [[CBMutableService alloc] initWithType: [CBUUID UUIDWithString: TRANSFER_SERVICE_UUID] primary: YES]; transferService.characteristics = @ [_ transferCharacteristic]; [_peripheralManager addService: transferService];
Nu we de service en de bijbehorende kenmerken hebben (één in dit geval), is het nu tijd om te detecteren wanneer een apparaat verbinding maakt met deze en dienovereenkomstig reageert. De - (void) peripheralManager: (CBPeripheralManager *) perifere centrale: (CBCentral *) central didSubscribeToCharacteristic: (CBCkarakteristiek *) kenmerk
methode vangt wanneer iemand zich op onze karakteristiek abonneert en begint dan met het verzenden van gegevens.
De app verzendt de gegevens die beschikbaar zijn op de tekstweergave
. Als de gebruiker de app wijzigt, stuurt de app deze in realtime naar de centrale waarop wordt geabonneerd. De methode roept een aangepaste methode genaamd verstuur data
.
- (void) peripheralManager: (CBPeripheralManager *) perifere centrale: (CBCentral *) central didSubcribeToCharacteristic: (CBCkarakteristiek *) kenmerk _dataToSend = [_textView.text dataUsingEncoding: NSUTF8StringEncoding]; _sendDataIndex = 0; [zelf sendData];
De verstuur data
is de methode die alle logica behandelt met betrekking tot de gegevensoverdracht. Het kan verschillende acties uitvoeren, zoals:
De volledige broncode wordt hieronder weergegeven. Verschillende opmerkingen zijn met opzet achtergelaten om het begrip ervan te vergemakkelijken.
- (void) sendData static BOOL sendingEOM = NO; // einde bericht? if (sendingEOM) BOOL didSend = [self.peripheralManager updateValue: [@ "EOM" dataUsingEncoding: NSUTF8StringEncoding] forChitchisticistic: self.transferKarakteristiek onSubcribedCentrals: nil]; if (didSend) // Dat deed het, dus markeer het als verstuurde sendingEOM = NO; // heeft niet verzonden, dus we sluiten af en wachten op peripheralManagerIsReadyToUpdateSubscribers om sendData opnieuw te bellen; // We verzenden gegevens // Is er nog ruimte over om te verzenden? if (self.sendDataIndex> = self.dataToSend.length) // Geen gegevens over. Niets terugkeren; // Er zijn gegevens over, dus verzend totdat het terugbellen mislukt of we zijn klaar. BOOL didSend = YES; while (didSend) // Ga na hoe groot het moet zijn NSInteger amountToSend = self.dataToSend.length - self.sendDataIndex; // Kan niet langer zijn dan 20 bytes als (amountToSend> NOTIFY_MTU) amountToSend = NOTIFY_MTU; // Kopieer de gegevens die we willen NSData * chunk = [NSData dataWithBytes: self.dataToSend.bytes + self.sendDataIndex length: amountToSend]; didSend = [self.peripheralManager updateValue: chunk forCharacteristic: self.transferCharacteristic onSubcribedCentrals: nil]; // Als het niet werkte, stop dan en wacht op de callback als (! DidSend) return; NSString * stringFromData = [[NSString-allocatie] initWithData: chunk-codering: NSUTF8StringEncoding]; NSLog (@ "Sent:% @", stringFromData); // Het heeft wel verzonden, dus update onze index self.sendDataIndex + = amountToSend; // Was het de laatste? if (self.sendDataIndex> = self.dataToSend.length) // Stel dit in als het verzenden mislukt, we zullen het de volgende keer verzenden sendingEOM = YES; BOOL eomSent = [self.peripheralManager updateValue: [@ "EOM" dataEncoding: NSUTF8StringEncoding] voorKarakteristiek: self.transferKarakteristiek opSubcribedCentrals: nihil]; if (eomSent) // Het stuurde, we zijn klaar met verzendenEOM = NO; NSLog (@ "Verzonden: EOM"); terug;
Ten slotte moet u een callback definiëren die wordt aangeroepen wanneer de PeripheralManager
is klaar om het volgende stuk gegevens te verzenden. Dit zorgt ervoor dat pakketten binnenkomen in de volgorde waarin ze zijn verzonden. De methode is de - (void) peripheralManagerIsReadyToUpdateSubscribers: (CBPeripheralManager *) randapparatuur
en het roept alleen het verstuur data
methode. De volledige versie is hieronder:
- (void) peripheralManagerIsReadyToUpdateSubscribers: (CBPeripheralManager *) peripheral [self sendData];
Je kunt nu eindelijk Rennen
de app en test de Bluetooth-communicatie. De volgende afbeelding toont de interface van de CBCentralManager
.
Aan het einde van deze tutorial moet u de Core Bluetooth-raamwerkspecificaties begrijpen. U moet ook een CBCentralManager en een CBPeripheral-rol kunnen definiëren en configureren en enkele best practices kunnen begrijpen en toepassen bij het ontwikkelen met Core Bluetooth.
Als u vragen of opmerkingen heeft, laat ze hieronder!
Als je vaak met de iOS-SDK werkt, bekijk dan Envato Market om honderden handige en tijdbesparende iOS-app-sjablonen te vinden.