Recherche…


Introduction

Chaud pour lire et écrire des données sur un appareil Bluetooth basse consommation.

Remarques

Quelques points importants

  • Aucune capacité n'est requise.
  • iPhone stockez les octets au format Little Endian, vérifiez donc si les accessoires Bluetooth utilisent également Little Endian. Exemple:
    • Intel CPU utilise généralement peu d'endian.
    • L'architecture ARM était un peu endian avant la version 3 lorsqu'elle est devenue big-endian.
  • Après une opération unique ou par lots, la connexion sera perdue, vous devez donc vous reconnecter avant de continuer.

Scan for SERVICE UUID

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

Comment découvrir UUID SERVICE sans documentation

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 signifie que tous les services seront retournés, ce qui n'est pas une bonne option. (LIRE les remarques 3)
  • Si vous n'avez pas trouvé l'UUID de SERVICE, exécutez votre code et cherchez dans la console entrer la description de l'image ici
  • J'ai trouvé 3 services: Batterie, Informations sur l'appareil (Firmware) et FFF0
  • Ce service uuid n'est pas un service standard, une liste de normes peut être trouvée ici
  • FFF0 est l'UUID de SERVICE dans ce cas

Convertir des données en UInt16 et contraire

Ajoutez cette extension à votre 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))
    }
}

Affichage des noms de tous les Bluetooth Low Energy (BLE)

  • Pour cet exemple, j'ai une salle contrôlée avec un seul périphérique BLE.
  • Votre classe devrait étendre CBCentralManagerDelegate.
  • Implémentez la méthode: centralManagerDidUpdateState (_ central: CBCentralManager).
  • Utilisez la file d'attente globale pour ne pas geler l'écran lors de la recherche d'un périphérique.
  • Instanciez CBCentralManager et attendez la réponse de 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)")
    }
}

Le rappel de centralManagerDidUpdateState indique que CoreBluetooth est prêt, vous pouvez donc rechercher BLE maintenant. Mettez à jour le code centralManagerDidUpdateState pour rechercher tout périphérique BLE lorsqu'il est prêt.

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 () recherche les périphériques BLE et arrête la recherche après 5 secondes
  • cb_manager.scanForPeripherals (withServices: nil, options: nil) recherche chaque BLE à portée de main.
  • StopSearchBLE () arrête la recherche après 5 secondes.
  • Chaque BLE trouvée rappellera func centralManager (_ central: CBCentralManager, périphérique didDiscover: 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)
}

Connecter et lire la valeur majeure

  • Je suis dans une pièce contrôlée avec une seule balise qui utilise le protocole IBEACON.
  • BLEController doit étendre CBPeripheralDelegate
  • Je vais utiliser le premier BLE pour se connecter après l'arrêt de la recherche.
  • Modifier la méthode 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)
        }
    }
/...
}
  • Dans la documentation de votre appareil BLE, vous devez rechercher la CARACTÉRISTIQUE SERVICE UUID et MAJOR 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])!)
}
  • Créez une variable 'service_uuid' et 'major_uuid' comme le code ci-dessus. '-0000-1000-8000-00805f9b34fb' fait partie de la norme. 'fff0' est mon UUID DE SERVICE, 'fff2' est ma caractéristique MAJEURE UUID et '0000' est nécessaire pour remplir le bloc uuid 1º de 4 octets.
  • discoverCharacteristics ([major_uuid], pour: (périphérique.services?[0])!) aura une caractéristique majeure de mon serveur gatt de périphérique et aura pour valeur NIL pour le moment.
  • (périphérique.services?[0])! - 0 beacuse renverra une seule valeur une fois que j'ai fait peripher.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)")
}
  • La valeur de la caractéristique ne sera lisible qu'après un appel périphérique.readValue (pour: caractéristique)
  • readValue se traduira par un périphérique func (périphérique: CBPeripheral, didUpdateValueFor caractéristique: caractéristique caractéristique, erreur: erreur?) avec une valeur dans le type de données.

Ecrire une valeur majeure

  • Vous devez découvrir les services et les caractéristiques
  • Vous n'avez pas besoin de lire la valeur de la caractéristique avant de l'écrire.
  • continuera pour, pour cet exemple, après la valeur lue. Modifier le périphérique func (périphérique: CBPeripheral, didUpdateValueFor caractéristique: CBCcaracteristic, erreur: erreur?)
  • Ajouter une variable new_major et 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 par deafult enverra et recevra des octets au format Little Endian, mais mon appareil MINEW avec son jeu de puces NRF51822 a une architecture ARM et a besoin d’octets au format Big Endian, je dois donc l’échanger.
  • La documentation de BLE Device indiquera quel type d'entrée et de sortie chaque caractéristique aura et si vous pouvez le lire comme ci-dessus (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)
    }
}
  • Pour mettre à jour les informations d'un serveur gatt, vous devez les redémarrer par programme ou enregistrer des données, puis les désactiver et les activer manuellement.
  • FFFF est caractéristique qui le fait dans cet appareil.
  • 'minew123' est le mot de passe par défaut pour le redémarrage o enregistrer les informations dans ce cas.
  • exécutez votre application et regardez-vous la console pour toute erreur, je l'espère pas, mais vous ne verrez pas encore la nouvelle valeur.
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)

}

  • La dernière étape consiste à commenter la dernière ligne de la méthode didUpdateValueFor et à réexécuter l'application, maintenant vous aurez la nouvelle valeur.


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow