【问题标题】:Incorrect BLE Peripheral Name with iOSiOS 的 BLE 外设名称不正确
【发布时间】:2014-11-14 07:20:05
【问题描述】:

我正在编写一个 iOS 应用程序来与 BLE 设备通信。设备可以在连接之间更改名称(不能在 BLE 连接期间),但 iOS 拒绝更改设备名称。

例如:我可以连接到名称为 SadName 的设备。我断开它,关闭应用程序等并将设备的名称更改为 HappyName。但是,当我扫描设备时,iOS 仍将外围设备名称显示为 SadName。

如果我调试应用程序并查看:

 (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI

peripheral.name 的值是 SadName,所以我不认为这是我在代码中的错误解释。我应该提一下,当我扫描设备时,我的代码是:

[self.CM scanForPeripheralsWithServices:nil options:0]; // Start scanning 

我猜这仅仅是因为设备 UUID 相同,因此 iOS 将其从其缓存设备列表中拉出,但我想覆盖它。

想法?抱歉,我是 iOS 新手。 干杯 - 施密特鲍尔

【问题讨论】:

  • 感谢大家的回复。你们俩都有很好的观点,我能够通过剥离广告数据找到一种效果很好的方法。干杯! ~MSchmidtbauer

标签: ios objective-c iphone bluetooth bluetooth-lowenergy


【解决方案1】:

iOS SDK 的 CoreBluetooth API 没有提供强制刷新外设名称的方法。

目前在iOS中使用peripheral.name是不可行的,当BLEdevice中的设备名称发生变化时。

Apple 建议通过指定您传递给 scanForPeripheralsWithServices 的 CBUUID 对象列表(包含一个或多个服务 UUID)来扫描特定设备:

NSArray *services = @[[CBUUID UUIDWithString: @"2456e1b9-26e2-8f83-e744-f34f01e9d701"] ]; // change to your service UUID!
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:1] forKey:CBCentralManagerScanOptionAllowDuplicatesKey];

[self.manager scanForPeripheralsWithServices:services options:dictionary];

这减少了 didDiscoverPeripheral 的调用次数。不要只将 nil 传递给 scanForPeripheralsWithServices。它还允许您的应用在后台状态下扫描外围设备。

如果您正在寻找一种在建立连接之前广播可用的动态信息的方法,您可以使用广告或扫描响应数据。外围设备可以配置为广播名为 Local NameManufacturer Specific Data 的条目。该数据在 didDiscoverPeripheral 中可用:

- (void)centralManager:         (CBCentralManager *)central
 didDiscoverPeripheral:  (CBPeripheral *)peripheral
     advertisementData:      (NSDictionary *)advertisementData
                  RSSI:         (NSNumber *)RSSI {
NSString *localName = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey];
NSData *manufacturerData = [advertisementData objectForKey:CBAdvertisementDataManufacturerDataKey];
NSLog(@"Local: name: %@", localName); 
NSLog(@"Manufact. Data: %@", [manufacturerData description]);
}

Local Name 是一个 NSString,所以在这个字段中只写 BLE 设备上可打印的字符。 Manufacturer Data 是一个 NSData,它可以包含任何字节值,所以你甚至可以在这里拥有二进制数据。

根据您使用的 BLE 设备,本地名称和制造商特定数据的长度是有限的。

在我的 BLE 设备上,我可以发送 128 位服务 UUID 和带有广告数据的 8 字符本地名称。制造商特定数据进入扫描响应数据,长度为 29 个字节。

使用 Adv./Scan Response Data 的好处是,它可以在此 BLE 设备上更改而无需重启。

建议:

  1. 扫描时使用服务UUID进行过滤(UUID必须是广告数据的一部分!我在上面的描述中省略了)
  2. 使用广告/扫描响应数据进行进一步过滤
  3. 只要没有可用的确定性刷新,就忘记 peripheral.name

【讨论】:

  • 我编辑了帖子,希望它现在比以前更多。
  • 在我读到关于在函数调用中放置正确的 CBUUID 后,我不再阅读你的长答案,因为这与问题完全无关。后来我又回来读了一遍,得到了正确的答案。你应该先给出真正的答案,然后再附加建议。
  • 如果您调用scanForPeripheralsWithServices:services,广告数据中不包含localName 是否还有其他问题?如果我打电话给scanForPeripheralsWithServices:nil the localName 就在那里。
【解决方案2】:

你的猜测是正确的。
这是因为core-blutetooth 缓存

通常“不支持”在 BLE 设备上更改名称/服务/特性。所有这些参数都被缓存了。

有两种方法可以解决这个问题:

  • 重启蓝牙适配器,清除蓝牙缓存(恐怕无法以编程方式执行此操作,但我可能错了)
  • 您的设备 BLE 实现了 GATT 服务更改特性:在此处阅读:core_v4.1.zip
    第 3 卷,G 部分,2.5.2,以及第 3 卷,G 部分,7.1

或者检查您的 BLE 设备的广告数据。它可能有一个 name 属性,每次 BLE 设备广告数据时都应该刷新该属性(广告数据不会被缓存)。

【讨论】:

    【解决方案3】:

    CBPeripheralDelegate 协议包含一个方法...

    - (void)peripheralDidUpdateName:(CBPeripheral *)peripheral NS_AVAILABLE(NA, 6_0);
    

    ...为此目的而制作的。

    【讨论】:

    • 遗憾的是,只有在 iOS 决定名称已更改时才会调用该方法,并且说服它似乎很难。
    【解决方案4】:

    编辑 - 刚刚意识到上面接受的答案的第二部分具有相同的解决方案 :-( 我应该更仔细地阅读。无论如何我都会把这个答案留在这里,因为它包含 RoboVM 代码。

    我已经找到了解决这个问题的方法。添加 GATT Service Changed 特性不起作用,也没有直接从 Device Name 特性 2A00 读取设备名称,因为 iOS 隐藏了 Generic Access 服务。但是,如果外围设备在广告数据包中包含其本地名称,则可以使用检索键 CBAdvertisementDataLocalNameKeyscan result 上提供的广告数据字典中获得它。我将它复制到我的 BLE 设备包装器中并使用它而不是 CBPeripheral 提供的名称。下面是 RoboVM 的 Java 示例代码。 OBJC 或 Swift 等价物很简单。

        @Override
        public void didDiscoverPeripheral(CBCentralManager cbCentralManager, CBPeripheral cbPeripheral, CBAdvertisementData cbAdvertisementData, NSNumber rssi) {
            NSData manufacturerData = cbAdvertisementData.getManufacturerData();
            byte[] data = null;
            if(manufacturerData != null)
                data = manufacturerData.getBytes();
            IosBleDevice bleDevice = new IosBleDevice(cbPeripheral);
            String name = cbAdvertisementData.getLocalName();
            if(name != null && !name.equals(cbPeripheral.getName())) {
                CJLog.logMsg("Set local name to %s (was %s)", name, cbPeripheral.getName());
                bleDevice.setName(name);
            }
            deviceList.put(bleDevice.getAddress(), bleDevice);
            if(!iosBlueMaxService.getSubscriber().isDisposed()) {
                BleScanResult bleScanResult = new IosBleScanResult(bleDevice,
                    cbAdvertisementData.isConnectable(),
                    data);
                bleScanResult.setRssi(rssi.intValue());
                iosBlueMaxService.getSubscriber().onNext(bleScanResult);
            }
        }
    

    【讨论】:

    • 在Objective c中如何获取外设的名称。
    • 嗯。对我来说,外围设备的 name 属性和 kCBAdvDataLocalName 的值都是截断的名称,而不是全名。似乎还有更多。
    • 这很可能是因为广告数据只包含截断的名称,这似乎是 iOS 第一次看到设备时缓存的名称。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多