如何从React JSX网站通过蓝牙向Raspberry Pi Zero 2W传输数据?
解决方案:React Web 蓝牙控制树莓派电机
一、树莓派端配置(BlueZ + GATT 服务 / RFCOMM 套接字)
1. 适配Web Bluetooth API的GATT服务配置
Web Bluetooth仅支持BLE(低功耗蓝牙)通信,树莓派Zero 2W原生支持BLE,按以下步骤配置自定义GATT服务:
- 更新BlueZ到5.50+版本:
sudo apt update && sudo apt install bluez bluez-tools - 启用BLE双模模式,编辑
/etc/bluetooth/main.conf:ControllerMode = dual - 重启蓝牙服务:
sudo systemctl restart bluetooth - 设置蓝牙可发现、可配对:
bluetoothctl power on discoverable on pairable on exit - 用Python创建GATT服务(依赖
bluezero库),暴露可写特征接收4位指令:
安装依赖:from bluezero import peripheral class MotorController(peripheral.Peripheral): def __init__(self): super().__init__(name='MotorControl', appearance=0x0000) # 添加自定义服务和特征 self.add_service(srv_id=1, uuid='0000FFE0-0000-1000-8000-00805F9B34FB', primary=True) self.add_characteristic( srv_id=1, chr_id=1, uuid='0000FFE1-0000-1000-8000-00805F9B34FB', value=[], notifying=False, flags=['write', 'write-without-response'], write_callback=self.handle_command ) def handle_command(self, value): # 解码收到的4位数字序列 cmd = value.decode('utf-8').strip() if len(cmd) == 4: # 此处编写电机控制逻辑,比如映射数字到GPIO引脚 print(f"执行指令: {cmd}") # 示例:控制GPIO18引脚 # import RPi.GPIO as GPIO # GPIO.setmode(GPIO.BCM) # GPIO.setup(18, GPIO.OUT) # GPIO.output(18, GPIO.HIGH if cmd[0] == '1' else GPIO.LOW) if __name__ == '__main__': service = MotorController() service.publish()pip install bluezero
2. RFCOMM蓝牙套接字配置(经典蓝牙串口)
如果偏好套接字通信,可使用RFCOMM协议实现蓝牙串口:
- 树莓派端启动RFCOMM服务器:
import bluetooth server_sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM) server_sock.bind(("", bluetooth.PORT_ANY)) server_sock.listen(1) port = server_sock.getsockname()[1] uuid = "94f39d29-7d6d-437d-973b-fba39e49d4ee" bluetooth.advertise_service(server_sock, "MotorServer", service_id=uuid) print(f"等待连接,端口: {port}") client_sock, client_info = server_sock.accept() print(f"已连接: {client_info}") try: while True: data = client_sock.recv(1024) if not data: break cmd = data.decode('utf-8').strip() if len(cmd) ==4: print(f"收到指令: {cmd}") # 电机控制逻辑 except IOError: pass client_sock.close() server_sock.close() - 注册RFCOMM服务:
sudo sdptool add SP
二、React JSX端实现
1. Web Bluetooth API连接GATT服务
仅支持HTTPS/localhost环境,Chrome、Edge等浏览器兼容:
import { useState } from 'react'; const MotorControl = () => { const [char, setChar] = useState(null); const [connected, setConnected] = useState(false); const connectBluetooth = async () => { try { const device = await navigator.bluetooth.requestDevice({ filters: [{ namePrefix: 'MotorControl' }], optionalServices: ['0000FFE0-0000-1000-8000-00805F9B34FB'] }); const server = await device.gatt.connect(); const service = await server.getPrimaryService('0000FFE0-0000-1000-8000-00805F9B34FB'); const characteristic = await service.getCharacteristic('0000FFE1-0000-1000-8000-00805F9B34FB'); setChar(characteristic); setConnected(true); } catch (err) { console.error('连接失败:', err); } }; const sendCmd = async (cmd) => { if (!char) return alert('先连接蓝牙'); const encoder = new TextEncoder(); await char.writeValue(encoder.encode(cmd)); console.log(`发送指令: ${cmd}`); }; return ( <div> <button onClick={connectBluetooth} disabled={connected}> {connected ? '已连接' : '连接蓝牙'} </button> <div style={{ marginTop: '1rem' }}> <button onClick={() => sendCmd('1000')}>电机1启动</button> <button onClick={() => sendCmd('0100')}>电机2启动</button> <button onClick={() => sendCmd('0000')}>全部停止</button> </div> </div> ); }; export default MotorControl;
2. RFCOMM套接字(仅Electron环境支持)
普通网页无法直接访问经典蓝牙RFCOMM,需用Electron包装,依赖bluetooth-serial-port库:
import { useState } from 'react'; import BluetoothSerialPort from 'bluetooth-serial-port'; const MotorControlElectron = () => { const [btSerial, setBtSerial] = useState(null); const connectRFCOMM = () => { const serial = new BluetoothSerialPort(); // 替换为树莓派蓝牙MAC地址 serial.findSerialPortChannel('AA:BB:CC:DD:EE:FF', (channel) => { serial.connect('AA:BB:CC:DD:EE:FF', channel, () => { setBtSerial(serial); alert('RFCOMM连接成功'); }, (err) => console.error(err)); }, () => alert('未找到设备')); }; const sendCmd = (cmd) => { if (!btSerial) return; btSerial.write(Buffer.from(cmd), (err) => err && console.error(err)); }; return ( <div> <button onClick={connectRFCOMM}>连接RFCOMM</button> <button onClick={() => sendCmd('1234')}>发送指令</button> </div> ); }; export default MotorControlElectron;
三、常见问题排查
- Web Bluetooth连接失败:确认树莓派蓝牙可发现、GATT服务运行、浏览器环境为HTTPS/localhost、UUID完全匹配。
- BlueZ操作报错:检查蓝牙服务状态
sudo systemctl status bluetooth,将当前用户加入蓝牙组sudo usermod -aG bluetooth $USER,重启后生效。 - RFCOMM连接失败:确保已执行
sudo sdptool add SP,树莓派蓝牙MAC地址正确,设备已配对。
内容的提问来源于stack exchange,提问作者Caleb Pickett
相关产品推荐
相关产品推荐

