iOS
Настройка маяков с помощью CoreBluetooth
Поиск…
Вступление
Горячая запись и запись данных на устройство с низким энергопотреблением.
замечания
Некоторые важные моменты
- Никаких возможностей не требуется.
- iPhone хранит байты в формате Little Endian, поэтому проверьте, использует ли bluetooth-аксессуар Little Endian. Пример:
- Intel CPU обычно использует немного endian.
- Архитектура ARM была миниатюрной до версии 3, когда она стала широкоформатной.
- После однократной или пакетной операции соединение будет потеряно, поэтому перед продолжением необходимо снова подключиться.
Сканировать для SERVICE UUID
func SearchBLE(){
cb_manager.scanForPeripherals(withServices:[service_uuid], options: nil)
StopSearchBLE()
}
Как открыть SERVICE UUID без документации
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 означает, что все службы будут возвращены, что не является хорошим вариантом. (READ Remarks 3)
- Если вы еще не обнаружили, что SERVICE UUID запускает ваш код и ищет консоль
- Я обнаружил, что у меня есть 3 службы: Аккумулятор, Информация об устройстве (Firmware) и FFF0
- Этот сервис uuid не является стандартным, список со стандартами можно найти здесь
- FFF0 - это SERVICE UUID в этом случае
Преобразование данных в UInt16 и наоборот
Добавьте эти расширения в свой класс
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))
}
}
Отображение имен всех Bluetooth Low Energy (BLE)
- В этом примере у меня есть контролируемая комната с одним устройством BLE.
- Ваш класс должен расширить CBCentralManagerDelegate.
- Внедрите метод: centralManagerDidUpdateState (_ central: CBCentralManager).
- Используйте глобальную очередь, чтобы не затормозить экран во время поиска устройства.
- Выполните активацию CBCentralManager и дождитесь ответа callManagerDidUpdateState.
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)")
}
}
Обратный вызов centralManagerDidUpdateState указывает, что CoreBluetooth готов, поэтому вы можете искать BLE сейчас. Обновите код centralManagerDidUpdateState для поиска всех устройств BLE, когда они будут готовы.
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 () выполняет поиск устройств BLE и прекращает поиск после 5 секунд
- cb_manager.scanForPeripherals (withServices: nil, options: nil) ищет все BLE в диапазоне с вами.
- StopSearchBLE () остановит поиск после 5 секунд.
- Каждый найденный BLE будет callback func centralManager (_ central: CBCentralManager, didDiscover периферийное: CBPeripheral, advertData: [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)
}
Подключить и прочитать главное значение
- Я в контролируемой комнате с одним маяком, который использует протокол IBEACON.
- BLEController необходимо расширить CBPeripheralDelegate
- Я буду использовать первый BLE для подключения после остановки поиска.
- Измените метод 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)
}
}
/...
}
- В документации вашего устройства BLE вы должны искать SERVICE UUID и 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])!)
}
- Создайте переменную 'service_uuid' и 'major_uuid', как показано выше. '-0000-1000-8000-00805f9b34fb' является частью стандарта. 'fff0' - мой СЕРВИСНЫЙ UUID, 'fff2' - моя главная характеристика UUID, и '0000' необходимо заполнить блок размером 4 байта uuid 1º.
- discoverCharacteristics ([major_uuid], для: (peripheral.services?[0])!) будет получать основные характеристики с моего gatt-сервера устройства, и на данный момент он будет иметь значение NIL.
- (Peripheral.services?[0])! - 0 beacuse вернет одно значение после того, как я сделал периферийное. 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)")
}
- Характеристическое значение будет читаемым только после вызова peripheral.readValue (для: характеристики)
- readValue приведет к периферийной функции func (_ периферийная: CBPeripheral, didUpdateValueFor характеристика: CBCharacteristic, error: Error?) со значением в типе данных.
Напишите основное значение
- Вам необходимо открыть службы и характеристики
- Перед тем, как написать над ним, вам не нужно считывать значение с характеристики.
- будет продолжен для этого примера после значения чтения. Изменить func периферийное (_ периферийное: CBPeripheral, didUpdateValueДля характеристики: CBCharacteristic, ошибка: ошибка?)
- Добавьте переменную new_major и 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 by deafult отправит и получит байты в формате Little Endian, но мое устройство MINEW witch чипсет NRF51822 имеет ARM archteture и нуждается в байтах в формате Big Endian, поэтому мне нужно его поменять.
- Документация BLE Device сообщит, какой тип ввода и вывода будет иметь каждый признак, и если вы можете прочитать его, как указано выше (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)
}
}
- Чтобы обновить информацию о gatt-сервере, вам необходимо перезагрузить его программным способом или сохранить данные на нем, выключить и включить вручную.
- FFFF характерен для этого устройства.
- «minew123» - это пароль по умолчанию для перезагрузки. o сохранение информации в этом случае.
- запустите приложение и наблюдайте за консолью за любую ошибку, я надеюсь, что нет, но вы еще не увидите новое значение.
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)
}
- Последний шаг - прокомментировать последнюю строку в методе didUpdateValueFor и повторно запустить приложение, теперь вы получите новое значение.
Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow