使用react-native-ble-plx连接E7 Beacon至安卓平板时扫描异常咨询
问题解决:react-native-ble-plx扫描E7 Beacon UUID无效及RSSI距离计算优化
核心问题分析
- UUID扫描无效:react-native-ble-plx的
startDeviceScan第一个参数传入的是BLE服务UUID,而E7 Beacon作为iBeacon设备,广播的是Proximity UUID(属于iBeacon广播帧的一部分),并非BLE服务UUID,因此直接传入该UUID无法匹配到设备。 - txPower错误:代码中txPower写死为0,而实际Beacon的txPower为40(需确认对应dBm值,通常E7的等级40对应+4dBm),这会导致距离计算完全失真。
- 不必要的连接:Beacon是广播设备,无需连接即可获取RSSI,连接操作反而可能干扰广播接收,且持续监听RSSI不需要建立BLE连接。
解决方案
1. 正确匹配E7 Beacon
扫描所有设备(传null),在回调中解析广播数据,过滤出iBeacon设备并匹配Proximity UUID:
- iBeacon广播帧的特征是制造商数据(manufacturer data)的前两个字节为
0x4C00(苹果公司的蓝牙厂商ID),后续数据包含Proximity UUID。
2. 修正txPower与距离计算
- 使用Beacon实际的txPower dBm值(比如E7等级40对应+4dBm,可通过Beacon配置APP确认)。
- 使用更准确的Free Space Path Loss公式计算距离。
3. 移除不必要的连接逻辑
直接通过扫描获取RSSI,无需建立BLE连接,减少资源消耗。
修改后的代码
import React, { useEffect, useRef, useState } from 'react'; import { PermissionsAndroid, Platform, Text, View } from 'react-native'; import { BleManager, Device, BleError } from 'react-native-ble-plx'; const BeaconDistanceTracker = () => { const [beaconDistance, setBeaconDistance] = useState<number | null>(null); const [detected, setDetected] = useState<boolean>(false); const manager = useRef(new BleManager()).current; // 目标E7 Beacon的Proximity UUID const TARGET_UUID = 'e2c56db5-dffb-48d2-b060-d0f5a71096e0'; // E7 Beacon txPower等级40对应的实际dBm(需根据设备确认,示例为+4dBm) const BEACON_TX_POWER = 4; useEffect(() => { const requestPermissions = async () => { const fineLocationGranted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION); const coarseLocationGranted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION); if (Platform.OS === 'android' && Platform.Version >= 31) { await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN); await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT); } return fineLocationGranted === PermissionsAndroid.RESULTS.GRANTED && coarseLocationGranted === PermissionsAndroid.RESULTS.GRANTED; }; // 解析iBeacon广播数据,提取Proximity UUID const parseIBeaconUUID = (manufacturerData: Uint8Array): string | null => { // iBeacon制造商数据格式:0x4C00 (苹果ID) + 0x02 (类型) + 0x15 (长度) + UUID(16字节) + ... if (manufacturerData.length < 25 || manufacturerData[0] !== 0x00 || manufacturerData[1] !== 0x4C) { return null; } if (manufacturerData[2] !== 0x02 || manufacturerData[3] !== 0x15) { return null; } // 提取UUID字节(第4到19字节) const uuidBytes = manufacturerData.slice(4, 20); // 转换为标准UUID格式 const uuidParts = [ uuidBytes.slice(0,4).reduce((acc, byte) => acc + byte.toString(16).padStart(2, '0'), ''), uuidBytes.slice(4,6).reduce((acc, byte) => acc + byte.toString(16).padStart(2, '0'), ''), uuidBytes.slice(6,8).reduce((acc, byte) => acc + byte.toString(16).padStart(2, '0'), ''), uuidBytes.slice(8,10).reduce((acc, byte) => acc + byte.toString(16).padStart(2, '0'), ''), uuidBytes.slice(10,16).reduce((acc, byte) => acc + byte.toString(16).padStart(2, '0'), '') ]; return uuidParts.join('-').toLowerCase(); }; // RSSI转距离(Free Space Path Loss公式) const calculateDistance = (rssi: number, txPower: number): number => { if (rssi === 0) return -1; const ratio = rssi * 1.0 / txPower; if (ratio < 1.0) { return Math.pow(ratio, 10); } else { return (0.89976) * Math.pow(ratio, 7.7095) + 0.111; } }; const handleDeviceScan = (error: BleError | null, device: Device | null) => { if (error) { console.error('扫描错误:', error); return; } if (!device) return; // 获取制造商数据,解析iBeacon const manufacturerData = device.manufacturerData; if (!manufacturerData) return; const beaconUUID = parseIBeaconUUID(manufacturerData); if (beaconUUID === TARGET_UUID.toLowerCase()) { setDetected(true); const rssi = device.rssi; if (rssi !== null) { const distance = calculateDistance(rssi, BEACON_TX_POWER); setBeaconDistance(distance); console.log('RSSI:', rssi, '距离:', distance.toFixed(2), '米'); } } }; const startScanning = async () => { const hasPermissions = await requestPermissions(); if (!hasPermissions) { console.error('权限不足,无法扫描蓝牙设备'); return; } // 扫描所有设备,后续过滤iBeacon manager.startDeviceScan(null, null, handleDeviceScan); }; startScanning(); return () => { manager.stopDeviceScan(); }; }, [manager]); useEffect(() => { console.log('当前距离:', beaconDistance); }, [beaconDistance]); useEffect(() => { const subscription = manager.onStateChange((state) => { console.log('BLE管理器状态:', state); }); return () => subscription.remove(); }, []); return ( <View style={{ padding: 20, backgroundColor: '#111' }}> <Text style={{ color: 'white', fontSize: 18, marginBottom: 10 }}> 设备状态: {detected ? '已检测到E7 Beacon' : '未检测到Beacon'} </Text> <Text style={{ color: 'white', fontSize: 18 }}> 与Beacon的距离: {beaconDistance !== null ? `${beaconDistance.toFixed(2)} 米` : '测量中...'} </Text> </View> ); }; export default BeaconDistanceTracker;
额外说明
- txPower确认:如果不确定E7 Beacon等级40对应的dBm值,可通过Beacon配置APP查看,或者从iBeacon广播数据的第21字节读取(iBeacon格式中txPower为该字节的dBm值)。
- 扫描优化:如果需要减少扫描范围,可在
startDeviceScan的options中设置scanMode为ScanMode.LowLatency或ScanMode.Balanced,根据需求调整。
内容的提问来源于stack exchange,提问作者지민이
相关产品推荐
相关产品推荐

