iOS
Configurar balizas con CoreBluetooth
Buscar..
Introducción
Caliente para leer y escribir datos en un dispositivo bluetooth de baja energía.
Observaciones
Algunos puntos importantes
- No se necesitan capacidades.
- Los bytes de la tienda de iPhone en formato Little Endian, así que compruebe si el accesorio Bluetooth también usa Little Endian. Ejemplo:
- Intel CPU usualmente usa little endian.
- La arquitectura ARM era little-endian antes de la versión 3 cuando se convirtió en big-endian.
- Después de una operación única o por lotes, la conexión se perderá, por lo que debe volver a conectarse antes de continuar.
Escanear para UUID DE SERVICIO
func SearchBLE(){
cb_manager.scanForPeripherals(withServices:[service_uuid], options: nil)
StopSearchBLE()
}
Cómo descubrir el UUID de SERVICIO sin documentación.
func centralManager(_ central: CBCentralManager, didConnect peripheral:
CBPeripheral) {
peripheral.delegate = self
peripheral.discoverServices(nil)
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
for service in peripheral.services! {
print("Service: \(service)\n error: \(error)")
}
}
- discoverServices (nil) - NIL significa que se devolverán todos los servicios, lo que no es una buena opción (LEA Comentarios 3)
- Si no ha encontrado el UUID DE SERVICIO, ejecute su código y busque en la consola
- Encontré tener 3 servicios: Batería, información del dispositivo (Firmware) y FFF0
- Este servicio de uuid no es estándar, puede encontrar una lista de estándares aquí
- FFF0 es el UUID DE SERVICIO en este caso
Convertir datos a UInt16 y contrario.
Agrega estas extensiones a tu clase
protocol DataConvertible {
init?(data: Data)
var data: Data { get }
}
extension DataConvertible {
init?(data: Data) {
guard data.count == MemoryLayout<Self>.size else { return nil }
self = data.withUnsafeBytes { $0.pointee }
}
var data: Data {
var value = self
return Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
}
}
extension UInt16 : DataConvertible {
init?(data: Data) {
guard data.count == MemoryLayout<UInt16>.size else { return nil }
self = data.withUnsafeBytes { $0.pointee }
}
var data: Data {
var value = CFSwapInt16HostToBig(self)
return Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
}
}
Mostrando nombres de todos los Bluetooth de baja energía (BLE)
- Para este ejemplo, tengo una sala controlada con un solo dispositivo BLE habilitado.
- Su clase debe extender CBCentralManagerDelegate.
- Implementar el método: centralManagerDidUpdateState (_ central: CBCentralManager).
- Use la cola global para no congelar la pantalla mientras busca un dispositivo.
- Cree una instancia de CBCentralManager y espere la devolución de llamada centralManagerDidUpdateState respuesta.
class BLEController: CBCentralManagerDelegate{
var cb_manager: CBCentralManager!
var bles : [CBPeripheral] = []
override func viewDidLoad() {
super.viewDidLoad()
cb_manager = CBCentralManager(delegate: self, queue: DispatchQueue.global())
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
print("UPDATE STATE - \(central)")
}
}
La devolución de llamada a centralManagerDidUpdateState indica que CoreBluetooth está listo, por lo que puede buscar BLE ahora. Actualice el código centralManagerDidUpdateState para buscar todos los dispositivos BLE cuando esté listo.
func centralManagerDidUpdateState(_ central: CBCentralManager) {
print("UPDATE STATE - \(central)")
SearchBLE()
}
func SearchBLE(){
cb_manager.scanForPeripherals(withServices: nil, options: nil)
StopSearchBLE()
}
func StopSearchBLE() {
let when = DispatchTime.now() + 5 // change 5 to desired number of seconds
DispatchQueue.main.asyncAfter(deadline: when) {
self.cb_manager.stopScan()
}
}
- SearchBLE () busca dispositivos BLE y deja de buscar después de 5s
- cb_manager.scanForPeripherals (withServices: nil, options: nil) busca todos los BLE que se encuentren en el rango con usted.
- StopSearchBLE () detendrá la búsqueda después de 5s.
- Cada BLE encontrado devolverá call a func centralManager (_ central: CBCentralManager, didDescubrir periférico: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber)
func centralManager(_ central: CBCentralManager, didDiscover peripheral:
CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
guard let name = peripheral.name else {
return
}
print(name)
bles.append(peripheral)
}
Conectar y leer valor mayor
- Estoy en una habitación controlada con una única baliza de mina que usa el protocolo IBEACON.
- BLEController necesita extender CBPeripheralDelegate
- Usaré el primer BLE para conectar después de que la búsqueda haya terminado.
- Modificar el método StopSearchBLE ()
class BLEController: CBCentralManagerDelegate, CBPeripheralDelegate{
//...
func StopSearchMiniewBeacon() {
let when = DispatchTime.now() + 5 // change 2 to desired number of seconds
DispatchQueue.main.asyncAfter(deadline: when) {
self.cb_manager.stopScan()
self.cb_manager.connect(bles.first)
}
}
/...
}
- En la documentación de su dispositivo BLE, debe buscar el UUID DE SERVICIO y la CARACTERÍSTICA PRINCIPAL DE UUID
var service_uuid = CBUUID(string: "0000fff0-0000-1000-8000-00805f9b34fb")
var major_uuid = CBUUID(string: "0000fff2-0000-1000-8000-00805f9b34fb")
func centralManager(_ central: CBCentralManager, didConnect peripheral:
CBPeripheral) {
peripheral.delegate = self
peripheral.discoverServices([service_uuid])
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
print("Service: \(service)\n error: \(error)")
peripheral.discoverCharacteristics([major_uuid], for: (peripheral.services?[0])!)
}
- Cree una variable como 'service_uuid' y 'major_uuid' como el código anterior. '-0000-1000-8000-00805f9b34fb' es parte de la norma. 'fff0' es mi UUID DE SERVICIO, 'fff2' es mi característica de UUID PRINCIPAL y se requiere '0000' para completar el bloque de 4 bytes uuid 1º.
- discoverCharacteristics ([major_uuid], for: (peripheral.services?[0])! obtendrá la característica principal de mi servidor de dispositivos gatt y tendrá NIL como valor por ahora.
- (periferico.servicios? icono0])! - 0 porque se devolverá un solo valor una vez que hice peripheral.discoverServices ([service_uuid])
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for characteristic in service.characteristics! {
print("Characteristic: \(characteristic)\n error: \(error)")
if(characteristic.uuid.uuidString == "FFF2"){
peripheral.readValue(for: characteristic)
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
print("Characteristic read: \(characteristic)\n error: \(error)")
let major = UInt16.init(bigEndian: UInt16(data: characteristic.value!)!)
print("major: \(major)")
}
- El valor de la característica solo se podrá leer después de la llamada peripheral.readValue (para: característica)
- readValue dará como resultado una función periférica (_ periférica: CBPeripheral, didUpdateValueFor feature: CBCaracterística, error: ¿Error?) con valor en Tipo de datos.
Escribe valor mayor
- Necesitas descubrir los servicios y características.
- No necesita leer el valor de la característica antes de escribir sobre ella.
- continuará por, para este ejemplo, después de leer el valor. Modificar la función del periférico (_ periférico: CBPeripheral, didUpdateValueFor feature: CBCaracterística, error: ¿Error?)
- Agrega una variable new_major y reset_characteristic
var reset_characteristic : CBCharacteristic!
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
for characteristic in service.characteristics! {
print("Characteristic: \(characteristic)\n error: \(error)")
if(characteristic.uuid.uuidString == "FFF2"){
peripheral.readValue(for: characteristic)
}
if(characteristic.uuid.uuidString == "FFFF"){
reset_characteristic = characteristic
}
}
}
let new_major : UInt16 = 100
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
print("Characteristic read: \(characteristic)\n error: \(error)")
let major = UInt16.init(bigEndian: UInt16(data: characteristic.value!)!)
print("major: \(major)")
peripheral.writeValue(new_major.data, for: characteristic, type: CBCharacteristicWriteType.withResponse)
}
- El iPhone de deafult enviará y recibirá bytes en formato Little Endian, pero mi dispositivo MINEW con el conjunto de chips NRF51822 tiene architeture ARM y necesita bytes en formato Big Endian, así que tengo que intercambiarlo.
- La documentación del dispositivo BLE le dirá qué tipo de entrada y salida tendrá cada característica y si puede leerla como arriba (CBCharacteristicWriteType.withResponse).
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
print("Characteristic write: \(characteristic)\n error: \(error)")
if(characteristic.uuid.uuidString == "FFF2"){
print("Resetting")
peripheral.writeValue("minew123".data(using: String.Encoding.utf8)!, for: reset_characteristic, type: CBCharacteristicWriteType.withResponse)
}
if(characteristic.uuid.uuidString == "FFFF"){
print("Reboot finish")
cb_manager.cancelPeripheralConnection(peripheral)
}
}
- Para actualizar la información de un servidor gatt, debe reiniciarlo mediante programación o guardar datos en él, apagarlo y encenderlo manualmente.
- Es característico el FFFF que lo hacen en este dispositivo.
- 'minew123' es la contraseña predeterminada para reiniciar o guardar información en este caso.
- ejecute su aplicación y observe su consola por cualquier error, espero que ninguno, pero aún no verá el nuevo valor.
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
print("Characteristic read: \(characteristic)\n error: \(error)")
let major = UInt16.init(bigEndian: UInt16(data: characteristic.value!)!)
print("major: \(major)")
//peripheral.writeValue(new_major.data, for: characteristic, type: CBCharacteristicWriteType.withResponse)
}
- El último paso es comentar la última línea del método didUpdateValueFor y volver a ejecutar la aplicación, ahora tendrá el nuevo valor.
Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow