Recientemente, a nuestro equipo se le ocurrió e implementó la función de transferir dinero por aire utilizando la tecnología Bluetooth LE. Quiero contarte cómo lo hicimos y qué nos proporciona Apple a partir de las herramientas. Muchos desarrolladores piensan que Bluetooth es difícil, porque es un protocolo de bajo nivel y no hay muchos expertos en él. Pero no todo es tan aterrador, y de hecho, ¡usar esta función es muy simple! Y esas funciones que pueden implementarse usando Bluetooth LE son ciertamente interesantes y posteriormente resaltarán su aplicación entre los competidores.

Primero, comprendamos qué tipo de tecnología es y cuál es su diferencia con el Bluetooth clásico.
¿Qué es Bluetooth LE?
¿Por qué los desarrolladores de Bluetooth nombraron esta tecnología, a saber, Low Energy? Después de todo, con cada nueva versión de Bluetooth, el consumo de energía ya era muchas veces menor. La respuesta está en esta batería.
Su diámetro es de solo 2 cm y la capacidad es de aproximadamente 220 mA * h. Cuando los ingenieros desarrollaron Bluetooth LE, querían que el dispositivo con dicha batería funcionara durante varios años. ¡Y lo hicieron! Los dispositivos Bluetooth LE con dicha batería pueden funcionar durante un año. ¿Cuántos de ustedes todavía apagan el Bluetooth en su teléfono a la antigua usanza para ahorrar energía, como lo hicieron en 2000? En vano haces esto: los ahorros serán menos de 10 segundos del teléfono por día. Y deshabilita una funcionalidad muy grande, como Handoff, AirDrop y otros.
¿Qué lograron los ingenieros al desarrollar Bluetooth LE? ¿Han refinado el protocolo clásico? ¿Lo hizo más eficiente energéticamente? ¿Acaba de optimizar todos los procesos? No Rediseñaron por completo la arquitectura de la pila Bluetooth y lograron el hecho de que ahora, para ser visible para todos los demás dispositivos, necesita menos tiempo para estar en el aire y ocupar el canal. A su vez, esto permitió un buen ahorro en el consumo de energía. Y con la nueva arquitectura, cualquier dispositivo nuevo ahora se puede estandarizar, gracias a lo cual los desarrolladores de todo el mundo pueden comunicarse con el dispositivo y, por lo tanto, escribir fácilmente nuevas aplicaciones para administrarlo. Además, el principio de autodescubrimiento se establece en la arquitectura: cuando se conecta a un dispositivo, no necesita ingresar ningún código PIN, y si su aplicación puede comunicarse con este dispositivo, la conexión toma unos milisegundos.
- Menos tiempo en el aire.
- Menos consumo de energía.
- Nueva arquitectura
- Tiempo de conexión reducido.
¿Cómo lograron los ingenieros dar un salto tan grande en eficiencia energética?La frecuencia se mantuvo igual: 2,4 GHz, no certificada y gratuita para su uso en muchos países. Pero el retraso de la conexión se ha reducido: 15-30 ms en lugar de 100 ms con Bluetooth clásico. La distancia de trabajo se mantuvo igual, 100 m. El intervalo de transmisión no fue fuerte, pero cambió, en lugar de 0.625 ms, se convirtió en 3 ms.
Pero debido a esto, el consumo de energía no se pudo reducir diez veces. Por supuesto, algo tuvo que sufrir. Y esta es la velocidad: en lugar de 24 Mbps, se convirtió en 0.27 Mbps. Probablemente dirás que esta es una velocidad ridícula para 2018.
¿Dónde se usa Bluetooth LE?

Esta tecnología no es joven, apareció por primera vez en el iPhone 4s. Y ya logró conquistar muchas áreas. Bluetooth LE se utiliza en todos los dispositivos domésticos inteligentes y dispositivos electrónicos portátiles. Ahora incluso hay chips del tamaño de granos de café.
¿Y cómo se aplica esta tecnología en el software?Dado que Apple fue el primero en integrar Bluetooth en su dispositivo y comenzar a usarlo, ahora han hecho un buen progreso e integrado la tecnología en su ecosistema. Y ahora puede conocer esta tecnología en servicios como AirDrop, inicio rápido de dispositivos, compartir contraseñas, traspaso. E incluso las notificaciones en el reloj se realizan a través de Bluetooth LE. Además, Apple ha puesto a disposición del público documentación sobre cómo asegurarse de que las notificaciones de todas las aplicaciones lleguen a sus propios dispositivos. ¿Cuáles son los roles de los dispositivos dentro de Bluetooth LE?
Brodcaster Envía mensajes a todos los que están cerca, no puede conectarse a este dispositivo. Según este principio, iBeacons y la navegación interior funcionan.
Observador Escucha lo que sucede y recibe datos solo de mensajes públicos. No crea conexiones.
Pero con
Central y
Periférico más interesante. ¿Por qué no fueron llamados simplemente Servidor-Cliente? Lógicamente, a juzgar por el nombre. Pero no
Porque Periférico en realidad actúa como un servidor. Este es un dispositivo periférico que consume menos energía y se conecta a la Central más poderosa. Peripheral puede informarle que está cerca y qué servicios tiene. Solo un dispositivo puede conectarse a él, y Peripheral tiene algunos datos. Y Central puede escanear el aire en busca de dispositivos, enviar solicitudes de conexión, conectarse a cualquier número de dispositivos, puede leer, escribir y suscribirse a los datos de Peripheral.
¿A qué tenemos acceso los desarrolladores en el ecosistema de Apple?
¿Qué hay disponible para nosotros?
iOS / Mac OS:- Periférico y Central.
- Modo de fondo
- Recuperación del estado.
- Intervalo de conexión 15 ms.
watchOS / tvOS:- watchOS 4+ / tvOS 9+.
- Central solamente.
- Máximo dos conexiones.
- Apple watch series 2+ / AppleTv 4+.
- Apagar al ingresar al fondo.
- Intervalo de conexión 30 ms.
La diferencia más importante es el intervalo de conexión. ¿A qué afecta? Para responder a esta pregunta, primero debe comprender cómo funciona el protocolo Bluetooth LE y por qué una diferencia tan pequeña en los valores absolutos es muy importante.
Como funciona el protocolo
¿Cómo es el proceso de búsqueda y conexión?
Peripheral anuncia su presencia en la frecuencia del intervalo de publicidad, su paquete es muy pequeño y contiene solo unos pocos identificadores de servicio proporcionados por el dispositivo, así como el nombre del dispositivo. El intervalo puede ser bastante grande y puede variar según el estado actual del dispositivo, el modo de ahorro de energía y otras configuraciones. Apple aconseja a los desarrolladores de dispositivos externos que unan la longitud del intervalo al acelerómetro: aumente el intervalo si no se usa el dispositivo y, cuando esté activo, disminuya para encontrarlo rápidamente. El intervalo de publicidad no se correlaciona con el intervalo de conexión y lo determina el propio dispositivo, según el consumo de energía y su configuración. Es inaccesible y desconocido para nosotros en el ecosistema de Apple; está completamente controlado por el sistema.

Después de encontrar el dispositivo, enviamos una solicitud de conexión, y aquí el intervalo de conexión entra en escena, el tiempo después del cual el segundo dispositivo puede responder a la solicitud. Pero esto es cuando se conecta, pero ¿qué sucede al leer / escribir?

El intervalo de conexión también aparece cuando se leen datos; reducirlo 2 veces aumenta la velocidad de transferencia de datos. Pero debe comprender que si ambos dispositivos no admiten el mismo intervalo, se seleccionará el máximo de ellos.
Veamos en qué consiste un paquete de información que pasa Peripheral.
La MTU (unidad de transmisión máxima) de dicho paquete se determina durante el proceso de conexión y varía de un dispositivo a otro y dependiendo del sistema operativo. En la versión 4.0 del protocolo, la MTU era de aproximadamente 30 y el tamaño de la carga útil no superaba los 20 bytes. En la versión 4.2, todo ha cambiado, ahora puede transferir unos 520 bytes. Pero, desafortunadamente, solo los dispositivos más jóvenes que el iPhone 5s admiten esta versión del protocolo. El tamaño de la sobrecarga, independientemente del tamaño de la MTU, es de 7 bytes: esto incluye encabezados ATT y L2CAP. Con el registro, en general, una situación similar.

Solo hay dos modos: con respuesta y sin ella. El modo sin respuesta acelera significativamente la transferencia de datos, ya que no hay intervalo de espera antes de la próxima grabación. Pero este modo no siempre está disponible, no en todos los dispositivos ni en todos los sistemas. El acceso a este modo de grabación puede estar limitado por el propio sistema, ya que se considera menos eficiente energéticamente. En iOS, hay un método en el que puede verificar antes de grabar si este modo está disponible.
Ahora veamos en qué consiste el protocolo.

El protocolo consta de 5 niveles.
La capa de aplicación es su lógica, descrita en la parte superior de CoreBluetooth.
GATT (Generic Attributes Layer) se usa para intercambiar servicios y características que están en los dispositivos.
ATT (Capa de atributos) se utiliza para administrar sus características y transferir sus datos.
L2CAP es un protocolo de intercambio de datos de bajo nivel.
El controlador es el chip BT en sí.
Probablemente se pregunte qué es GATT y cómo podemos trabajar con él.GATT consta de características y servicios. Una característica es un objeto en el que se almacenan sus datos, como una variable. Y un servicio es un grupo en el que se ubican sus características, como un espacio de nombres. El servicio tiene un nombre: UUID, lo eliges tú mismo. Un servicio puede contener un servicio subsidiario.

La característica también tiene su propio
UUID , de hecho, un nombre.
El valor de la característica es NSData, aquí puede registrar y almacenar datos.
Los descriptores son una descripción de su característica, puede describir qué datos espera de esta característica o qué significan. Hay muchos descriptores en el protocolo Bluetooth, pero hasta ahora solo dos están disponibles en los sistemas Apple: la descripción humana y el formato de datos. También hay permisos para su función:

Probémoslo tú mismo
Tuvimos la idea de hacer posible transferir dinero por vía aérea sin requerir nada del destinatario. Imagínese, está desconcertando una tarea muy interesante, escribiendo el código perfecto, y aquí un colega sugiere ir a tomar un café. Y te apasiona tanto la tarea que no puedes irte, y pídele que te compre una taza de delicioso capuchino. Él te trae café, y debes devolverle el dinero. Puede traducir por número de teléfono, funciona bien. Pero aquí hay una situación incómoda: no sabes su número. Bueno, así, has estado trabajando durante tres años, pero no han intercambiado números :)
Por lo tanto, decidimos hacer posible transferir dinero a los que están cerca, sin ingresar ningún dato de usuario. Como en AirDrop. Simplemente seleccione un usuario y envíe la cantidad que necesita. Veamos qué necesitamos para esto.

EMPUJAR mapeo
Necesitamos el remitente:
- Podría encontrar todos los dispositivos que están cerca y que admiten nuestro servicio.
- Podía leer los detalles.
- Y podría enviar un mensaje al destinatario que le había enviado con éxito el dinero.
El destinatario, a su vez, debe poder informar a los remitentes de los alrededores que tiene un servicio con los datos necesarios y poder recibir mensajes del remitente. Creo que no vale la pena describir cómo se lleva a cabo el proceso de transferencia de dinero por detalles en nuestro banco. Ahora intentemos implementar esto.
Primero debe encontrar los nombres de nuestro servicio y características. Como dije, este es el UUID. Simplemente los generamos y los guardamos en Peripheral y Central para que sean iguales en ambos dispositivos.

Usted es libre de usar cualquier UUID, excepto los que terminan así: XXXXXXXX-
icsoft1000-8000-00805F9B34FB : están reservados para diferentes compañías. Usted mismo puede comprar ese número y nadie lo usará.
Costará $ 2500.
Luego, necesitaremos crear gerentes: uno para transferir fondos y el otro para recibir. Solo necesita especificar delegados. Transmitiremos Central, recibiremos Periférico. Creamos ambos, porque tanto el emisor como el receptor pueden ser una persona en diferentes momentos.

Ahora tenemos que hacer posible detectar al destinatario y anotar los detalles del destinatario en nuestra característica.

Primero, crea un servicio. Registraremos el UUID e indicaremos que es primario, es decir, el servicio es el principal para este dispositivo. Un buen ejemplo: un monitor de frecuencia cardíaca, para el cual la frecuencia cardíaca actual será el servicio principal, y el estado de la batería es información secundaria.
A continuación, creamos dos características: una para leer los detalles del destinatario, la segunda para escribir para que el destinatario pueda aprender sobre el envío de dinero. Los registramos en nuestro servicio, luego los agregamos al administrador, iniciamos el descubrimiento e indicamos el UUID del servicio para que todos los dispositivos que estén cerca puedan conocer nuestro servicio antes de conectarse. Estos datos se colocan en el paquete que Central envía durante la transmisión.
El destinatario está listo, diríjase al remitente. Ejecute la búsqueda y conéctese.

Cuando activa el administrador, comenzamos la búsqueda de dispositivos con nuestro servicio. Cuando los encontramos, los obtenemos en el método delegado e inmediatamente nos conectamos. Importante: debe mantener un vínculo fuerte con todos los periféricos con los que trabaja, de lo contrario, se filtrarán.

Después de una conexión exitosa, configuramos el delegado que trabajará con este dispositivo y obtenemos el servicio que necesitamos del dispositivo.

Nos hemos conectado con éxito al destinatario, ahora necesita leer sus detalles.
Después de conectarnos, ya hemos solicitado todos los servicios del dispositivo. Y después de recibirlos, se llamará al método delegado, que enumerará todos los servicios disponibles en este dispositivo. Encontramos el correcto y solicitamos sus características. El UUID puede encontrar el resultado en el método delegado, que almacena los datos para la traducción. Intentamos leerlos y obtenemos lo deseado nuevamente en el método delegado. Todos los servicios, características y sus valores son almacenados en caché por el sistema, por lo que no es necesario solicitarlos más tarde cada vez.

Eso es todo, enviamos dinero para el café, es hora de mostrarle al destinatario un aviso hermoso para que espere rublos en su cuenta. Para hacer esto, debe implementar el proceso de enviar un mensaje.
Obtenemos la característica que necesitamos del remitente, en este caso la tomamos del valor almacenado. Pero antes de eso, debe obtenerlo del dispositivo, como lo hicimos antes. Y luego simplemente escriba los datos a la característica deseada.
Después de eso, en el otro dispositivo, recibimos una solicitud de escritura en el método delegado. Aquí puede leer los datos que se le envían, responder a cualquier error, por ejemplo, no hay acceso o esta característica no existe. Todo funcionará, pero solo si ambos dispositivos están encendidos y las aplicaciones están activas. ¡Y tenemos que trabajar en segundo plano!

Apple te permite usar Bluetooth en segundo plano. Para hacer esto, debe indicar en info.plist la clave en qué modo queremos usar, en Periférico o Central.

A continuación, en el administrador, debe especificar la clave de recuperación y crear un método delegado. Ahora el modo de fondo está disponible para nosotros. Si la aplicación se queda dormida o se descarga de la memoria, cuando encuentre el periférico deseado o cuando Central esté conectada, se activará y el administrador se restaurará con su clave.

Todo está bien, listo para ser lanzado. Pero aquí los diseñadores vienen corriendo y nos dicen: "Queremos insertar fotos de los usuarios para que les sea más fácil encontrarse". Que hacer En nuestra característica, puede escribir solo unos 500 bytes, pero en algunos dispositivos en general 20 :(

Ir más profundo
Para resolver este problema, tuvimos que profundizar.

Ahora hablamos con dispositivos en el nivel GATT / ATT. Pero en iOS 11, tenemos acceso al protocolo L2CAP. Sin embargo, en este caso, deberá hacerse cargo de la transferencia de datos usted mismo. Los paquetes se envían con MTU de 2 Kb, no es necesario volver a codificar nada, se aplica NSStream normal. Velocidades de datos de hasta 394 Kbps, según Apple.
Suponga que transfiere cualquier dato de su servicio de Periférico a Central en forma de características normales. Y me llevó abrir el canal. Lo abres en Peripheral, a cambio obtienes PSM: este es el número del canal al que puedes conectarte, y debes transferirlo a Central usando las mismas características. El número es dinámico, el sistema mismo elige qué PSM abrir en este momento. Después de la transferencia, ya puede conectarse a Peripheral en Central e intercambiar datos en un formato conveniente para usted. Veamos cómo hacer esto.
Primero, abra el puerto encriptado en Peripheral. Puede hacerlo sin cifrado, esto acelerará un poco la transferencia.

Luego, en el método delegado, obtenemos el PSM y lo enviamos a otro dispositivo.

Después de conectar otro dispositivo, se nos llamará un método en el cual podemos obtener el NSStream que necesitamos para la transmisión desde el canal.

Central es aún más fácil, solo nos conectamos al canal con el número deseado ...

... y después de eso obtenemos las transmisiones que necesitamos. En ellos, puede transferir absolutamente cualquier información de cualquier tamaño y construir su protocolo sobre L2CAP. Entonces nos dimos cuenta de la transferencia de las fotos de los destinatarios.

Pero hay escollos, donde prescindir de ellos.
Trampas
Echemos un vistazo a las dificultades al trabajar en segundo plano. Dado que los roles de Periférico y Central están disponibles para usted, puede pensar. que en el fondo puede determinar qué dispositivos están cerca en el fondo y cuáles están activos. En teoría, debería haberlo sido, pero Apple introdujo una restricción: los teléfonos que están en segundo plano, ya sean centrales o periféricos, no están disponibles para otros teléfonos que también están en segundo plano. Además, los teléfonos que están en segundo plano no son visibles desde dispositivos que no son iOS. Veamos por qué sucede esto.
Cuando su dispositivo está activo, envía un paquete de difusión regular, que puede contener el nombre del dispositivo y una lista de servicios. que proporciona este dispositivo. Y los datos de desbordamiento es todo lo que no encajaba.

Cuando el dispositivo pasa a segundo plano, no transmite el nombre y transfiere la lista de servicios admitidos a datos desbordados. Si la aplicación está activa, cuando escanea desde un dispositivo iOS, lee estos datos y, al pasar a segundo plano, los ignora. Por lo tanto, al cambiar al fondo, no podrá ver las aplicaciones que también están en segundo plano.
Otros sistemas operativos de Apple siempre ignoran los datos de desbordamiento, por lo que si busca dispositivos que admitan su servicio, obtendrá una matriz vacía. Y si se conecta a cada dispositivo que está cerca y solicita servicios compatibles, entonces la lista puede contener su servicio y puede trabajar con él.
Luego nos estábamos preparando para enviar pruebas, corregimos errores menores y nos dedicamos a la optimización. Y de repente, en algún momento, comenzamos a recibir este error en la consola:CoreBluetooth[WARNING] Unknown error: 124
Lo peor fue que no se llamó a ningún método delegado, ni siquiera pudimos superar este error para el usuario. Solo un mensaje en el registro, y silencio, todo se congeló. No se realizaron cambios importantes, por lo que comenzamos a retroceder en los commits. Y descubrieron que una vez optimizaron el código y rehicieron la forma de escribir datos. El problema era que no todos los clientes se actualizaron, por lo que se produjo este error.
.write != .writeWithoutResponse
Estamos contentos de haberlo arreglado todo, más bien pasamos a pasarlo a prueba, y casi de inmediato nos respondieron: “Tus fotos de moda no funcionan. Todos vienen subcargados. Comenzamos a intentarlo, y es cierto que a veces, en diferentes dispositivos, las fotos rotas vienen en diferentes momentos. Comenzaron a buscar una razón.
Y luego nuevamente vieron el error anterior. Inmediatamente pensé que estaba en diferentes versiones. Pero después de la eliminación completa de la versión anterior de todos los dispositivos de prueba, el error aún se reproduce. Estábamos tristes ...
CoreBluetooth[WARNING] Unknown error: 722 CoreBluetooth[WARNING] Unknown error: 249 CoreBluetooth[WARNING] Unknown error: 312
Comenzamos a buscar una herramienta de depuración. Lo primero que encontramos fue el Apple Bluetooth Explorer. Un programa poderoso, puede hacer muchas cosas, pero para depurar el protocolo Bluetooth LE, hay una pequeña pestaña con la búsqueda de dispositivos y la obtención de características. Y necesitábamos analizar L2CAP.
Luego encontraron LightBlue Explorer. Resultó ser un programa bastante decente, aunque con un diseño de iOS 7. Puede hacer lo mismo que Bluetooth Explorer, y también sabe cómo suscribirse a las especificaciones. Y funciona más estable. Todo está bien, pero de nuevo sin L2CAP.
Y luego recordamos el conocido sniffer WireShark.
Resultó que estaba familiarizado con Bluetooth LE: puede leer L2CAP, pero solo bajo Windows. Aunque no da miedo que no encontremos Windows o algo así. El mayor inconveniente: el programa solo funciona con un dispositivo específico. Es decir, tenía que encontrar el dispositivo en algún lugar de la tienda oficial. Y usted mismo comprende que es poco probable que una gran empresa apruebe la compra de un dispositivo incomprensible en un mercado de pulgas. Incluso comenzamos a navegar en tiendas en línea en el extranjero.
Pero aquí encontraron el programa PacketLogger en Herramientas adicionales de Xcode. Le permite ver el tráfico que va en el dispositivo OS X. ¿Por qué no reescribir nuestro MoneyDrop en OS X? Ya teníamos una biblioteca separada. Acabamos de reemplazar UIImage con NSImage, todo comenzó después de 10 minutos.

Finalmente, podríamos leer los paquetes intercambiados entre dispositivos. De inmediato se hizo evidente que en el momento de la transmisión de datos a través de L2CAP se registró una de las características. Y debido al hecho de que el canal estaba completamente ocupado con la transferencia de fotos, iOS ignoró la grabación y el remitente después de ignorar rompió el canal. Después de solucionar los problemas con la transferencia de la foto no fue así.

Eso es todo, gracias por leer :)
Enlaces utiles
WWDC / CoreBluetooth:BluetoothYouTube- Arrow Electronics → Serie Bluetooth de baja energía