Выполнение общих периферийных ролевых задач

В последней главе Вы изучили, как выполнить наиболее распространенные типы низкоэнергетических задач Bluetooth с центральной стороны. В этой главе Вы изучаете, как использовать Ядро платформа Bluetooth для выполнения наиболее распространенных типов низкоэнергетических задач Bluetooth с периферийной стороны. Следующие основанные на коде примеры помогут Вам в разработке Вашего приложения реализовывать периферийную роль на своем локальном устройстве. В частности Вы будете учиться как:

Примеры кода, которые Вы находите в этой главе, просты и абстрактны; Вы, возможно, должны внести надлежащие изменения для слияния их в реальное приложение. Более усовершенствованные темы, связанные с реализацией периферийной роли на Вашем локальном устройстве — включая подсказки, приемы и методы наиболее успешной практики — затронуты в более поздних главах, Ядро Фоновая обработка Bluetooth для приложений для iOS и Методы наиболее успешной практики для Установки Вашего Локального устройства как Периферийное устройство.

Запуск периферийного менеджера

Первый шаг в реализации периферийной роли на Вашем локальном устройстве должен выделить и инициализировать периферийный экземпляр менеджера (представленный a CBPeripheralManager объект). Запустите своего периферийного менеджера путем вызова initWithDelegate:queue:options: метод CBPeripheralManager класс, как это:

    myPeripheralManager =
        [[CBPeripheralManager alloc] initWithDelegate:self queue:nil options:nil];

В этом примере, self установлен как делегат получить любые периферийные ролевые события. Когда Вы указываете очередь отгрузки как nil, периферийный менеджер диспетчеризирует периферийные ролевые события с помощью основной очереди.

Когда Вы создаете периферийного менеджера, периферийные вызовы диспетчера peripheralManagerDidUpdateState: метод его объекта делегата. Необходимо реализовать этот метод делегата гарантировать, что низкая энергия Bluetooth поддерживается и доступна для использования на локальном периферийном устройстве. Для получения дополнительной информации о том, как реализовать этот метод делегата, посмотрите Ссылку на протокол CBPeripheralManagerDelegate.

Установка служб и характеристик

Как показано на рисунке 1-7, база данных локального периферийного устройства служб и характеристик организована древовидным способом. Необходимо организовать их этим древовидным способом для установки служб и характеристик на локальном периферийном устройстве. Ваш первый шаг в выполнении этих задач понимает, как идентифицируются службы и характеристики.

Службы и характеристики идентифицируются UUIDs

Службы и характеристики периферийного устройства идентифицируются 128-разрядными специфичными для Bluetooth UUIDs, представленными в Ядре платформа Bluetooth CBUUID объекты. Хотя не все UUIDs, идентифицирующие службу или характеристику, предопределены Специальной группой (SIG) Bluetooth, Bluetooth SIG определил и опубликовал, много обычно использовали UUIDs, сокращенные к 16 битам для удобства. Например, Bluetooth, SIG предопределил 16-разрядный UUID, идентифицирующий службу сердечного ритма как 180D. Этот UUID сокращен от его эквивалентного 128-разрядного UUID, 0000180D 0000 1000 8000 00805F9B34FB, который основывается на Bluetooth, базируют UUID, определяющийся в спецификации Bluetooth 4.0, Объем 3, Часть F, Раздел 3.2.1.

CBUUID класс обеспечивает методы фабрики, делающие намного проще иметь дело с длинным UUIDs при разработке приложения. Например, вместо того, чтобы раздать строковое представление 128-разрядного UUID службы сердечного ритма в Вашем коде, можно просто использовать UUIDWithString метод для создания a CBUUID объект от предопределенного 16-разрядного UUID службы, как это:

    CBUUID *heartRateServiceUUID = [CBUUID UUIDWithString: @"180D"];

Когда Вы создаете a CBUUID объект от предопределенного 16-разрядного UUID, Ядро остальная часть Bluetooth перед заливками 128-разрядного UUID с Bluetooth базируют UUID.

Создайте свой собственный UUIDs для служб поддержки и характеристик

У Вас могут быть службы и характеристики, не идентифицирующиеся предопределенным Bluetooth UUIDs. Если Вы делаете, необходимо генерировать собственный 128-разрядный UUIDs для идентификации их.

Используйте утилиту командной строки uuidgen легко генерировать 128-разрядный UUIDs. Для начала работы откройте окно в Терминале. Затем, для каждой службы и характеристики, которую необходимо идентифицировать с UUID, ввести uuidgen на командной строке для получения уникального 128-разрядного значения в форме строки ASCII, акцентированной дефисами, как в следующем примере:

$ uuidgen
71DA3FD1-7E10-41C1-B16F-4430B506CDE7

Можно тогда использовать этот UUID для создания a CBUUID объект с помощью UUIDWithString метод, как это:

    CBUUID *myCustomServiceUUID =
        [CBUUID UUIDWithString:@"71DA3FD1-7E10-41C1-B16F-4430B506CDE7"];

Создайте свое дерево служб и характеристик

После того, как у Вас есть UUIDs Ваших служб и характеристик (представленный CBUUID объекты), можно создать непостоянные службы и характеристики и организовать их древовидным способом, описанным выше. Например, если у Вас есть UUID характеристики, можно создать непостоянную характеристику путем вызова initWithType:properties:value:permissions: метод CBMutableCharacteristic класс, как это:

    myCharacteristic =
        [[CBMutableCharacteristic alloc] initWithType:myCharacteristicUUID
         properties:CBCharacteristicPropertyRead
         value:myValue permissions:CBAttributePermissionsReadable];

При создании непостоянной характеристики Вы устанавливаете ее свойства, значение и полномочия. Свойства и полномочия, которые Вы устанавливаете, определяют, среди прочего, читаемо ли значение характеристики или writeable, и может ли связанное центральное подписаться на значение характеристики. В этом примере значение характеристики установлено быть читаемым связанным центральным. Для получения дополнительной информации о диапазоне поддерживаемых свойств и полномочиях непостоянных характеристик, посмотрите Ссылку класса CBMutableCharacteristic.

Теперь, когда Вы создали непостоянную характеристику, можно создать непостоянную службу для соединения характеристики с. Для этого вызовите initWithType:primary: метод CBMutableService класс, как показано здесь:

    myService = [[CBMutableService alloc] initWithType:myServiceUUID primary:YES];

В этом примере второй параметр устанавливается на YES, указание, что служба является основной в противоположность вторичному устройству. Основная служба описывает основную функциональность устройства и может быть включена (ссылаемая) другой службой. Вторичная служба описывает службу, которая релевантна только в контексте другой службы, сославшейся на него. Например, основная служба монитора сердечного ритма может быть должна представить данные сердечного ритма от датчика сердечного ритма монитора, тогда как вторичная служба может быть должна представить данные батареи датчика.

После создания службы можно связать характеристику с нею путем установки массива службы характеристик, как это:

    myService.characteristics = @[myCharacteristic];

Публикация служб и характеристик

После создания дерева служб и характеристик следующий шаг в реализации периферийной роли на локальном устройстве публикует их к базе данных устройства служб и характеристик. Эта задача проста выполнить использование Ядра платформа Bluetooth. Вы вызываете addService: метод CBPeripheralManager класс, как это:

    [myPeripheralManager addService:myService];

Когда Вы вызываете этот метод для публикации служб, периферийные вызовы диспетчера peripheralManager:didAddService:error: метод его объекта делегата. Если ошибка происходит, и Ваши службы не могут быть опубликованы, реализовать этот метод делегата получить доступ к причине ошибки, поскольку следующий пример показывает:

- (void)peripheralManager:(CBPeripheralManager *)peripheral
            didAddService:(CBService *)service
                    error:(NSError *)error {
 
    if (error) {
        NSLog(@"Error publishing service: %@", [error localizedDescription]);
    }
    ...

Распространение Ваших служб

При публикации служб и характеристик к базе данных устройства служб и характеристик Вы готовы начать распространять некоторые из них к любому centrals, который может слушать. Поскольку следующий пример показывает, можно распространить некоторые службы путем вызова startAdvertising: метод CBPeripheralManager класс, передающий в словаре (экземпляр NSDictionary) из данных рекламы:

    [myPeripheralManager startAdvertising:@{ CBAdvertisementDataServiceUUIDsKey :
        @[myFirstService.UUID, mySecondService.UUID] }];

В этом примере, единственный ключ в словаре, CBAdvertisementDataServiceUUIDsKey, ожидает как значение массив (экземпляр NSArray) из CBUUID объекты, представляющие UUIDs служб, которые Вы хотите распространить. Возможные ключи, которые можно указать в словаре данных рекламы, детализированы в константах, описанных в Advertisement Data Retrieval Keys в Ссылке на протокол CBCentralManagerDelegate. Однако только два из ключей поддерживаются для периферийных объектов менеджера: CBAdvertisementDataLocalNameKey и CBAdvertisementDataServiceUUIDsKey.

Когда Вы начинаете распространять некоторые данные по Вашему локальному периферийному устройству, периферийные вызовы диспетчера peripheralManagerDidStartAdvertising:error: метод его объекта делегата. Если ошибка происходит, и Ваши службы не могут быть распространены, реализовать этот метод делегата получить доступ к причине ошибки, как это:

- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral
                                       error:(NSError *)error {
 
    if (error) {
        NSLog(@"Error advertising: %@", [error localizedDescription]);
    }
    ...

Как только Вы начинаете рекламные данные, удаленный centrals может обнаружить и инициировать соединение с Вами.

Ответ на запросы чтения и записи от центрального

После того, как Вы будете подключены к одному или более удаленному centrals, можно начать получать чтение или записать запросы от них. Когда Вы делаете, убедиться реагировать на те запросы надлежащим способом. Следующие примеры описывают, как обработать такие запросы.

Когда связанные центральные запросы для чтения значения одной из характеристик, периферийные вызовы диспетчера peripheralManager:didReceiveReadRequest: метод его объекта делегата. Метод делегата поставляет запрос Вам в форме a CBATTRequest объект, имеющий много свойств, которые можно использовать для выполнения запроса.

Например, когда Вы получаете простой запрос для чтения значения характеристики, свойств CBATTRequest объект, который Вы получаете от метода делегата, может использоваться, чтобы удостовериться, что характеристика в базе данных Вашего устройства соответствует ту, которую удаленное центральное указало в исходном запросе чтения. Можно начать реализовывать этот метод делегата, как это:

- (void)peripheralManager:(CBPeripheralManager *)peripheral
    didReceiveReadRequest:(CBATTRequest *)request {
 
    if ([request.characteristic.UUID isEqual:myCharacteristic.UUID]) {
        ...

Если UUIDs характеристик соответствуют, следующий шаг должен удостовериться, что запрос чтения не просит читать из индексной позиции, которая является вне границ значения Вашей характеристики. Поскольку следующий пример показывает, можно использовать a CBATTRequest объект offset свойство для обеспечения запроса чтения не пытается читать вне надлежащих границ:

    if (request.offset > myCharacteristic.value.length) {
        [myPeripheralManager respondToRequest:request
            withResult:CBATTErrorInvalidOffset];
        return;
    }

Принятие смещения запроса проверяется, теперь установите значение характерного свойства запроса (чье значение по умолчанию nil) к значению характеристики Вы создали на своем локальном периферийном устройстве, приняв во внимание смещение запроса чтения:

    request.value = [myCharacteristic.value
        subdataWithRange:NSMakeRange(request.offset,
        myCharacteristic.value.length - request.offset)];

После того, как Вы устанавливаете значение, реагируете на удаленное центральное, чтобы указать, что был успешно обработан запрос. Сделайте так путем вызова respondToRequest:withResult: метод CBPeripheralManager класс, пасуя назад запрос (чье значение Вы обновили), и результат запроса, как это:

    [myPeripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
    ...

Вызовите respondToRequest:withResult: метод точно один раз каждый раз peripheralManager:didReceiveReadRequest: метод делегата вызывают.

Обрабатывание запросов записи от связанного центрального является также прямым. Когда связанное центральное отправляет запрос для записи значения один или больше характеристик, периферийные вызовы диспетчера peripheralManager:didReceiveWriteRequests: метод его объекта делегата. На сей раз метод делегата поставляет запросы Вам в форме массива, содержащего один или больше CBATTRequest объекты, каждый представляющий запрос записи. После того, как Вы гарантировали, что запрос записи может быть обработан, можно записать значение характеристики, как это:

    myCharacteristic.value = request.value;

Несмотря на то, что вышеупомянутый пример не демонстрирует это, убедиться принять во внимание свойство смещения запроса при записи значения характеристики.

Так же, как Вы реагируете на запрос чтения, вызовите respondToRequest:withResult: метод точно один раз каждый раз peripheralManager:didReceiveWriteRequests: метод делегата вызывают. Однако первый параметр respondToRequest:withResult: метод ожидает сингл CBATTRequest объект, даже при том, что Вы, возможно, получили массив, содержащий больше чем одного из них от peripheralManager:didReceiveWriteRequests: метод делегата. Необходимо передать в первом запросе массива, как это:

    [myPeripheralManager respondToRequest:[requests objectAtIndex:0]
        withResult:CBATTErrorSuccess];

Отправка обновленных характеристических значений к подписанному Centrals

Часто, соединенный centrals подпишется на один или больше Ваших характеристических значений, как описано в Подписке на Значение Характеристики. Когда они делают, Вы ответственны за отправку им уведомления, когда значение характеристики они подписались на изменения. Следующие примеры описывают как.

Когда связанное центральное подписывается на значение одной из Ваших характеристик, периферийные вызовы диспетчера peripheralManager:central:didSubscribeToCharacteristic: метод его объекта делегата:

- (void)peripheralManager:(CBPeripheralManager *)peripheral
                  central:(CBCentral *)central
didSubscribeToCharacteristic:(CBCharacteristic *)characteristic {
 
    NSLog(@"Central subscribed to characteristic %@", characteristic);
    ...

Используйте вышеупомянутый метод делегата в качестве сигнала, чтобы начать отправлять центральные обновленные значения.

Затем, получите обновленное значение характеристики и отправьте его в центральное путем вызова updateValue:forCharacteristic:onSubscribedCentrals: метод CBPeripheralManager класс.

    NSData *updatedValue = // fetch the characteristic's new value
    BOOL didSendValue = [myPeripheralManager updateValue:updatedValue
        forCharacteristic:characteristic onSubscribedCentrals:nil];

При вызове этого метода для отправки обновленных характеристических значений в подписанный centrals, можно указать, какой centrals Вы хотите обновить в последнем параметре. Как в вышеупомянутом примере, если Вы указываете nil, все соединились и подписались, centrals обновляются (и любой соединил centrals, не подписавшиеся, проигнорированы).

updateValue:forCharacteristic:onSubscribedCentrals: метод возвращает булево значение, указывающее, было ли обновление успешно отправлено в подписанный centrals. Если базовая очередь, возвраты метода, использующаяся для передачи обновленного значения полна NO. Периферийный менеджер тогда вызывает peripheralManagerIsReadyToUpdateSubscribers: когда больше пространства в очереди передачи становится доступным, метод его делегата возражает. Можно тогда реализовать этот метод делегата снова послать значение, снова с помощью updateValue:forCharacteristic:onSubscribedCentrals: метод.