简体   繁体   English

iOS 中的 BLE 外设名称不正确

[英]Incorrect BLE Peripheral Name with iOS

I am writing an iOS app to communicate with a BLE device.我正在编写一个 iOS 应用程序来与 BLE 设备进行通信。 The device can change names between connections (not during the BLE connection), but iOS refuses to change the device name.设备可以在连接之间更改名称(不是在 BLE 连接期间),但 iOS 拒绝更改设备名称。

For example: I can connect to the device when its name is SadName.例如:当设备名称为 SadName 时,我可以连接到设备。 I disconnect it, shut down the app, etc. and change the device's name to HappyName.我断开它,关闭应用程序等并将设备的名称更改为HappyName。 But, when I scan for devices iOS still shows the peripheral name as SadName.但是,当我扫描设备时,iOS 仍然将外围设备名称显示为 SadName。

If I debug the app and look at:如果我调试应用程序并查看:

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

the value of peripheral.name is SadName so I don't think that it is something that I am interpreting incorrectly in code. peripheral.name 的值是 SadName 所以我不认为这是我在代码中错误解释的东西。 I should mention that when I scan for devices, my code is:我应该提到,当我扫描设备时,我的代码是:

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

I am guessing that it is simply because the devices UUID is the same so iOS is pulling it from its cached devices list, but I want to override that.我猜这只是因为设备 UUID 是相同的,所以 iOS 将它从它的缓存设备列表中拉出来,但我想覆盖它。

Thoughts?想法? Sorry, I am new to iOS.抱歉,我是 iOS 新手。 Cheers - MSchmidtbauer干杯 - MSchmidtbauer

The CoreBluetooth API of iOS SDK does not provide a way to force refresh the peripheral name. iOS SDK 的 CoreBluetooth API 没有提供强制刷新外设名称的方法。

Currently it is not feasible to use peripheral.name in iOS when the device name in the BLEdevice changes.当前在 BLEdevice 中的设备名称更改时,在 iOS 中使用peripheral.name 是不可行的。

Apple suggests to scan for a specific device by specifying a list of CBUUID objects (containing one or more service UUIDs) that you pass to scanForPeripheralsWithServices: 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];

This reduces the number of calls of didDiscoverPeripheral.这减少了 didDiscoverPeripheral 的调用次数。 Do not just pass nil to scanForPeripheralsWithServices.不要只是将 nil 传递给 scanForPeripheralsWithServices。 It also allows your app to scan for a peripheral when in background state.它还允许您的应用程序在处于后台状态时扫描外围设备。

If you are looking for a way to broadcast dynamic information that's available before a connection is established, you can use the Advertise or Scan Response Data .如果您正在寻找一种在建立连接之前广播可用动态信息的方法,您可以使用Advertise 或 Scan Response Data The peripheral can be configured to broadcast the entries called Local Name and Manufacturer Specific Data .外设可以配置为广播称为本地名称制造商特定数据的条目。 This data is availabe in the didDiscoverPeripheral:此数据在 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 is an NSString , so write only printable characters on the BLE device in this filed.本地名称是一个 NSString ,因此在此字段中仅在 BLE 设备上写入可打印字符。 Manufacturer Data is an NSData , this can contain any byte value, so you can even have binary data here.制造商数据是一个 NSData ,它可以包含任何字节值,所以你甚至可以在这里拥有二进制数据。

Depending on the BLE device you use, the length of Local Name and Manufacturer Specific Data is limited.根据您使用的 BLE 设备,本地名称和制造商特定数据的长度是有限的。

On my BLE device,I can send the 128 Bit service UUID and a 8 char Local Name with the Advertise Data.在我的 BLE 设备上,我可以发送 128 位服务 UUID 和一个 8 字符的本地名称以及广告数据。 The Manufacturer Specific Data goes into the Scan Response Data and can be 29 bytes long.制造商特定数据进入扫描响应数据并且可以是 29 字节长。

Good thing about using the Adv./Scan Response Data is, it can change on this BLE device without a power cycle.使用 Adv./Scan Response Data 的好处是,它可以在此 BLE 设备上更改而无需重启。

Suggestion:建议:

  1. Use the service UUID to filter when scanning (UUID must be part of advertising data! I omitted it in the above description)扫描时使用服务UUID进行过滤(UUID必须是广告数据的一部分!我在上面的描述中省略了)
  2. Use the Advertise/Scan Response Data for further filtering使用广告/扫描响应数据进行进一步过滤
  3. Forget about peripheral.name as long as there is no deterministic refresh available只要没有可用的确定性刷新,就忘掉peripheral.name

Your guessing is correct.你的猜测是正确的。
It is because of the core-blutetooth cache .这是因为core-blutetooth缓存

Generally changing name / services / characteristics on BLE devices are "not supported".通常“不支持”更改 BLE 设备上的名称/服务/特性。 All these parameters are getting cached.所有这些参数都被缓存。

There are two ways of solving this:有两种方法可以解决这个问题:

  • restart bluetooth adapter, so bluetooth cache gets cleared (I'm afraid there is no way to do this programatically, but i might be wrong)重新启动蓝牙适配器,以便清除蓝牙缓存(恐怕无法以编程方式执行此操作,但我可能错了)
  • your device BLE implements the GATT Service Changed characteristic: read about this here: core_v4.1.zip您的设备 BLE 实现了 GATT Service Changed 特性:在此处阅读相关信息: core_v4.1.zip
    Vol 3, Part G, 2.5.2 , and Vol 3, Part G, 7.1 .第 3 卷,G 部分, 2.5.2和第 3 卷,G 部分, 7.1

Alternatively check the advertisement data of your BLE device.或者检查您的 BLE 设备的广告数据。 It might have a name property which should get refreshed every time the BLE device is advertising data (advertising data doesn't get cachced).它可能有一个name属性,每次 BLE 设备广告数据时都应该刷新该属性(广告数据不会被缓存)。

The CBPeripheralDelegate protocol contains a method... CBPeripheralDelegate协议包含一个方法...

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

... which is made for this purpose. ...这是为此目的而制作的。

Edit - just realized that the second part of the accepted answer above has the same solution :-( I should have read more closely. I will leave this answer here anyway, since it includes RoboVM code.编辑 - 刚刚意识到上面接受的答案的第二部分具有相同的解决方案:-(我应该更仔细地阅读。无论如何我都会把这个答案留在这里,因为它包含了 RoboVM 代码。

I have found a solution to this problem.我找到了解决这个问题的方法。 Adding the GATT Service Changed characteristic didn't work, nor did reading the device name directly from the Device Name characteristic 2A00 since iOS hides the Generic Access service.添加 GATT 服务更改特性不起作用,直接从设备名称特性 2A00 读取设备名称也不起作用,因为 iOS 隐藏了通用访问服务。 However, if the peripheral includes its local name in an advertising packet, it is available from the advertisement data dictionary provided on a scan result using the retrieval key CBAdvertisementDataLocalNameKey .但是,如果外围设备在广告数据包中包含其本地名称,则可以使用检索键CBAdvertisementDataLocalNameKey扫描结果上提供的广告数据字典中获得该CBAdvertisementDataLocalNameKey I copy this into my BLE device wrapper and use it instead of the name available from the CBPeripheral.我将其复制到我的 BLE 设备包装器中,并使用它而不是 CBPeripheral 提供的名称。 Example code in Java for RoboVM is below. RoboVM 的 Java 示例代码如下。 The OBJC or Swift equivalent is straightforward. 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);
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM