You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 00:53:18