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 introduzca la descripción de la imagen aquí
  • 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