‌React Native蓝牙串口通信神器:React Native Bluetooth Serial详解‌

本文从原理、环境配置、核心 API、代码示例、图解等多个维度进行详尽说明,帮助你快速上手 React Native + 蓝牙串口通信开发。


目录

  1. 概述
  2. 环境准备
  3. 安装与配置
  4. 原理与整体架构图解
  5. 权限设置

  6. 核心 API 详解

  7. 完整示例:一个简单的串口控制页面
  8. 注意事项与常见问题
  9. 总结

概述

在许多 IoT 场景中,蓝牙串口(Bluetooth Serial Port Profile, SPP)是最常见的无线数据传输方式之一。尤其是当你需要在手机端与 Arduino、Raspberry Pi、各种自制蓝牙模块(如 HC-05/HC-06)等设备通信时,蓝牙串口通信显得尤为重要。

React Native 社区中,有一个叫做 react-native-bluetooth-serial-next(常简称为 “React Native Bluetooth Serial”)的第三方库,可以帮助我们在 React Native 应用中快速实现蓝牙 SPP 的扫描、连接、收发数据、断开等功能。它对 Android/iOS 都提供了比较完整的封装,并且支持事件回调,非常适合初学者和中小型项目使用。

本文将从最基础的安装开始,一步步带你了解整个流程,并且附上大量代码示例与图解,帮助你快速上手。


环境准备

  • React Native 环境

    • Node.js (建议 v14 及以上)
    • React Native CLI (若已安装,可跳过)
    • Android Studio / Xcode(根据你做 Android 或 iOS)
  • 目标设备

    • 一台具备蓝牙功能的手机(Android 或 iOS)
    • 一块支持 SPP 的蓝牙模块(如 HC-05 / HC-06)
    • Arduino、树莓派或其他控制板(本文以 HC-05 + Arduino UNO 为示例)
说明: 文中示例代码基于 React Native 0.70+,在 Android 10+ 与 iOS 13+ 上测试通过。

安装与配置

首先,进入你的项目根目录,执行以下命令安装 react-native-bluetooth-serial-next

# 使用 npm
npm install react-native-bluetooth-serial-next --save

# 或使用 yarn
yarn add react-native-bluetooth-serial-next

安装完成后,进入 iOS 目录执行 CocoaPods 安装:

cd ios
pod install
cd ..
Tip: 如果你使用了 React Native 0.60 以上(自动链接),上述安装完成后,通常不需要手动链接(react-native link ...)。如果你遇到链接问题,请参考官方文档自行调整。

原理与整体架构图解

在使用蓝牙串口通信时,整体流程如下:

+---------------------+      +-----------------------+      +----------------------+
|   React Native App  | <--> | 手机内置蓝牙适配器(BLE/Classic) | <--> | HC-05蓝牙模块(SPP) |
+---------------------+      +-----------------------+      +----------------------+
                                                                 |
                                                                 |
                                                                 v
                                                   +----------------------+
                                                   |    Arduino 控制器     |
                                                   +----------------------+
  1. React Native App:我们通过 react-native-bluetooth-serial-next 调用原生蓝牙 API(Android/ iOS),进行扫描、配对、连接,以及收发数据。
  2. 手机蓝牙适配器:基于 Classic Bluetooth SPP 协议,它将手机的串口数据转换成射频进行传输。(注意:React Native Bluetooth Serial 默认使用 Classic SPP,非 BLE)
  3. HC-05 蓝牙模块:通过串口(UART)与 Arduino 等控制器连接,充当蓝牙接收端,接受手机发送的指令或返回传感器数据。
  4. Arduino / 控制器:通过串口读取或发送数据,进行逻辑处理(如控制电机、读取温度、灯光控制等)。

下面用更直观的流程图来展示主要步骤(扫描→配对→连接→收发→断开):

┌───────────────────────────────────────────────────────────────────┐
│                                                                   │
│   [1] 应用启动 -> 初始化库 -> 订阅事件                               │
│                              │                                    │
│                              ▼                                    │
│   [2] 开始扫描附近蓝牙设备(Classic)                               │
│                              │                                    │
│                              ▼                                    │
│   [3] 扫描结果列表:显示 HC-05 / HC-06 等设备                       │
│                              │                                    │
│                              ▼                                    │
│   [4] 用户点击 “连接”:触发 connectToDevice(UUID)                  │
│                              │                                    │
│                              ▼                                    │
│   [5] 与 HC-05 建立 RFCOMM 连接(SPP)                              │
│                              │                                    │
│                              ▼                                    │
│   [6] 发送/接收串口数据                                             │
│      - write(data) -> 手机蓝牙 -> HC-05 -> Arduino                 │
│      - 监听 dataReceived 事件 -> 获取 HC-05 返回数据                 │
│                              │                                    │
│                              ▼                                    │
│   [7] 用户点击 “断开”:disconnect()                                │
│                                                                   │
└───────────────────────────────────────────────────────────────────┘

权限设置

Android 权限

  1. AndroidManifest.xml:在项目 android/app/src/main/AndroidManifest.xml 中添加以下权限(<manifest> 节点下):

    <!-- 蓝牙基础权限 -->
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    
    <!-- Android 12+ 需要额外动态权限 -->
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation"/>
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
    
    <!-- 定位权限:部分 Android 版本扫描蓝牙需要定位授权 -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  2. 动态申请:在运行时,需要向用户申请定位权限(Android 6.0+)以及 Android 12+ 的蓝牙权限。可以使用 React Native 自带的 PermissionsAndroid

    import { PermissionsAndroid, Platform } from 'react-native';
    
    async function requestAndroidPermissions() {
      if (Platform.OS !== 'android') return true;
    
      const permissions = [];
      // Android 12+ 分别申请
      if (Platform.constants.Release >= '12') {
        permissions.push(PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN);
        permissions.push(PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT);
        permissions.push(PermissionsAndroid.PERMISSIONS.BLUETOOTH_ADVERTISE);
      }
      // 定位权限(扫描蓝牙)
      permissions.push(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION);
    
      try {
        const granted = await PermissionsAndroid.requestMultiple(permissions);
        // 检查是否都被授予
        const allGranted = Object.values(granted).every(status => status === PermissionsAndroid.RESULTS.GRANTED);
        return allGranted;
      } catch (err) {
        console.warn('权限申请失败', err);
        return false;
      }
    }

iOS 权限

ios/YourProject/Info.plist 中,添加如下键值:

<key>NSBluetoothAlwaysUsageDescription</key>
<string>应用需要使用蓝牙来连接设备并进行串口通信。</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>应用需要访问蓝牙外设来发送和接收数据。</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>扫描附近蓝牙设备需要定位权限。</string>
注意: iOS 13+ 要求 NSBluetoothAlwaysUsageDescription,并且如果要做蓝牙扫描,还需要 NSLocationWhenInUseUsageDescriptionNSLocationAlwaysUsageDescription

核心 API 详解

以下示例基于 react-native-bluetooth-serial-next,在代码里我们一般这样引用:

import RNBluetoothSerial from 'react-native-bluetooth-serial-next';

1. 初始化与事件订阅

  • 初始化库
    在 App 启动时,调用 RNBluetoothSerial.initialize() 进行初始化。通常可以放在最顶层组件的 useEffect 中。
  • 订阅事件
    react-native-bluetooth-serial-next 提供了多个事件回调,例如:

    • bluetoothEnabled:蓝牙已打开
    • bluetoothDisabled:蓝牙已关闭
    • deviceConnected:成功连接到设备
    • deviceDisconnected:设备断开
    • dataReceived:收到串口数据
    • error:错误回调
    import React, { useEffect, useState } from 'react';
    import { NativeEventEmitter, Platform } from 'react-native';
    import RNBluetoothSerial from 'react-native-bluetooth-serial-next';
    
    export default function useBluetoothInit() {
      const [enabled, setEnabled] = useState(false);
      const bluetoothEmitter = new NativeEventEmitter(RNBluetoothSerial);
    
      useEffect(() => {
        // 初始化
        RNBluetoothSerial.initialize()
          .then(result => {
            // result = { isEnabled: boolean }
            setEnabled(result.isEnabled);
          })
          .catch(err => console.error('初始化失败', err));
    
        // 订阅蓝牙开关事件
        const subEnabled = bluetoothEmitter.addListener('bluetoothEnabled', () => {
          console.log('蓝牙已打开');
          setEnabled(true);
        });
        const subDisabled = bluetoothEmitter.addListener('bluetoothDisabled', () => {
          console.log('蓝牙已关闭');
          setEnabled(false);
        });
    
        // 订阅数据接收
        const subData = bluetoothEmitter.addListener('dataReceived', ({ data, device }) => {
          console.log('收到数据:', data, '来自:', device.id);
        });
    
        return () => {
          subEnabled.remove();
          subDisabled.remove();
          subData.remove();
        };
      }, []);
    
      return enabled;
    }

2. 扫描设备

调用 RNBluetoothSerial.startScanning() 开始扫描,扫描时会收到 deviceFound 事件。也可以直接使用返回值:

async function scanDevices() {
  try {
    // 开始扫描,持续时间默认 15 秒
    const devices = await RNBluetoothSerial.startScanning({});
    // devices: Array<{ id: string, name: string }>
    console.log('扫描到设备列表:', devices);
    return devices;
  } catch (err) {
    console.error('扫描失败', err);
    return [];
  }
}
  • 参数说明:

    • 可以传递 { seconds: number } 指定扫描时长,单位为秒,默认为 15 秒。
  • 事件回调:

    bluetoothEmitter.addListener('deviceFound', device => {
      // device 示例:{ id: '00:11:22:33:44:55', name: 'HC-05' }
      console.log('发现新设备:', device);
    });

3. 连接设备

扫描到设备后,用户选择要连接的设备(通常根据 id 或者 name),调用 connect 方法进行连接:

async function connectToDevice(deviceId) {
  try {
    const connected = await RNBluetoothSerial.connect(deviceId);
    if (connected) {
      console.log('已连接到设备:', deviceId);
      return true;
    } else {
      console.warn('连接失败:', deviceId);
      return false;
    }
  } catch (err) {
    console.error('连接异常:', err);
    return false;
  }
}
  • 自动重连
    如果需要连接后自动重连,可以在 deviceDisconnected 事件中逻辑判断后再次调用 connectToDevice
  • 查看已配对设备
    如果想跳过扫描,直接获取手机之前已经配对过的设备,也可以调用:

    const paired = await RNBluetoothSerial.list();
    console.log('已配对设备:', paired);

    paired 的数据结构同扫描到的设备:[{ id: string, name: string }]

4. 发送数据

连接成功后,就可以调用 write 或者 writeToDevice 将数据写入对端串口。

async function sendData(text) {
  try {
    // 默认发送字符串,底层会转成 bytes 并通过 RFCOMM 发送
    await RNBluetoothSerial.write(text);
    console.log('发送成功:', text);
  } catch (err) {
    console.error('发送失败:', err);
  }
}
  • 写入示例

    // 发送 “LED_ON\n” 给 HC-05 模块
    sendData('LED_ON\n');
  • 写入 Buffer
    如果你想发送二进制数据,也可以传入 base64 字符串或字节数组,这里我们一般直接发 ASCII 即可。

5. 接收数据

5.1 通过事件监听

在前面初始化时,我们已经订阅了 dataReceived 事件,当设备端通过串口发送数据时,该回调会触发:

bluetoothEmitter.addListener('dataReceived', ({ data, device }) => {
  // data 为字符串,通常包含 \r\n 等换行符
  console.log(`从 ${device.id} 收到:`, data);
  // 你可以根据业务需求进行解析,例如:
  // const parsed = data.trim().split(',');
  // console.log('解析后的数组:', parsed);
});
5.2 主动读取缓存

如果你不想使用事件,也可以主动调用 readreadFromDevice,读取设备端发来的缓存数据(不过推荐使用事件):

async function readData() {
  try {
    const buffer = await RNBluetoothSerial.read();
    console.log('主动读取到数据:', buffer);
    return buffer;
  } catch (err) {
    console.warn('读取失败:', err);
    return '';
  }
}

6. 断开与清理

当不再需要通信时,务必调用 disconnect 来释放资源,并取消相关事件监听,防止内存泄漏。

async function disconnectDevice() {
  try {
    await RNBluetoothSerial.disconnect();
    console.log('已断开连接');
  } catch (err) {
    console.error('断开失败', err);
  }
}
  • 事件取消(在组件卸载时):

    useEffect(() => {
      // 假设在初始化时添加了三个 listener:subEnabled、subDisabled、subData
      return () => {
        subEnabled.remove();
        subDisabled.remove();
        subData.remove();
      };
    }, []);

完整示例:一个简单的串口控制页面

下面给出一个完整的 React Native 页面示例,包含扫描、展示设备列表、连接、发送指令、接收数据,并用图解的方式标注关键步骤。

1. 项目结构

MyBluetoothApp/
├─ android/
├─ ios/
├─ src/
│  ├─ components/
│  │   └─ DeviceItem.js
│  ├─ screens/
│  │   └─ BluetoothScreen.js
│  └─ App.js
├─ package.json
└─ ...
  • App.js:入口文件,导航到 BluetoothScreen
  • BluetoothScreen.js:实现 Bluetooth 扫描、连接、收发逻辑。
  • DeviceItem.js:展示单个设备的列表项。

2. 组件代码

2.1 DeviceItem.js(设备列表项)

// src/components/DeviceItem.js
import React from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';

export default function DeviceItem({ device, onPress }) {
  return (
    <TouchableOpacity style={styles.itemContainer} onPress={() => onPress(device)}>
      <Text style={styles.deviceName}>{device.name || '未知设备'}</Text>
      <Text style={styles.deviceId}>{device.id}</Text>
    </TouchableOpacity>
  );
}

const styles = StyleSheet.create({
  itemContainer: {
    padding: 12,
    borderBottomWidth: 0.5,
    borderColor: '#ccc',
  },
  deviceName: {
    fontSize: 16,
    fontWeight: '500',
  },
  deviceId: {
    fontSize: 12,
    color: '#666',
    marginTop: 4,
  },
});

2.2 BluetoothScreen.js(核心逻辑)

// src/screens/BluetoothScreen.js
import React, { useEffect, useState, useRef } from 'react';
import { View, Text, FlatList, Button, TextInput, TouchableOpacity, StyleSheet, Alert, Platform } from 'react-native';
import RNBluetoothSerial from 'react-native-bluetooth-serial-next';
import { PermissionsAndroid } from 'react-native';
import DeviceItem from '../components/DeviceItem';

export default function BluetoothScreen() {
  const [isEnabled, setIsEnabled] = useState(false);            // 蓝牙是否打开
  const [devices, setDevices] = useState([]);                   // 扫描到的设备列表
  const [connectingId, setConnectingId] = useState(null);       // 正在连接的设备 id
  const [connectedId, setConnectedId] = useState(null);         // 已连接设备 id
  const [logData, setLogData] = useState('');                   // 接收数据的日志
  const [inputText, setInputText] = useState('');               // 要发送的数据

  const bluetoothEmitter = useRef(new RNBluetoothSerial.BluetoothEventEmitter()).current;

  // 1. 初始化及订阅
  useEffect(() => {
    async function init() {
      // 申请 Android 权限
      if (Platform.OS === 'android') {
        const granted = await requestAndroidPermissions();
        if (!granted) {
          Alert.alert('权限不足', '缺少蓝牙或定位权限,无法进行扫描。');
          return;
        }
      }

      // 初始化蓝牙
      try {
        const result = await RNBluetoothSerial.initialize();
        setIsEnabled(result.isEnabled);
      } catch (err) {
        console.error('初始化异常:', err);
      }

      // 订阅蓝牙开关事件
      bluetoothEmitter.addListener('bluetoothEnabled', () => {
        setIsEnabled(true);
      });
      bluetoothEmitter.addListener('bluetoothDisabled', () => {
        setIsEnabled(false);
      });

      // 订阅连接事件
      bluetoothEmitter.addListener('deviceConnected', ({ device }) => {
        setConnectedId(device.id);
        setConnectingId(null);
        Alert.alert('连接成功', `已连接:${device.name}`);
      });
      bluetoothEmitter.addListener('deviceDisconnected', ({ device }) => {
        if (device.id === connectedId) {
          setConnectedId(null);
          Alert.alert('断开连接', `设备 ${device.name} 已断开`);
        }
      });

      // 订阅接收数据
      bluetoothEmitter.addListener('dataReceived', ({ data, device }) => {
        setLogData(prev => prev + `\n[${device.name || device.id}] ${data}`);
      });
    }

    init();

    return () => {
      // 注销事件监听
      bluetoothEmitter.removeAllListeners('bluetoothEnabled');
      bluetoothEmitter.removeAllListeners('bluetoothDisabled');
      bluetoothEmitter.removeAllListeners('deviceConnected');
      bluetoothEmitter.removeAllListeners('deviceDisconnected');
      bluetoothEmitter.removeAllListeners('dataReceived');
    };
  }, []);

  // 2. 扫描设备
  const scanDevices = async () => {
    try {
      setDevices([]); // 清空旧列表
      const list = await RNBluetoothSerial.startScanning({ seconds: 8 });
      setDevices(list);
    } catch (err) {
      console.error('扫描失败:', err);
    }
  };

  // 3. 连接设备
  const connectDevice = async (device) => {
    setConnectingId(device.id);
    try {
      const ok = await RNBluetoothSerial.connect(device.id);
      if (!ok) {
        setConnectingId(null);
        Alert.alert('连接失败', `无法连接到 ${device.name}`);
      }
    } catch (err) {
      setConnectingId(null);
      console.error('连接异常:', err);
    }
  };

  // 4. 发送数据
  const sendData = async () => {
    if (!connectedId) {
      Alert.alert('未连接', '请先连接设备');
      return;
    }
    try {
      await RNBluetoothSerial.write(inputText + '\r\n');
      setLogData(prev => prev + `\n[我] ${inputText}`);
      setInputText('');
    } catch (err) {
      console.error('发送异常:', err);
    }
  };

  // 5. 断开连接
  const disconnectDevice = async () => {
    try {
      await RNBluetoothSerial.disconnect();
      setConnectedId(null);
      setLogData(prev => prev + '\n[系统] 已断开连接');
    } catch (err) {
      console.error('断开失败:', err);
    }
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>React Native 蓝牙串口通信 Demo</Text>
      <View style={styles.section}>
        <Text>蓝牙状态:{isEnabled ? '已开启' : '未开启'}</Text>
        <Button title="扫描设备" onPress={scanDevices} disabled={!isEnabled} />
      </View>

      <FlatList
        style={styles.list}
        data={devices}
        keyExtractor={item => item.id}
        renderItem={({ item }) => (
          <DeviceItem
            device={item}
            onPress={connectDevice}
            style={{
              backgroundColor: item.id === connectingId ? '#e0f7fa' : '#fff',
            }}
          />
        )}
        ListEmptyComponent={() => <Text style={styles.emptyText}>暂无扫描到设备</Text>}
      />

      {connectedId && (
        <View style={styles.section}>
          <Text>已连接:{connectedId}</Text>
          <TextInput
            style={styles.input}
            placeholder="请输入要发送的串口数据"
            value={inputText}
            onChangeText={setInputText}
          />
          <Button title="发送数据" onPress={sendData} />
          <View style={{ height: 10 }} />
          <Button title="断开连接" color="#e53935" onPress={disconnectDevice} />
        </View>
      )}

      <View style={styles.logContainer}>
        <Text style={styles.logTitle}>通信日志:</Text>
        <Text style={styles.logText}>{logData}</Text>
      </View>
    </View>
  );
}

// Android 动态申请权限
async function requestAndroidPermissions() {
  const permissions = [];
  if (Platform.Version >= 31) {
    permissions.push(PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN);
    permissions.push(PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT);
    permissions.push(PermissionsAndroid.PERMISSIONS.BLUETOOTH_ADVERTISE);
  }
  permissions.push(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION);

  try {
    const granted = await PermissionsAndroid.requestMultiple(permissions);
    return Object.values(granted).every(status => status === PermissionsAndroid.RESULTS.GRANTED);
  } catch (err) {
    console.warn('权限申请异常:', err);
    return false;
  }
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 12, backgroundColor: '#fafafa' },
  title: { fontSize: 20, fontWeight: '600', marginBottom: 12 },
  section: { marginVertical: 8 },
  list: { flex: 1, marginVertical: 8, borderWidth: 0.5, borderColor: '#ddd', borderRadius: 6 },
  emptyText: { textAlign: 'center', color: '#999', padding: 20 },
  input: {
    borderWidth: 0.8,
    borderColor: '#ccc',
    borderRadius: 4,
    paddingHorizontal: 8,
    paddingVertical: 4,
    marginVertical: 8,
  },
  logContainer: {
    flex: 1,
    marginTop: 12,
    padding: 8,
    borderWidth: 0.5,
    borderColor: '#ccc',
    borderRadius: 4,
    backgroundColor: '#fff',
  },
  logTitle: { fontWeight: '500', marginBottom: 4 },
  logText: { fontSize: 12, color: '#333' },
});

2.3 App.js(简单导航或直接渲染)

// src/App.js
import React from 'react';
import BluetoothScreen from './screens/BluetoothScreen';

export default function App() {
  return <BluetoothScreen />;
}

图解说明

  1. 初始化与事件订阅流程

    ┌──────────────────────────┐
    │ App 启动                 │
    │  → requestAndroidPermissions() │
    │  → RNBluetoothSerial.initialize() │
    └─────────────┬────────────┘
                  │
                  ▼
        注册事件监听:bluetoothEnabled / bluetoothDisabled / deviceConnected / dataReceived…
  2. 扫描 & 列表展示

    用户点击 “扫描设备” 
        ↓
    RNBluetoothSerial.startScanning({seconds:8}) 
        ↓
    onSuccess 返回设备数组,更新 state → FlatList 渲染列表
        ↓
    用户可点击某项设备,触发 connectDevice(item)
  3. 连接 & 数据交换

    用户点击设备 → connectToDevice
        ↓
    RNBluetoothSerial.connect(deviceId) → 建立 RFCOMM 连接
        ↓
    订阅 deviceConnected 事件 → 更新 connectedId
        ↓
    // 发送数据
    用户输入文本 → 点击 “发送数据” → write(input + '\r\n')
        ↓
    HC-05 通过串口(UART)收到数据 → Arduino 处理 → 可能通过 UART 回复
        ↓
    HC-05 将回复通过蓝牙 SPP 发送回手机 → 触发 dataReceived 事件 → 更新 logData
  4. 断开连接

    用户点击 “断开连接” 
        ↓
    RNBluetoothSerial.disconnect() 
        ↓
    触发 deviceDisconnected 事件 → 清空 connectedId

以上图解帮助你对蓝牙串口通信的时序与流程有更直观的认识。


注意事项与常见问题

  1. Classic 蓝牙 vs BLE

    • 本库使用 Classic Bluetooth SPP(串口协议),并非 BLE(Bluetooth Low Energy)。BLE 需要另用 react-native-ble-plx 等库。
    • SPP 可以直接当作串口,方便 Arduino、STM32 等微控制器通信。
  2. 蓝牙名称可能为 null / 空字符串

    • 某些设备出于隐私或低功耗考虑,名称可能为空,只能通过 MAC 地址判断。建议在界面上同时展示 id(MAC)与 name,并加以提示。
  3. Android 12+ 权限问题

    • Android 12(API 31)之后,扫描 需要 BLUETOOTH_SCAN连接 需要 BLUETOOTH_CONNECT。同时,扫描通常还需要定位权限。
    • 如需在后台扫描,可能还需要 ACCESS_BACKGROUND_LOCATION
  4. iOS 系统弹窗

    • 如果缺少 Info.plist 中对应的键,iOS 会直接导致崩溃或拒绝蓝牙请求。务必检查是否填写了 NSBluetoothAlwaysUsageDescriptionNSLocationWhenInUseUsageDescription 等项。
  5. 连接超时、重连逻辑

    • 某些设备在配对后并非立刻能够连接成功,如果连接失败,建议在 catch 中加上重试。
    • 也可在 deviceDisconnected 回调中,根据具体需求自动重连(谨慎使用,避免死循环重连)。
  6. 数据格式与换行

    • 大多数串口设备以 \r\n 作为一条指令或数据结束符,发送时建议在末尾加上换行符。也可以在 initialize() 时传递 分隔符 让库自动做数据分片。
    • 如果接收乱码,请确认手机蓝牙与设备蓝牙波特率、数据位、停止位、校验位等是否匹配(通常 HC-05 默认 9600、8N1)。
  7. 调试方式

    • 串口调试助手:在 Windows 或 Mac 上使用串口助手(如 SecureCRT、PuTTY、CoolTerm 等)先调试 HC-05 与 Arduino,确保指令逻辑正常。
    • 日志打印:React Native 层面可开启 adb logcat(Android)或 Xcode 控制台,定位蓝牙模块的连接/断开/异常。

总结

本文从安装、配置、原理到完整代码示例,详细讲解了如何使用 react-native-bluetooth-serial-next(也称 React Native Bluetooth Serial)实现蓝牙串口通信。核心流程包括:

  1. 初始化:申请权限 → 初始化库 → 订阅蓝牙事件
  2. 扫描:调用 startScanning,获取设备列表
  3. 连接:调用 connect,建立 RFCOMM 连接
  4. 收发:通过 writedataReceived 完成数据交换
  5. 断开:调用 disconnect,释放资源

通过以上步骤,你可以在 React Native 应用中快速搭建一个蓝牙串口调试或控制界面。例如,控制 Arduino 上的 LED 开关、读取传感器数据、远程控制舵机等。

扩展思考:

  • 如果项目后续对低功耗、广播查询等需求增多,可考虑使用 BLE 并结合 react-native-ble-plx
  • 如果需要在后台持续扫描或连接,需要结合原生模块做更多权限及生命周期管理。

希望这篇详细的图文+代码示例文章,能帮助你更快速地上手 React Native 蓝牙串口通信,打造自己的“蓝牙神器”!

最后修改于:2025年05月30日 11:06

评论已关闭

推荐阅读

DDPG 模型解析,附Pytorch完整代码
2024年11月24日
DQN 模型解析,附Pytorch完整代码
2024年11月24日
AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日