Ricerca…


introduzione

Caldo da leggere e scrivere dati su un dispositivo a basso consumo di bluetooth.

Osservazioni

Alcuni punti importanti

  • Non sono necessarie capacità.
  • I byte del negozio di iPhone in formato Little Endian, quindi controlla se l'accessorio Bluetooth usa anche Little Endian. Esempio:
    • CPU Intel generalmente usa little endian.
    • L'architettura ARM era little-endian prima della versione 3 quando divenne big-endian.
  • Dopo un'operazione singola o batch, la connessione andrà persa, quindi è necessario riconnettersi prima di continuare.

Cerca UUID SERVICE

func SearchBLE(){
    cb_manager.scanForPeripherals(withServices:[service_uuid], options: nil)
    StopSearchBLE()
}

Come scoprire SERVICE UUID senza documentazione

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 che tutti i servizi saranno restituiti, che non è una buona opzione. (LEGGI Note 3)
  • Se non hai trovato l'UUID SERVICE, esegui il tuo codice e cerca nella console inserisci la descrizione dell'immagine qui
  • Ho trovato 3 servizi: batteria, informazioni sul dispositivo (firmware) e FFF0
  • Questo servizio uuid non è uno standard, una lista con gli standard può trovare qui
  • FFF0 è l'UUID di SERVIZIO in questo caso

Converti i dati in UInt16 e viceversa

Aggiungi queste estensioni alla tua classe

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))
    }
}

Visualizzazione dei nomi di tutti i Bluetooth Low Energy (BLE)

  • Per questo esempio ho una stanza controllata con una sola abilitazione del dispositivo BLE.
  • La tua classe dovrebbe estendere CBCentralManagerDelegate.
  • Implementare il metodo: centralManagerDidUpdateState (_ centrale: CBCentralManager).
  • Utilizzare la coda globale per non congelare lo schermo durante la ricerca di un dispositivo.
  • Istanziare CBCentralManager e attendere la risposta di callback centralManagerDidUpdateState.
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 richiamata su centralManagerDidUpdateState indica che CoreBluetooth è pronto, quindi puoi cercare BLE ora. Aggiorna il codice centralManagerDidUpdateState per cercare tutto il dispositivo BLE quando è pronto.

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 () cerca dispositivi BLE e interrompe la ricerca dopo 5 secondi
  • cb_manager.scanForPeripherals (withServices: nil, options: nil) cerca ogni BLE in campo con te.
  • StopSearchBLE () interromperà la ricerca dopo 5 secondi.
  • Ogni BLE trovato eseguirà callback func centralManager (_ central: CBCentralManager, didDiscover periferica: 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)
}

Connetti e leggi il valore maggiore

  • Sono in una stanza controllata con un singolo segnale di soccorso che utilizza il protocollo IBEACON.
  • BLEController deve estendere CBPeripheralDelegate
  • Userò il primo BLE da connettere dopo che la ricerca si è fermata.
  • Modifica il metodo 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)
        }
    }
/...
}
  • Nel documentario del tuo dispositivo BLE, dovresti cercare l'UUID di SERVIZIO e l'UUIDO MAGGIORE CARATTERISTICO
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])!)
}
  • Crea una variabile "service_uuid" e "major_uuid" come sopra. '-0000-1000-8000-00805f9b34fb' fa parte dello standard. 'fff0' è il mio UUID di SERVIZIO, 'fff2' è la mia caratteristica MAUOR UUID e '0000' sono necessari per riempire i 4 byte del blocco uuid 1º.
  • discoverCharacteristics ([major_uuid], per: (peripheral.services?[0])!) otterrà le principali caratteristiche dal mio dispositivo gatt server e avrà NIL come valore per ora.
  • (Peripheral.services?[0])! - 0 beacuse restituirà un singolo valore una volta eseguito periferal.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)")
}
  • Il valore caratteristico sarà leggibile solo dopo aver chiamato peripheral.readValue (per: caratteristica)
  • readValue risulterà nella periferica func (_ periferica: CBPeripheral, didUpdateValueFor caratteristica: CBCharacteristic, errore: errore?) con valore nel tipo di dati.

Scrivi un valore importante

  • Hai bisogno di scoprire i servizi e le caratteristiche
  • Non è necessario leggere il valore dalla caratteristica prima di scriverla.
  • continuerà per, per questo esempio, dopo il valore letto. Modifica periferica func (_ periferica: CBPeripheral, didUpdateValueFor caratteristica: caratteristica CBC, errore: errore?)
  • Aggiungi una variabile new_major e 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)
}
  • iPhone di deafult invierà e riceverà byte in formato Little Endian, ma il mio dispositivo MINEW chipset NRF51822 ha archteture ARM e necessita di byte in formato Big Endian, quindi devo scambiarlo.
  • La documentazione del dispositivo BLE dirà quale tipo di input e output avrà ciascuna caratteristica e se è possibile leggerla come sopra (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)
    }
}
  • Per aggiornare le informazioni di un server Gatt, è necessario riavviarlo a livello di codice o salvare i dati su di esso e spegnere e accendere manualmente.
  • FFFF è caratteristico che lo fa in questo dispositivo.
  • 'minew123' è la password predefinita per il riavvio o salva le informazioni in questo caso.
  • esegui la tua app e tieni sotto controllo la console per qualsiasi errore, spero nessuno, ma non vedrai ancora il nuovo valore.
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)

}

  • L'ultimo passaggio consiste nel commentare l'ultima riga nel metodo didUpdateValueFor e rieseguire l'app, ora si avrà il nuovo valore.


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow