Szukaj…


Wprowadzenie

Gorący do odczytu i zapisu danych do urządzenia Bluetooth o niskim zużyciu energii.

Uwagi

Kilka ważnych punktów

  • Nie są potrzebne żadne możliwości.
  • iPhone przechowuje bajty w formacie Little Endian, więc sprawdź, czy akcesorium Bluetooth również używa Little Endian. Przykład:
    • procesor Intel zwykle używa małego endiana.
    • Architektura ARM była little-endian przed wersją 3, kiedy stała się big-endian.
  • Po operacji pojedynczej lub wsadowej połączenie zostanie utracone, dlatego przed kontynuowaniem należy ponownie nawiązać połączenie.

Wyszukaj UUID usługi

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

Jak odkryć UUID SERWISU bez dokumentacji

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 (zero) - NIL oznacza, że wszystkie usługi zostaną zwrócone, co nie jest dobrą opcją. (CZYTAJ Uwagi 3)
  • Jeśli nie znalazłeś UUIDU SERWISOWEGO, uruchom kod i poszukaj w konsoli wprowadź opis zdjęcia tutaj
  • Znalazłem 3 usługi: Bateria, Informacje o urządzeniu (Firmware) i FFF0
  • Ta usługa UUID nie jest standardowa, listę ze standardami można znaleźć tutaj
  • W tym przypadku FFF0 jest UUIDU SERWISOWEGO

Konwertuj dane na UInt16 i odwrotnie

Dodaj to rozszerzenie do swojej klasy

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

Wyświetlanie nazw wszystkich urządzeń Bluetooth Low Energy (BLE)

  • W tym przykładzie mam kontrolowany pokój z włączonym pojedynczym urządzeniem BLE.
  • Twoja klasa powinna rozszerzyć CBCentralManagerDelegate.
  • Zaimplementuj metodę: centralManagerDidUpdateState (_ central: CBCentralManager).
  • Użyj globalnej kolejki, aby nie zamrażać ekranu podczas wyszukiwania urządzenia.
  • Utwórz instancję CBCentralManager i poczekaj na oddzwonienie 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)")
    }
}

Oddzwonienie do centralManagerDidUpdateState wskazuje, że CoreBluetooth jest gotowy, więc możesz teraz wyszukać BLE. Zaktualizuj kod centralManagerDidUpdateState, aby wyszukać wszystkie urządzenia BLE, gdy będzie ono gotowe.

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 () wyszukuje urządzenia BLE i przestaje wyszukiwać po 5s
  • cb_manager.scanForPeripherals (withServices: zero, opcje: zero) szuka każdego BLE w twoim zasięgu.
  • StopSearchBLE () zatrzyma wyszukiwanie po 5s.
  • Każde znalezione BLE wywoła funkcję zwrotną func centralManager (_ central: CBCentralManager, didDiscover peryferia: CBPeripheral, reklama Data: [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)
}

Połącz i przeczytaj główną wartość

  • Jestem w kontrolowanym pokoju z jednym minewą, która używa protokołu IBEACON.
  • BLEController musi rozszerzyć CBPeripheralDelegate
  • Użyję pierwszego BLE do połączenia po zakończeniu wyszukiwania.
  • Zmodyfikuj metodę 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)
        }
    }
/...
}
  • W dokumentacji urządzenia BLE należy poszukać UUIDU SERWISOWEGO i CHARAKTERYSTYKI UŻYTKOWNIKA GŁÓWNEGO
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])!)
}
  • Utwórz zmienną „service_uuid” i „major_uuid” jak kod powyżej. „-0000-1000-8000-00805f9b34fb” jest częścią standardu. „fff0” to mój UUID SERWISU, „fff2” to moja GŁÓWNA charakterystyka UUID, a „0000” jest wymagane do wypełnienia 4 bajtowego bloku uuid 1º.
  • discoverCharacteristics ([major_uuid], dla: (peripheral.services?[0])!) otrzyma główną cechę z mojego urządzenia gatt server i na razie będzie miał wartość NIL.
  • (peripheral.services?[0])! - 0, ponieważ zrobiłem 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)")
}
  • Wartość charakterystyczna będzie czytelna dopiero po wywołaniu peryferyjnej.readValue (dla: charakterystyczna)
  • readValue spowoduje func peryferyjne (_ peryferyjne: CBPeripheral, didUpdateValueForakterystyka: CBCharakterystyka, błąd: błąd?) z wartością w Typ danych.

Napisz główną wartość

  • Musisz odkryć usługi i cechy
  • Nie musisz czytać wartości z charakterystyki przed jej zapisaniem.
  • będzie kontynuowane w tym przykładzie po odczytaniu wartości. Zmodyfikuj func urządzenie peryferyjne (_ urządzenie peryferyjne: CBPeripheral, didUpdateValue Dla charakterystyki: CBCharakterystyka, błąd: Błąd?)
  • Dodaj zmienną new_major i 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 od niesłyszących będzie wysyłać i odbierać bajty w formacie Little Endian, ale moje urządzenie MINEW z mikroukładem NRF51822 ma architekturę ARM i potrzebuje bajtów w formacie Big Endian, więc muszę go zamienić.
  • Dokumentacja urządzenia BLE powie, jaki typ danych wejściowych i wyjściowych będzie miała każda cecha i czy można ją odczytać jak wyżej (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)
    }
}
  • Aby zaktualizować informacje o serwerze gatt, musisz go ponownie uruchomić programowo lub zapisać na nim dane oraz wyłączyć i włączyć ręcznie.
  • FFFF jest charakterystyczny, że robi to w tym urządzeniu.
  • „minew123” jest domyślnym hasłem do ponownego uruchomienia lub zapisu informacji w tym przypadku.
  • uruchom swoją aplikację i obserwuj konsolę pod kątem błędów, mam nadzieję, że nie, ale nie zobaczysz jeszcze nowej wartości.
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)

}

  • Ostatnim krokiem jest skomentowanie ostatniego wiersza w metodzie didUpdateValueFor i ponowne uruchomienie aplikacji, teraz pojawi się nowa wartość.


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow