CoreBluetooth na prática

Tradução prática de CoreBluetooth para periféricos


Alguns anos atrás, quando encontrei o Bluetooth em um projeto em funcionamento, encontrei este artigo, que ajudou bastante a entender como ele funciona, para encontrar um ponto de partida. Espero que seja útil para iniciantes.

Sobre o autor: Yoav Schwartz é um desenvolvedor iOS líder na Donkey Republic, um sistema de compartilhamento de motociclistas em Copenhague, que se esforça para mudar atitudes em relação ao ciclismo. A seguir, falaremos em nome do autor.

Neste artigo, falarei sobre técnicas práticas para trabalhar com o CoreBluetooth. Primeiro, sobre Bluetooth Low Energy (BLE), porque nem todo mundo está familiarizado com essa tecnologia, depois sobre CoreBluetooth, uma estrutura da Apple que nos permite interagir com dispositivos BLE. Também vou falar sobre alguns dos truques de desenvolvimento que eu mesmo aprendi ao depurar, chorar e arrancar cabelos da minha cabeça.

Baixa energia Bluetooth


Para iniciantes, o que é o BLE? É como o Bluetooth, que todos usamos em alto-falantes, fones de ouvido etc., mas há uma diferença - esse protocolo consome muito pouca energia. Normalmente, uma única carga de bateria para um dispositivo habilitado para BLE pode durar meses ou até anos (dependendo, é claro, de como o dispositivo é usado). Isso nos permite fazer coisas anteriormente indisponíveis para o Bluetooth "normal". Esse padrão é chamado Bluetooth 4.0, tudo começou com uma tecnologia chamada Smart Bluetooth, que mais tarde se tornou o BLE. Há um manual de 200 páginas, você pode ler a hora de dormir, leitura emocionante.

O BLE é muito econômico em termos de consumo de energia e o protocolo em si não é muito complexo. Então, por que ble? Como podemos usá-lo? O primeiro e mais comum exemplo é um sensor de frequência cardíaca. Normalmente, este dispositivo mede e transmite sua freqüência cardíaca através do protocolo. Também existem todos os tipos de sensores aos quais você pode se conectar via BLE e ler os dados que eles coletam. Finalmente, existem iBeacons que podem dizer a você "proximidade" de um lugar. Entre aspas, porque nos iPhones, a Apple bloqueia a capacidade de detectar os iBeacons como dispositivos Bluetooth comuns, por isso temos que trabalhar com o CoreLocation. Em geral, esta é a Internet: você pode conectar-se a uma TV ou ar-condicionado e se comunicar com ela usando este protocolo.

Como isso funciona?


Temos um periférico - esses são os dispositivos que usam o protocolo Bluetooth. Cada periférico possui serviços, pode haver qualquer número deles e cada um deles possui características. Você pode considerar o periférico como um servidor. Com todas as conseqüências que se seguem: às vezes é desativado, às vezes leva tempo para transferir dados, e às vezes esses dados não chegam.

Em geral, temos um serviço com muitas características, cada uma contendo um valor, tipo e assim por diante. Para trabalhar com o CoreBluetooth, você não precisa saber tudo, o mais importante é ler os dados. É isso que estamos tentando obter, modificar ou usar para nossos próprios propósitos. Precisamos desses dados e conhecimento do que podemos fazer com eles.

Aqui está uma rápida introdução ao BLE porque existem milhares de recursos que explicam melhor os recursos técnicos do que eu.

Core bluetooth


O Core Bluetooth foi introduzido pela Apple há muito tempo, no iOS 5. A Apple começou a trabalhar na introdução do BLE em seus dispositivos muito antes do Android e da crescente popularidade da tecnologia. Muitos desenvolvedores usam essa estrutura em seus aplicativos, em geral é apenas um invólucro, pois os próprios protocolos BLE são bastante complexos. Não é verdade, mas acredite, isso não é algo que eu gostaria de trabalhar todos os dias. Assim como muitas outras coisas, a Apple o embrulhou em um pacote bonito e conveniente, permitindo que você use termos que todos nós, desenvolvedores estúpidos, possamos entender.

Agora é a vez de dizer o que você realmente precisa saber sobre as aulas envolvidas na comunicação com a estrutura. Nosso principal ator é o CBCentralManager, crie-o:

manager = CBCentralManager(delegate:self, queue:nil, options: nil) 

Acima, criamos um novo gerente, indicando seu delegado, caso contrário, não poderemos usá-lo. Também indicamos a fila, no nosso caso, nula, o que significa que toda a comunicação com o gerente será realizada na fila principal.

Você precisa entender exatamente o que vai fazer - o uso de uma fila separada complicará o aplicativo, mas, é claro, os usuários amarão você mais. Se você planeja se comunicar com apenas um dispositivo, não pode incomodar e usar a fila principal. Se você ainda deseja experimentar, crie uma fila, especifique-a no construtor e não se esqueça de retornar à principal antes de usar os resultados em outro local.

Opções Não há nada de particularmente interessante aqui, talvez o principal - quando você cria um gerente e o bluetooth está desativado para o usuário - o aplicativo o informa sobre isso, mas quase todo mundo clica em "OK" (o que realmente não inclui bluetooth), e é por isso que também estou usando esta opção Eu não uso

Primeiro de tudo, depois de criar o gerente, o delegado chama o método:

 func centralManagerDidUpdateState(_ central: CBCentralManager) 

Portanto, obtemos uma resposta do hardware - se o usuário tem o Bluetooth ativado ou não.
Primeira dica: o gerente é inútil até obtermos uma resposta de que o bluetooth está ativado, seu estado é. Outros estados podem ser usados ​​apenas para solicitar ao usuário que ligue o bluetooth.

Pesquisa de dispositivos


Agora que nosso gerente está funcionando corretamente, podemos assistir,
o que está ao nosso redor (depois de receber o estado .PoweredOn - chamamos a função scanForPeripheralsWithServices :)

 manager.scanForPeripheralsWithServices([CBUUID], options: nil) 

Quanto aos serviços, é uma variedade de CBUUIDs (uma classe que é um identificador universal universal de 128 bits para os atributos usados ​​pelo Bluetooth Low Energy aprox. Por.), Que usamos como um filtro para encontrar dispositivos com apenas esse conjunto de UIDs, é uma prática comum no CoreBluetooth .

Se você passar nulo como argumento, podemos ver todos os dispositivos ao redor. Para o desempenho, é claro, é melhor especificar uma matriz dos parâmetros que precisamos, mas no caso em que você não os conhece, nada de terrível acontecerá se você passar nulo, ninguém morre.

Desde que lançamos a pesquisa de dispositivos, devemos interrompê-lo. Caso contrário, a pesquisa continuará e aterrará a bateria do usuário até que a paremos. Assim que encontrarmos o dispositivo certo ou a necessidade da pesquisa desaparecer, pararemos:

 manager.stopScan() 

Cada vez que um novo dispositivo é descoberto, o representante do gerente chama a função didDiscoverPeripheral na fila que especificamos quando foi inicializado. A função nos transmite o dispositivo encontrado (periférico), informações sobre ele (advertisementData - algo que os desenvolvedores de chips decidiram mostrar cada vez) e o nível de sinal RSSI relativo em decibéis.

 func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) 

Segunda dica: sempre mantenha um forte vínculo com o perifer detectado. Se isso não for feito, o sistema decidirá que não precisamos do dispositivo encontrado e o descartará. Ela vai se lembrar dele, mas não teremos mais acesso a ele. Caso contrário, não poderemos trabalhar com o dispositivo.

Conexão do dispositivo


Encontramos o dispositivo em que estamos interessados ​​- é assim que vamos a uma festa e vemos uma garota bonita. Queremos conectar, chamamos a função connectPeripheral - oferecemos "compre uma bebida". Assim, tentamos conectar ao dispositivo desejado (periférico), e ele pode nos dizer "sim" ou "não", mas nosso iPhone é realmente bom, portanto, ouviremos uma resposta positiva.

 manager.connectPeripheral(peripheral, options: nil) 

Aqui, nos voltamos para o gerente responsável pelas conexões, dizemos a ele a qual dispositivo específico estamos nos conectando e novamente damos nada às opções (se você está realmente muito interessado em aprender sobre as opções, leia a documentação, mas geralmente pode fazê-las sem elas). Quando você terminar de trabalhar com o dispositivo, poderá desconectá-lo, bem, de manhã - cancelPeripheralConnection:

 //called to cancel and/or disconnect manager.cancelPeripheralConnection(peripheral) 

Depois de conectar ou desconectar, o delegado nos informará sobre isso:

 //didConnect func centralManager(central: CBCentralManager!, didConnectPeripheral peripheral: CBPeripheral!) //didDisconnect func centralManager(central: CBCentralManager!, didDisconnectPeripheral peripheral: CBPeripheral!, error: NSError!) 

Agora, mais duas dicas importantes. O protocolo Bluetooth pressupõe um tempo limite de conexão, mas a Apple não se importa. O iOS tentará se conectar novamente e não será interrompido até você ligar para cancelPeripheralConnection. Esse processo pode demorar muito, portanto é necessário limitá-lo a tempo e, se no final não recebermos uma mensagem de conexão bem-sucedida (didConnectPeripheral), precisamos informar ao usuário que algo deu errado.

Se você não mantiver um vínculo forte com o periférico, o iOS simplesmente redefinirá a conexão. Do ponto de vista dela, isso significa que você não precisa dela, e mantê-la é uma tarefa que consome muita energia da bateria, e sabemos como a Apple se relaciona com o consumo de energia.

Vamos tornar o dispositivo útil


E assim, nos conectamos ao dispositivo, vamos fazer algo com ele. Anteriormente, mencionei serviços e recursos, os valores que eles contêm, é disso que precisamos. Agora temos um dispositivo, ele está conectado e podemos obter seus serviços ligando para periférico.discoverServiços.

 peripheral.discoverServices(nil) func peripheral(peripheral: CBPeripheral!, didDiscoverServices error: NSError!) peripheral.services 

Agora vai parecer um pouco confuso, mas o delegado é chamado no segmento que definimos ao criar o gerente, apesar do fato de ser um delegado para a periferia. Ou seja, o sistema lembra com qual fluxo ele trabalha e toda a nossa comunicação Bluetooth ocorre nesse fluxo. É importante não esquecer de voltar ao principal se você não o tiver usado.

Temos os serviços, mas ainda não temos nada para trabalhar. Em seguida, é necessário chamar periférico.discoverCharacteristics, o delegado nos fornecerá todas as características disponíveis para os serviços recebidos em didDiscoverCharacteristicsForService. Agora podemos ler os valores,
que estão contidos lá (readValueForCharacteristic) ou para pedir para nos informar assim que algo mudar lá - setNotifyValue.

 peripheral.discoverCharacteristics(nil, forService: (service as CBService)) func peripheral(peripheral: CBPeripheral!, didDiscoverCharacteristicsForService service: CBService!, error: NSError!) peripheral.readValueForCharacteristic(characteristic) peripheral.setNotifyValue(true, forCharacteristic: characteristic) func peripheral(peripheral: CBPeripheral!, didUpdateValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!) 

Ao contrário do Android, a Apple não faz distinção entre leitura e notificação. Ou seja, não sabemos o que está acontecendo - estamos lendo algo no dispositivo ou esse dispositivo está nos dizendo algo.

Gravando em um dispositivo


Temos um dispositivo, lemos informações, gerenciamos. Assim, podemos registrar informações sobre ele, como regra, - NSData comum. Você só precisa descobrir o que esse dispositivo espera de nós e o que será aceito por ele.

A maioria dos dispositivos BLE vem com uma especificação, um tipo de API a partir do qual fica claro como "se comunicar" com eles. Você pode extrair dados das características para ter uma ideia aproximada do que o dispositivo espera de nós.

A partir das especificações, aprendemos em quais características quais propriedades lemos e em que escrevemos, se seremos notificados de alterações (isNotifying). Frequentemente, aqui encontraremos tudo o que é necessário para funcionar.

 peripheral.writeValue(data: NSData!, forCharacteristic: CBCharacteristic!, type: CBCharacteristicWriteType) characteristic.properties - OptionSet type characteristic.isNotifying func peripheral(peripheral: CBPeripheral!, didWriteValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!) 

Durante o processo de gravação, o delegado nos informará que tudo correu bem (didWriteValueForCharacteristics), que o valor desejado foi atualizado e podemos informar o usuário sobre isso ou usar essas informações de maneira diferente.

Consideramos o tópico em uma seção muito estreita, contando com a implementação da Apple; portanto, há vários problemas que terão que ser enfrentados. Por exemplo, uma dependência muito forte da delegação, tão amada da Apple.

Herança de CBPeripheral? Se tudo fosse tão fácil


Parece que, como temos um dispositivo, podemos começar a usá-lo, mas, na verdade, não nos diz nada sobre si. Talvez desejemos controlar o bloqueio, o ar condicionado ou o sensor de frequência cardíaca. Você precisa saber com qual dispositivo estamos nos comunicando.

Parece herança: temos um caso especial de algo em comum. Pela minha experiência, posso dizer que, ao usar herança, algo não funcionará como esperado, algo não funcionará e você não saberá o porquê. Em geral, eu o alertaria contra a idéia de herdar o CBPeripheral. O que fazer?

Eu recomendo que você adicione CBPeripheral ao construtor do objeto que o gerenciará. Isso o encapsula dentro desta classe. Use-o para interagir com o dispositivo, mantenha um forte vínculo com ele para que o iOS não interrompa a conexão. Mas o mais importante é que essa classe seja usada como delegado; caso contrário, será difícil gerenciar todos os dispositivos em um só lugar, pois isso ameaça vários outros.

Conectando e trabalhando com o CBPeripheralDelegate


E assim nos conectamos ao dispositivo e queremos ser CBPeripheralDelegate. Há mais uma nuance: enquanto você trabalha com o dispositivo, “interrogando” seus serviços e características, lendo e escrevendo para eles, quase toda a comunicação ocorre com o periférico. Tudo, exceto a conexão.

Naturalmente, gostaríamos de concentrar toda a comunicação em um só lugar, mas o gerente deve estar ciente do que está acontecendo com o dispositivo. E a dificuldade é ter uma fonte de verdade, para garantir que todos sejam informados em tempo hábil sobre o que está acontecendo com o dispositivo. Para fazer isso, monitoraremos o status do periférico - ele pode mudar de desconectado, conectado e conectado. Ele sempre fala sobre a situação atual. Resta assinar uma alteração de status em nossa instalação de controle, sobre a qual falei anteriormente, isso possibilitará a comunicação com o dispositivo em um único local.

Proximidade


Um ponto muito importante, pois é difícil encontrar documentação normal sobre esse tópico. No caso da Apple e seus iBeacons, tudo é simples, eles nos dizem o quão perto estamos do dispositivo bluetooth.
Infelizmente, não temos uma maneira tão fácil de trabalhar com dispositivos de terceiros. E mais de uma vez aconteceu que havia uma necessidade de determinar o dispositivo mais próximo. Também é difícil entender se o dispositivo está no intervalo disponível ou não. Às vezes, ao pesquisar por dispositivos, ele pode informá-lo apenas uma vez e desaparecer, e uma tentativa de conexão não terá êxito.

Utilizamos o seguinte método: salve uma pilha com rótulos de data e intensidade do sinal (RSSI) para cada mensagem recebida no discoverPeripheral. Se alguém se deparar com CoreLocation, nosso método é semelhante ao modo como os carimbos de data e hora e as coordenadas correspondentes são armazenados lá. Normalmente, quanto maior o sinal (RSSI), mais próximo o dispositivo. É mais difícil entender se um dispositivo está em um alcance acessível ou não, em parte porque esse conceito em si é bastante flexível. Para isso, uso a média ponderada do sinal. Lembre-se de que a intensidade do sinal do dispositivo conectado deve ser solicitada manualmente sempre que você precisar conhecê-lo.

O que vem a seguir?


Infelizmente, este artigo não o tornará um especialista se você o ler também.
tornou-se interessante - preste atenção ao guia de programação CoreBluetooth da Apple , o guia não é muito grande, mas muito útil. Ainda existem algumas transmissões da WWDC 2012 ( básica e avançada ) e uma de 2013 , mas não se preocupe, pouco mudou desde então.

Há também um vídeo da Altconf 2015 publicado no site Realm, que compartilha a experiência de John Sher, um cara legal e especialista.

Source: https://habr.com/ru/post/pt425937/


All Articles