news 2026/5/6 9:58:07

react native如何发送蓝牙命令

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
react native如何发送蓝牙命令

使用react-native-ble-plx插件:

import { createContext, useState, useEffect, useContext, useRef } from 'react'; import { BleManager } from 'react-native-ble-plx'; import * as Location from 'expo-location'; import { Platform, PermissionsAndroid, ToastAndroid, Dimensions } from 'react-native'; import { Buffer } from '@craftzdog/react-native-buffer'; // 创建蓝牙上下文 const BlueToothContext = createContext(); // 蓝牙状态枚举 export const BluetoothState = { UNKNOWN: 'unknown', POWERED_OFF: 'poweredOff', POWERED_ON: 'poweredOn', RESETTING: 'resetting', UNAUTHORIZED: 'unauthorized', UNSUPPORTED: 'unsupported', }; //请求蓝牙权限 const requestBluetoothPermission = async () => { if (Platform.OS === 'ios') { return true; } if (Platform.OS === 'android' && PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION) { const apiLevel = parseInt(Platform.Version.toString(), 10); if (apiLevel < 31) { const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION); return granted === PermissionsAndroid.RESULTS.GRANTED; } if (PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN && PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT) { const result = await PermissionsAndroid.requestMultiple([ PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN, PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT, PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION ]); return ( result['android.permission.BLUETOOTH_CONNECT'] === PermissionsAndroid.RESULTS.GRANTED && result['android.permission.BLUETOOTH_SCAN'] === PermissionsAndroid.RESULTS.GRANTED && result['android.permission.ACCESS_FINE_LOCATION'] === PermissionsAndroid.RESULTS.GRANTED ); } } return false; }; // 蓝牙Provider组件 export const BlueToothProvider = ({ children }) => { // 蓝牙状态 const [bluetoothStatus, setBluetoothStatus] = useState(BluetoothState.UNKNOWN); // BLE管理器实例 const [manager, setManager] = useState(null); // 设备列表 const [devices, setDevices] = useState([]); // 已连接设备 const connectedDeviceRef = useRef(null); // const [connectedDevice, setConnectedDevice] = useState(null); //已连接设备的设备ID const connectedDeviceIDRef = useRef(["F9", "06", "78"]); // 扫描状态 const [isScanning, setIsScanning] = useState(false); // 连接状态 const [isConnecting, setIsConnecting] = useState(false); // 错误信息 const [error, setError] = useState(null); //可写入 const writableCharacteristic = useRef({}); //可监听 const monitorCharacteristic = useRef({}); // 监听器订阅列表 const monitorSubscriptions = useRef(null); //指令-响应映射 const instructionResponseMap = useRef(new Map()); //消息提示 const showToast = (message) => { ToastAndroid.showWithGravityAndOffset( message, ToastAndroid.SHORT, ToastAndroid.TOP, 0, -`${Dimensions.get('window').height / 2}` ); }; // 初始化BLE管理器 useEffect(() => { const bleManager = new BleManager(); setManager(bleManager); // 监听蓝牙状态变化 const subscription = bleManager.onStateChange((state) => { let newStatus = BluetoothState.UNKNOWN; switch (state) { case 'PoweredOn': newStatus = BluetoothState.POWERED_ON; break; case 'PoweredOff': newStatus = BluetoothState.POWERED_OFF; break; case 'Resetting': newStatus = BluetoothState.RESETTING; break; case 'Unauthorized': newStatus = BluetoothState.UNAUTHORIZED; break; case 'Unsupported': newStatus = BluetoothState.UNSUPPORTED; break; default: newStatus = BluetoothState.UNKNOWN; } setBluetoothStatus(newStatus); }, true); // 请求位置权限(Android需要) const requestLocationPermission = async () => { const { status } = await Location.requestForegroundPermissionsAsync(); if (status !== 'granted') { setError('需要位置权限才能使用蓝牙功能'); } }; requestLocationPermission(); return () => { subscription.remove(); bleManager.destroy(); }; }, []); // 开始扫描设备 const startScan = async () => { if (!manager || isScanning) return; const hasPermission = await requestBluetoothPermission(); if (!hasPermission) return; // 检查蓝牙状态 if (bluetoothStatus !== BluetoothState.POWERED_ON) { const errorMessage = bluetoothStatus === BluetoothState.POWERED_OFF ? '蓝牙未开启,请先开启蓝牙' : '蓝牙状态异常'; setError(errorMessage); return; } try { // 立即停止之前可能正在进行的扫描 if (isScanning) { manager.stopDeviceScan(); } setDevices([ ]); setIsScanning(true); setError(null); // 记录本次扫描中发现的设备ID const discoveredDeviceIds = new Set(); // 开始扫描,过滤指定服务(可选) manager.startDeviceScan(null, null, (error, device) => { if (error) { const errorMessage = error.reason || error.message || '蓝牙扫描失败'; setError(errorMessage); setIsScanning(false); return; } if (device) { // 记录设备ID discoveredDeviceIds.add(device.id); // 更新设备列表,添加或更新设备 setDevices(prevDevices => { // 检查是否已存在相同id的设备 const deviceIndex = prevDevices.findIndex(d => d.id === device.id); if (deviceIndex >= 0) { // 更新已存在的设备 const updatedDevices = [...prevDevices]; updatedDevices[deviceIndex] = device; return updatedDevices; } else { // 添加新设备 return [...prevDevices, device]; } }); } }); // 扫描10秒后停止并清理离线设备 setTimeout(() => { stopScan(); // 清理离线设备:只保留本次扫描中发现的设备 setDevices(prevDevices => { return prevDevices.filter(device => discoveredDeviceIds.has(device.id)); }); }, 10000); } catch (err) { setError('启动扫描失败'); setIsScanning(false); } }; // 停止扫描设备 const stopScan = () => { if (manager && isScanning) { manager.stopDeviceScan(); setIsScanning(false); } }; //刷新设备列表 const refreshDevices = () => { stopScan(); startScan(); }; // 连接到设备 const connectToDevice = (deviceId) => { if (!manager || isConnecting) return; return new Promise(async (resolve, reject) => { setIsConnecting(true); setError(null); try { // 1.查找设备 const device = await manager.connectToDevice(deviceId); //设置设备的mtu const result = await manager.requestMTUForDevice(deviceId, 512); // 2.发现服务 const discoveredDevice = await device.discoverAllServicesAndCharacteristics(); // 3. 获取服务列表 const servicesList = await discoveredDevice.services(); // 4. 获取每个服务的特征 const allCharacteristics = []; for (const service of servicesList) { const serviceCharacteristics = await discoveredDevice.characteristicsForService( service.uuid ); allCharacteristics.push(...serviceCharacteristics); } //可写入 let target = allCharacteristics.find(item => item.isWritableWithResponse && item.isWritableWithoutResponse); writableCharacteristic.current = target; //可监听 let monitorTarget = allCharacteristics.find(item => item.isNotifiable); monitorCharacteristic.current = monitorTarget; // 设置连接监听器 manager.onDeviceDisconnected(deviceId, (error, disconnectedDevice) => { if (disconnectedDevice?.id === deviceId) { connectedDeviceRef.current = null; setError('设备已断开连接'); } }); connectedDeviceRef.current = device; //开启监听器 const monitorStarted = monitorDevice(monitorCharacteristic.current.serviceUUID, monitorCharacteristic.current.uuid); setIsConnecting(false); resolve(device); return device; } catch (err) { setError(err.message); setIsConnecting(false); reject('连接失败'); return null; } }); }; //储存已连接设备的设备ID const setConnectedDeviceID = (deviceId) => { connectedDeviceIDRef.current = deviceId; }; // 断开设备连接 const disconnectFromDevice = async () => { if (!connectedDeviceRef.current) return; try { // 取消所有监听器 monitorSubscriptions.current.remove(); monitorSubscriptions.current = null; await connectedDeviceRef.current.cancelConnection(); connectedDeviceRef.current = null; // 清空特征引用 writableCharacteristic.current = {}; monitorCharacteristic.current = {}; // 清空已连接设备ID connectedDeviceIDRef.current = null; } catch (err) { setError(err.message); } }; // 写入数据到设备, 服务id、特征id、指令 const writeToDevice = (serviceId, characteristicId, data) => { return new Promise((resolve, reject) => { if (!connectedDeviceRef.current) { reject('未连接到任何设备'); showToast('未连接到任何设备'); return false; } //数据重构 const buffer = data.match(/[\da-f]{2}/gi).map(function (h) { return parseInt(h, 16); }); const typedArray = new Uint8Array(buffer); let value = Buffer.from(typedArray).toString('base64'); connectedDeviceRef.current.writeCharacteristicWithResponseForService( serviceId, characteristicId, value ).then((res) => { resolve(true); }).catch(err => { console.log("写入数据失败", err); reject(err.message || "写入数据失败"); setError(err.message || "写入数据失败"); }); }); }; // 读取设备数据 const readFromDevice = async (serviceId, characteristicId) => { if (!connectedDeviceRef.current) { showToast('未连接到任何设备'); return null; } try { const characteristic = await connectedDeviceRef.current.readCharacteristicForService( serviceId, characteristicId ); return characteristic.value; } catch (err) { setError(err.message); return null; } }; // 监听设备数据通知 const monitorDevice = (serviceId, characteristicId) => { if (!connectedDeviceRef.current) { showToast('未连接到任何设备'); return false; } if (!serviceId || !characteristicId) { setError('服务ID和特征ID不能为空'); return false; } try { // 如果已有订阅,先移除 if (monitorSubscriptions.current) { monitorSubscriptions.current.remove(); } const subscription = connectedDeviceRef.current.monitorCharacteristicForService( serviceId, characteristicId, (error, characteristic) => { if (error) { const errorMessage = error.reason || error.message || '设备通知监听失败'; setError(errorMessage); } else { let result = characteristic.value; console.log("接收到通知", result); //找到对应的callback并执行 const key = result.substring(6, 8); let callback = instructionResponseMap.current.get(key); if (callback) { callback(result); } } }, ); // 保存订阅到列表,以便后续管理 monitorSubscriptions.current = subscription; return true; } catch (err) { const errorMessage = err.reason || err.message || '设备通知监听失败'; setError(errorMessage); return false; } }; // 发送指令 const sendInstruction = (data, callback, sendSuccessCallback) => { // 存储回调函数 //取data下标6-7作为key const key = data.substring(6, 8); instructionResponseMap.current.set(key, callback); console.log('开始发送指令', data); return new Promise((resolve, reject) => { writeToDevice(writableCharacteristic.current.serviceUUID, writableCharacteristic.current.uuid, data).then(res => { sendSuccessCallback && sendSuccessCallback(res); resolve(res); }).catch(err => { reject(err || "发送指令失败"); }); }); }; // 上下文值 const contextValue = { bluetoothStatus, manager, devices, connectedDeviceRef, isScanning, isConnecting, error, startScan, stopScan, refreshDevices, connectToDevice, disconnectFromDevice, writeToDevice, readFromDevice, monitorDevice, sendInstruction, setConnectedDeviceID, connectedDeviceIDRef }; return ( <BlueToothContext.Provider value={contextValue}> {children} </BlueToothContext.Provider> ); }; // 自定义Hook,用于使用蓝牙上下文 export const useBlueToothContext = () => { const context = useContext(BlueToothContext); if (!context) { throw new Error('useBlueToothContext must be used within a BlueToothProvider'); } return context; };

1.关键地方:命令如何转格式发送给蓝牙设备

//数据重构(data: AA5502560157) const buffer = data.match(/[\da-f]{2}/gi).map(function (h) { return parseInt(h, 16); }); const typedArray = new Uint8Array(buffer); let value = Buffer.from(typedArray).toString('base64'); //qlUCVgFX

1.其中原始的data是十六进制数字拼接的字符串

(0xAA、0x55、0x02、0x56、0x01、0x57 -> AA5502560157)

2.先转为十进制的数组:buffer = [170, 85, 2, 86, 1, 87]

3.然后通过new Uint8Array处理进制数据

4.最后转为base64发给蓝牙设备

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/11 14:12:31

Maomi.In | .NET 全能多语言解决方案兆

AI Agent 时代的沙箱需求 从 Copilot 到 Agent&#xff1a;执行能力的质变 在生成式 AI 的早期阶段&#xff0c;应用主要以“Copilot”形式存在&#xff0c;AI 仅作为辅助生成建议。然而&#xff0c;随着 AutoGPT、BabyAGI 以及 OpenAI Code Interpreter&#xff08;现为 Advan…

作者头像 李华
网站建设 2026/4/12 4:49:19

3步掌握BiliTools:从B站资源收藏到高效管理的完整指南

3步掌握BiliTools&#xff1a;从B站资源收藏到高效管理的完整指南 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱&#xff0c;支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools 想…

作者头像 李华
网站建设 2026/4/11 20:25:07

Cadence Allegro 17.4进阶指南:PCB Editor高效布线技巧与实战解析

1. Cadence Allegro 17.4 PCB Editor布线核心技巧 作为PCB设计领域的工业标准工具&#xff0c;Cadence Allegro 17.4的PCB Editor提供了强大的布线功能。在实际项目中&#xff0c;掌握这些技巧可以显著提升设计效率。我经手过多个高速PCB设计项目&#xff0c;深刻体会到合理使用…

作者头像 李华
网站建设 2026/4/11 16:01:03

宇树机器人腿部动力系统拆解:模块化设计如何解决散热与抗冲击难题?

宇树机器人腿部动力系统拆解&#xff1a;模块化设计如何解决散热与抗冲击难题&#xff1f; 在四足机器人研发领域&#xff0c;腿部动力系统始终是决定运动性能的核心单元。传统设计往往面临散热效率与抗冲击能力难以兼得的困境——要么采用开放式结构牺牲防护性&#xff0c;要么…

作者头像 李华