iOS的BLE外设名称不正确

时间:2022-12-12 19:03:34

I am writing an iOS app to communicate with a BLE device. The device can change names between connections (not during the BLE connection), but iOS refuses to change the device name.

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

For example: I can connect to the device when its name is SadName. I disconnect it, shut down the app, etc. and change the device's name to HappyName. But, when I scan for devices iOS still shows the peripheral name as SadName.

例如:我可以在名称为SadName时连接到设备。我断开它,关闭应用程序等,并将设备的名称更改为HappyName。但是,当我扫描设备时,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. I should mention that when I scan for devices, my code is:

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

[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. Cheers - MSchmidtbauer

思考?对不起,我是iOS新手。干杯 - MSchmidtbauer

4 个解决方案

#1


22  

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. Do not just pass nil to scanForPeripheralsWithServices. It also allows your app to scan for a peripheral when in background state.

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

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. 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. Manufacturer Data is an NSData, this can contain any byte value, so you can even have binary data here.

本地名称是NSString,因此在此字段中仅在BLE设备上写入可打印字符。制造商数据是一个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. The Manufacturer Specific Data goes into the Scan Response Data and can be 29 bytes long.

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

Good thing about using the Adv./Scan Response Data is, it can change on this BLE device without a power cycle.

使用Adv./Scan响应数据的好处是,它可以在没有电源循环的情况下在此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)
  2. 扫描时使用服务UUID进行过滤(UUID必须是广告数据的一部分!我在上面的描述中省略了它)
  3. Use the Advertise/Scan Response Data for further filtering
  4. 使用播发/扫描响应数据进行进一步过滤
  5. Forget about peripheral.name as long as there is no deterministic refresh available
  6. 只要没有可用的确定性刷新,忘记peripheral.name

#2


5  

Your guessing is correct.
It is because of the core-blutetooth cache.

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

Generally changing name / services / characteristics on BLE devices are "not supported". All these parameters are getting cached.

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

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
    Vol 3, Part G, 2.5.2, and Vol 3, Part G, 7.1.
  • 您的设备BLE实现GATT服务更改特性:在此处阅读第3卷,第G部分,2.5.2和第3卷,第G部分,第7.1节。

Alternatively check the advertisement data of your BLE device. It might have a name property which should get refreshed every time the BLE device is advertising data (advertising data doesn't get cachced).

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

#3


0  

The CBPeripheralDelegate protocol contains a method...

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

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

... which is made for this purpose.

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

#4


0  

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. 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. I copy this into my BLE device wrapper and use it instead of the name available from the CBPeripheral. Example code in Java for RoboVM is below. The OBJC or Swift equivalent is straightforward.

我找到了解决这个问题的方法。添加GATT服务更改的特性不起作用,也没有直接从设备名称特征2A00读取设备名称,因为iOS隐藏了通用访问服务。然而,如果外围设备在广告包中包括其本地名称,则可以使用检索关键字CBAdvertisementDataLocalNameKey从扫描结果上提供的广告数据字典中获得它。我将其复制到我的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);
        }
    }

#1


22  

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. Do not just pass nil to scanForPeripheralsWithServices. It also allows your app to scan for a peripheral when in background state.

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

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. 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. Manufacturer Data is an NSData, this can contain any byte value, so you can even have binary data here.

本地名称是NSString,因此在此字段中仅在BLE设备上写入可打印字符。制造商数据是一个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. The Manufacturer Specific Data goes into the Scan Response Data and can be 29 bytes long.

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

Good thing about using the Adv./Scan Response Data is, it can change on this BLE device without a power cycle.

使用Adv./Scan响应数据的好处是,它可以在没有电源循环的情况下在此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)
  2. 扫描时使用服务UUID进行过滤(UUID必须是广告数据的一部分!我在上面的描述中省略了它)
  3. Use the Advertise/Scan Response Data for further filtering
  4. 使用播发/扫描响应数据进行进一步过滤
  5. Forget about peripheral.name as long as there is no deterministic refresh available
  6. 只要没有可用的确定性刷新,忘记peripheral.name

#2


5  

Your guessing is correct.
It is because of the core-blutetooth cache.

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

Generally changing name / services / characteristics on BLE devices are "not supported". All these parameters are getting cached.

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

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
    Vol 3, Part G, 2.5.2, and Vol 3, Part G, 7.1.
  • 您的设备BLE实现GATT服务更改特性:在此处阅读第3卷,第G部分,2.5.2和第3卷,第G部分,第7.1节。

Alternatively check the advertisement data of your BLE device. It might have a name property which should get refreshed every time the BLE device is advertising data (advertising data doesn't get cachced).

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

#3


0  

The CBPeripheralDelegate protocol contains a method...

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

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

... which is made for this purpose.

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

#4


0  

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. 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. I copy this into my BLE device wrapper and use it instead of the name available from the CBPeripheral. Example code in Java for RoboVM is below. The OBJC or Swift equivalent is straightforward.

我找到了解决这个问题的方法。添加GATT服务更改的特性不起作用,也没有直接从设备名称特征2A00读取设备名称,因为iOS隐藏了通用访问服务。然而,如果外围设备在广告包中包括其本地名称,则可以使用检索关键字CBAdvertisementDataLocalNameKey从扫描结果上提供的广告数据字典中获得它。我将其复制到我的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);
        }
    }