Xamarin.iOS开发:如何获取iBeacon的GUID、高低位字及信号强度?
Xamarin.iOS iBeacon扫描器开发疑问
我正在使用Xamarin.iOS开发一款基于iBeacon协议的BLE扫描器,需要获取设备的GUID、关联的两个16位字(Major/Minor)以及信号强度,以此计算手机与BLE信标间的距离。我们无需与信标建立连接,仅依靠其每250ms发送的广播数据即可满足需求,这两个16位字段用于触发靠近信标时的操作,信标已通过厂商软件配置完成。
目前我的伪代码如下:
if (scanner == null) { scanner = new CBCentralManager(); } if (scanner != null) { // FIXME Should I pass null to the first parameter of ScanForPeripherals? scanner.DiscoveredPeripheral += Scanner_DiscoveredPeripheral; scanner.ScanForPeripherals(null, new PeripheralScanningOptions { AllowDuplicatesKey = true }); } private void Scanner_DiscoveredPeripheral(object sender, CBDiscoveredPeripheralEventArgs e) { lock (this.visibleBeacons) { // FIXME : How do I get the 2 word values and the signal strength? BeaconInfo discoveredPeripheral = new BeaconInfo(e.Peripheral.Identifier.ToString(), word1, word2, signalStrength); ... } }
现有两个疑问:
ScanForPeripherals的第一个参数是否应传入null?- 如何在回调中获取那两个16位字的值和信号强度?
解答
1. ScanForPeripherals第一个参数的取值
传null是可行的,但会扫描所有BLE设备,效率较低。更优的做法是指定iBeacon专属的服务UUID(0000FEAA-0000-1000-8000-00805F9B34FB),这样只会扫描符合iBeacon协议的设备,减少不必要的回调:
var iBeaconServiceUuid = CBUUID.FromString("0000FEAA-0000-1000-8000-00805F9B34FB"); scanner.ScanForPeripherals(new[] { iBeaconServiceUuid }, new PeripheralScanningOptions { AllowDuplicatesKey = true });
如果你的信标配置了自定义的UUID,也可以直接指定该UUID来缩小扫描范围,进一步提升效率。
2. 获取信号强度与两个16位字(Major/Minor)
信号强度
直接从CBDiscoveredPeripheralEventArgs的RSSI属性获取:
var signalStrength = e.RSSI;
解析Major/Minor与GUID
iBeacon的核心数据(UUID、Major、Minor)封装在广播数据的厂商特定字段中,需要解析e.AdvertisementData[CBAdvertisementData.ManufacturerDataKey]对应的NSData:
首先添加解析方法:
private bool TryParseIBeaconData(NSData manufacturerData, out Guid uuid, out ushort major, out ushort minor) { uuid = Guid.Empty; major = 0; minor = 0; // iBeacon厂商数据格式:苹果公司ID(2字节) + 类型(1字节) + 长度(1字节) + UUID(16字节) + Major(2字节) + Minor(2字节) + 校准信号强度(1字节) if (manufacturerData.Length < 25) return false; var bytes = manufacturerData.ToArray(); // 验证苹果厂商ID(0x4C00) if (bytes[0] != 0x00 || bytes[1] != 0x4C) return false; // 验证iBeacon类型(0x02)和长度(0x15) if (bytes[2] != 0x02 || bytes[3] != 0x15) return false; // 提取UUID(字节4-19)并调整字节序 var uuidBytes = bytes.Skip(4).Take(16).ToArray(); Array.Reverse(uuidBytes, 0, 4); Array.Reverse(uuidBytes, 4, 2); Array.Reverse(uuidBytes, 6, 2); uuid = new Guid(uuidBytes); // 提取Major(字节20-21),大端转小端 major = BitConverter.ToUInt16(bytes.Skip(20).Take(2).Reverse().ToArray(), 0); // 提取Minor(字节22-23),大端转小端 minor = BitConverter.ToUInt16(bytes.Skip(22).Take(2).Reverse().ToArray(), 0); return true; }
然后在回调中调用解析:
private void Scanner_DiscoveredPeripheral(object sender, CBDiscoveredPeripheralEventArgs e) { lock (this.visibleBeacons) { var signalStrength = e.RSSI; if (e.AdvertisementData.TryGetValue(CBAdvertisementData.ManufacturerDataKey, out NSObject manufacturerObj) && manufacturerObj is NSData manufacturerData) { if (TryParseIBeaconData(manufacturerData, out Guid beaconUuid, out ushort major, out ushort minor)) { BeaconInfo discoveredPeripheral = new BeaconInfo(beaconUuid.ToString(), major, minor, signalStrength); // 后续逻辑处理 } } } }
注意:解析时要处理字节序问题,因为iBeacon的Major/Minor和UUID使用大端字节序,而C#的BitConverter默认使用小端,所以需要反转字节数组。
内容的提问来源于stack exchange,提问作者anon
相关产品推荐
相关产品推荐

