HarmonyOS APP开发RFCOMM通信
【摘要】 HarmonyOS APP开发RFCOMM通信BLE适合小数据量的传感器场景,但如果你需要传输音频、文件或者建立类似串口的通信,那就得请出"老大哥"——经典蓝牙了。今天咱们就来聊聊鸿蒙系统中的经典蓝牙开发,特别是RFCOMM协议的使用,看看如何实现设备间的流式数据传输。 一、背景与动机 1.1 经典蓝牙与BLE的区别先来搞清楚两者的定位差异:对比项经典蓝牙BLE蓝牙传输速率1-3 Mbps...
HarmonyOS APP开发RFCOMM通信
BLE适合小数据量的传感器场景,但如果你需要传输音频、文件或者建立类似串口的通信,那就得请出"老大哥"——经典蓝牙了。今天咱们就来聊聊鸿蒙系统中的经典蓝牙开发,特别是RFCOMM协议的使用,看看如何实现设备间的流式数据传输。
一、背景与动机
1.1 经典蓝牙与BLE的区别
先来搞清楚两者的定位差异:
| 对比项 | 经典蓝牙 | BLE蓝牙 |
|---|---|---|
| 传输速率 | 1-3 Mbps | ~1 Mbps |
| 连接时间 | 数秒 | 毫秒级 |
| 功耗 | 高(通话级) | 极低 |
| 典型应用 | 音频、文件传输、串口通信 | 传感器、控制指令 |
| 协议栈 | RFCOMM、SDP、L2CAP等 | GATT、ATT、GAP |
| 拓扑 | 点对点 | 星型拓扑 |
经典蓝牙的典型应用:
- 音频传输:蓝牙耳机、音箱、车载系统
- 文件传输:手机间文件分享(虽然现在用得少了)
- 串口模拟:打印机、POS机、工业设备通信
- 网络共享:蓝牙 tethering
1.2 RFCOMM协议简介
RFCOMM是经典蓝牙的核心协议之一,它提供了基于L2CAP的串口模拟:

RFCOMM核心概念:
- Channel(通道):类似TCP端口,用于区分不同的服务
- Server Socket:服务端监听,等待连接
- Client Socket:客户端连接,发起通信
- SDP(Service Discovery Protocol):服务发现,让客户端找到服务端
二、核心原理
2.1 RFCOMM通信流程

2.2 SDP服务记录
SDP记录描述了服务的属性,让客户端能够发现和识别服务:
interface SdpRecord {
// 服务UUID
serviceUuid: string;
// 服务名称
serviceName: string;
// 服务描述
serviceDescription?: string;
// 服务提供者
providerName?: string;
// RFCOMM通道号
rfcommChannel: number;
// 支持的协议
protocols: string[];
}
2.3 Socket通信模型
RFCOMM的Socket通信与TCP Socket类似:
// Socket状态
enum SocketState {
CLOSED = 0, // 已关闭
LISTENING = 1, // 监听中
CONNECTING = 2, // 连接中
CONNECTED = 3, // 已连接
ERROR = 4 // 错误
}
// Socket事件
interface SocketEvent {
type: 'connect' | 'data' | 'error' | 'close';
data?: ArrayBuffer;
error?: Error;
}
三、代码实战
3.1 经典蓝牙管理类封装
import bluetooth from '@ohos.bluetooth';
import socket from '@ohos.net.socket';
import { BusinessError } from '@ohos.base';
/**
* 经典蓝牙设备信息
*/
export interface ClassicBluetoothDevice {
// 设备地址
deviceId: string;
// 设备名称
deviceName: string;
// 设备类型
deviceClass: number;
// 配对状态
bondState: bluetooth.BondState;
// 信号强度
rssi?: number;
}
/**
* 经典蓝牙管理器
* 封装设备发现、配对、RFCOMM通信等功能
*/
export class ClassicBluetoothManager {
private static instance: ClassicBluetoothManager;
// 已发现的设备
private discoveredDevices: Map<string, ClassicBluetoothDevice> = new Map();
// RFCOMM服务端Socket
private serverSocket: bluetooth.BluetoothSocket | null = null;
// RFCOMM客户端Socket
private clientSocket: bluetooth.BluetoothSocket | null = null;
// 回调函数
private onDeviceFound: ((device: ClassicBluetoothDevice) => void) | null = null;
private onBondStateChanged: ((device: ClassicBluetoothDevice) => void) | null = null;
private onConnectionChanged: ((state: number) => void) | null = null;
private onDataReceived: ((data: ArrayBuffer) => void) | null = null;
private constructor() {
this.initEventListeners();
}
/**
* 获取单例实例
*/
public static getInstance(): ClassicBluetoothManager {
if (!ClassicBluetoothManager.instance) {
ClassicBluetoothManager.instance = new ClassicBluetoothManager();
}
return ClassicBluetoothManager.instance;
}
/**
* 初始化事件监听
*/
private initEventListeners(): void {
// 监听设备发现
bluetooth.on('bluetoothDeviceFind', (devices: Array<bluetooth.DeviceInfo>) => {
devices.forEach((device) => {
const deviceInfo: ClassicBluetoothDevice = {
deviceId: device.deviceId,
deviceName: device.deviceName || 'Unknown',
deviceClass: device.deviceClass,
bondState: bluetooth.BondState.BOND_STATE_INVALID
};
this.discoveredDevices.set(deviceInfo.deviceId, deviceInfo);
if (this.onDeviceFound) {
this.onDeviceFound(deviceInfo);
}
});
});
// 监听配对状态变化
bluetooth.on('bondStateChange', (data: { deviceId: string, bondState: bluetooth.BondState }) => {
const device = this.discoveredDevices.get(data.deviceId);
if (device) {
device.bondState = data.bondState;
if (this.onBondStateChanged) {
this.onBondStateChanged(device);
}
}
});
// 监听蓝牙状态变化
bluetooth.on('stateChange', (state: bluetooth.BluetoothState) => {
console.info(`[经典蓝牙] 状态变化: ${state}`);
});
}
/**
* 检查蓝牙是否开启
*/
public async isBluetoothEnabled(): Promise<boolean> {
try {
const state = bluetooth.getState();
return state === bluetooth.BluetoothState.STATE_ON;
} catch (error) {
console.error('[经典蓝牙] 检查状态失败:', error);
return false;
}
}
/**
* 开启蓝牙
*/
public async enableBluetooth(): Promise<boolean> {
try {
await bluetooth.enableBluetooth();
console.info('[经典蓝牙] 已开启');
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[经典蓝牙] 开启失败: ${err.message}`);
return false;
}
}
/**
* 关闭蓝牙
*/
public async disableBluetooth(): Promise<boolean> {
try {
await bluetooth.disableBluetooth();
console.info('[经典蓝牙] 已关闭');
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[经典蓝牙] 关闭失败: ${err.message}`);
return false;
}
}
/**
* 开始设备发现
*/
public async startDiscovery(): Promise<boolean> {
try {
// 清空已发现的设备
this.discoveredDevices.clear();
await bluetooth.startBluetoothDiscovery();
console.info('[经典蓝牙] 开始发现设备');
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[经典蓝牙] 发现失败: ${err.message}`);
return false;
}
}
/**
* 停止设备发现
*/
public async stopDiscovery(): Promise<boolean> {
try {
await bluetooth.stopBluetoothDiscovery();
console.info('[经典蓝牙] 停止发现');
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[经典蓝牙] 停止失败: ${err.message}`);
return false;
}
}
/**
* 获取已发现的设备列表
*/
public getDiscoveredDevices(): Array<ClassicBluetoothDevice> {
return Array.from(this.discoveredDevices.values());
}
/**
* 获取已配对的设备列表
*/
public async getPairedDevices(): Promise<Array<ClassicBluetoothDevice>> {
try {
const devices = await bluetooth.getPairedDevices();
return devices.map((device) => ({
deviceId: device.deviceId,
deviceName: device.deviceName || 'Unknown',
deviceClass: device.deviceClass,
bondState: bluetooth.BondState.BOND_STATE_BONDED
}));
} catch (error) {
console.error('[经典蓝牙] 获取配对设备失败:', error);
return [];
}
}
/**
* 发起配对
*/
public async pairDevice(deviceId: string): Promise<boolean> {
try {
await bluetooth.pairDevice(deviceId);
console.info(`[经典蓝牙] 正在配对 ${deviceId}`);
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[经典蓝牙] 配对失败: ${err.message}`);
return false;
}
}
/**
* 取消配对
*/
public async unpairDevice(deviceId: string): Promise<boolean> {
try {
await bluetooth.unpairDevice(deviceId);
console.info(`[经典蓝牙] 已取消配对 ${deviceId}`);
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[经典蓝牙] 取消配对失败: ${err.message}`);
return false;
}
}
/**
* 创建RFCOMM服务端
* @param serviceName 服务名称
* @param uuid 服务UUID
*/
public async createRfcommServer(
serviceName: string,
uuid: string
): Promise<number | null> {
try {
// 创建Socket
this.serverSocket = bluetooth.createBluetoothSocket(
bluetooth.SocketType.SOCKET_RFCOMM
);
// 监听连接
this.serverSocket.on('connection', (client: bluetooth.BluetoothSocket) => {
console.info('[经典蓝牙] 客户端已连接');
this.clientSocket = client;
this.setupDataHandler(client);
if (this.onConnectionChanged) {
this.onConnectionChanged(3); // CONNECTED
}
});
// 绑定并监听
const channel = await this.serverSocket.listen({
uuid: uuid,
name: serviceName
});
console.info(`[经典蓝牙] 服务端已创建,通道: ${channel}`);
return channel;
} catch (error) {
console.error('[经典蓝牙] 创建服务端失败:', error);
return null;
}
}
/**
* 连接到RFCOMM服务端
* @param deviceId 设备地址
* @param uuid 服务UUID
*/
public async connectRfcomm(
deviceId: string,
uuid: string
): Promise<boolean> {
try {
// 创建Socket
this.clientSocket = bluetooth.createBluetoothSocket(
bluetooth.SocketType.SOCKET_RFCOMM
);
// 设置事件处理
this.setupDataHandler(this.clientSocket);
// 连接
await this.clientSocket.connect({
deviceId: deviceId,
uuid: uuid
});
console.info(`[经典蓝牙] 已连接到 ${deviceId}`);
if (this.onConnectionChanged) {
this.onConnectionChanged(3); // CONNECTED
}
return true;
} catch (error) {
const err = error as BusinessError;
console.error(`[经典蓝牙] 连接失败: ${err.message}`);
return false;
}
}
/**
* 设置数据处理器
*/
private setupDataHandler(socket: bluetooth.BluetoothSocket): void {
socket.on('data', (data: ArrayBuffer) => {
if (this.onDataReceived) {
this.onDataReceived(data);
}
});
socket.on('error', (err: Error) => {
console.error('[经典蓝牙] Socket错误:', err);
if (this.onConnectionChanged) {
this.onConnectionChanged(4); // ERROR
}
});
socket.on('close', () => {
console.info('[经典蓝牙] Socket已关闭');
if (this.onConnectionChanged) {
this.onConnectionChanged(0); // CLOSED
}
});
}
/**
* 发送数据
*/
public async sendData(data: ArrayBuffer): Promise<boolean> {
if (!this.clientSocket) {
console.error('[经典蓝牙] 未连接');
return false;
}
try {
await this.clientSocket.send(data);
return true;
} catch (error) {
console.error('[经典蓝牙] 发送失败:', error);
return false;
}
}
/**
* 发送字符串
*/
public async sendString(text: string): Promise<boolean> {
const encoder = new TextEncoder();
const data = encoder.encode(text).buffer;
return await this.sendData(data);
}
/**
* 断开连接
*/
public async disconnect(): Promise<void> {
if (this.clientSocket) {
await this.clientSocket.close();
this.clientSocket = null;
}
if (this.serverSocket) {
await this.serverSocket.close();
this.serverSocket = null;
}
console.info('[经典蓝牙] 已断开');
}
/**
* 设置回调函数
*/
public setOnDeviceFound(callback: (device: ClassicBluetoothDevice) => void): void {
this.onDeviceFound = callback;
}
public setOnBondStateChanged(callback: (device: ClassicBluetoothDevice) => void): void {
this.onBondStateChanged = callback;
}
public setOnConnectionChanged(callback: (state: number) => void): void {
this.onConnectionChanged = callback;
}
public setOnDataReceived(callback: (data: ArrayBuffer) => void): void {
this.onDataReceived = callback;
}
/**
* 移除所有监听
*/
public removeAllListeners(): void {
bluetooth.off('bluetoothDeviceFind');
bluetooth.off('bondStateChange');
bluetooth.off('stateChange');
if (this.clientSocket) {
this.clientSocket.off('data');
this.clientSocket.off('error');
this.clientSocket.off('close');
}
if (this.serverSocket) {
this.serverSocket.off('connection');
}
console.info('[经典蓝牙] 已移除所有监听');
}
}
3.2 RFCOMM聊天应用示例
import { ClassicBluetoothManager, ClassicBluetoothDevice } from './ClassicBluetoothManager';
/**
* RFCOMM聊天应用
* 两台设备间通过经典蓝牙聊天
*/
@Entry
@Component
struct BluetoothChatPage {
private btManager: ClassicBluetoothManager = ClassicBluetoothManager.getInstance();
// 聊天服务UUID
private readonly CHAT_SERVICE_UUID = '00001101-0000-1000-8000-00805F9B34FB';
@State isBluetoothEnabled: boolean = false;
@State isDiscovering: boolean = false;
@State discoveredDevices: Array<ClassicBluetoothDevice> = [];
@State pairedDevices: Array<ClassicBluetoothDevice> = [];
@State isConnected: boolean = false;
@State connectedDeviceName: string = '';
@State messages: Array<{ text: string, isMine: boolean }> = [];
@State inputMessage: string = '';
@State isServer: boolean = false;
aboutToAppear(): void {
this.checkBluetoothState();
this.loadPairedDevices();
this.registerCallbacks();
}
aboutToDisappear(): void {
this.btManager.disconnect();
this.btManager.removeAllListeners();
}
/**
* 检查蓝牙状态
*/
async checkBluetoothState(): Promise<void> {
this.isBluetoothEnabled = await this.btManager.isBluetoothEnabled();
}
/**
* 加载已配对设备
*/
async loadPairedDevices(): Promise<void> {
this.pairedDevices = await this.btManager.getPairedDevices();
}
/**
* 注册回调
*/
registerCallbacks(): void {
// 设备发现回调
this.btManager.setOnDeviceFound((device: ClassicBluetoothDevice) => {
const index = this.discoveredDevices.findIndex(d => d.deviceId === device.deviceId);
if (index >= 0) {
this.discoveredDevices[index] = device;
} else {
this.discoveredDevices.push(device);
}
});
// 配对状态回调
this.btManager.setOnBondStateChanged((device: ClassicBluetoothDevice) => {
this.loadPairedDevices();
});
// 连接状态回调
this.btManager.setOnConnectionChanged((state: number) => {
this.isConnected = state === 3;
if (!this.isConnected) {
this.connectedDeviceName = '';
}
});
// 数据接收回调
this.btManager.setOnDataReceived((data: ArrayBuffer) => {
const decoder = new TextDecoder();
const text = decoder.decode(data);
this.messages.push({ text: text, isMine: false });
});
}
/**
* 开启蓝牙
*/
async enableBluetooth(): Promise<void> {
const success = await this.btManager.enableBluetooth();
if (success) {
this.isBluetoothEnabled = true;
}
}
/**
* 开始发现设备
*/
async startDiscovery(): Promise<void> {
this.isDiscovering = true;
this.discoveredDevices = [];
const success = await this.btManager.startDiscovery();
if (!success) {
this.isDiscovering = false;
}
// 设置超时
setTimeout(() => {
if (this.isDiscovering) {
this.stopDiscovery();
}
}, 60000);
}
/**
* 停止发现
*/
async stopDiscovery(): Promise<void> {
await this.btManager.stopDiscovery();
this.isDiscovering = false;
}
/**
* 创建聊天服务(作为服务端)
*/
async createChatServer(): Promise<void> {
const channel = await this.btManager.createRfcommServer(
'BluetoothChat',
this.CHAT_SERVICE_UUID
);
if (channel !== null) {
this.isServer = true;
console.info(`[聊天] 服务已创建,通道: ${channel}`);
}
}
/**
* 连接到设备(作为客户端)
*/
async connectToDevice(device: ClassicBluetoothDevice): Promise<void> {
await this.stopDiscovery();
// 如果未配对,先配对
if (device.bondState !== bluetooth.BondState.BOND_STATE_BONDED) {
const paired = await this.btManager.pairDevice(device.deviceId);
if (!paired) {
console.error('[聊天] 配对失败');
return;
}
}
// 连接
const connected = await this.btManager.connectRfcomm(
device.deviceId,
this.CHAT_SERVICE_UUID
);
if (connected) {
this.connectedDeviceName = device.deviceName;
}
}
/**
* 发送消息
*/
async sendMessage(): Promise<void> {
if (!this.inputMessage || !this.isConnected) {
return;
}
const success = await this.btManager.sendString(this.inputMessage);
if (success) {
this.messages.push({ text: this.inputMessage, isMine: true });
this.inputMessage = '';
}
}
build() {
Column() {
// 标题栏
Row() {
Text('蓝牙聊天')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
Blank()
Toggle({ type: ToggleType.Switch, isOn: this.isBluetoothEnabled })
.onChange((isOn: boolean) => {
if (isOn) {
this.enableBluetooth();
} else {
this.btManager.disableBluetooth();
this.isBluetoothEnabled = false;
}
})
}
.width('100%')
.padding(20)
.backgroundColor('#1A1A2E')
if (this.isBluetoothEnabled) {
if (this.isConnected) {
// 聊天界面
this.ChatView()
} else {
// 连接界面
this.ConnectionView()
}
} else {
// 提示
Column() {
Text('蓝牙未开启')
.fontSize(18)
.fontColor('#AAAAAA')
.margin({ top: 100 })
}
}
}
.width('100%')
.height('100%')
.backgroundColor('#0F0F1A')
}
/**
* 连接界面
*/
@Builder
ConnectionView() {
Column() {
// 创建服务按钮
Button('创建聊天服务')
.width('90%')
.height(50)
.backgroundColor('#4A90E2')
.onClick(() => {
this.createChatServer();
})
.margin({ top: 20 })
// 发现设备按钮
Button(this.isDiscovering ? '发现中...' : '发现设备')
.width('90%')
.height(50)
.backgroundColor(this.isDiscovering ? '#666666' : '#4CAF50')
.enabled(!this.isDiscovering)
.onClick(() => {
this.startDiscovery();
})
.margin({ top: 15 })
// 已配对设备
if (this.pairedDevices.length > 0) {
Column() {
Text('已配对设备')
.fontSize(16)
.fontColor('#FFFFFF')
.width('100%')
.margin({ bottom: 10 })
ForEach(this.pairedDevices, (device: ClassicBluetoothDevice) => {
Row() {
Text(device.deviceName)
.fontSize(14)
.fontColor('#FFFFFF')
.layoutWeight(1)
Button('连接')
.width(70)
.height(35)
.backgroundColor('#4A90E2')
.onClick(() => {
this.connectToDevice(device);
})
}
.width('100%')
.padding(10)
.backgroundColor('#16213E')
.borderRadius(8)
.margin({ bottom: 8 })
})
}
.width('90%')
.padding(15)
.backgroundColor('#1A1A2E')
.borderRadius(12)
.margin({ top: 20 })
}
// 发现的设备
if (this.discoveredDevices.length > 0) {
Column() {
Text(`发现 ${this.discoveredDevices.length} 台设备`)
.fontSize(16)
.fontColor('#FFFFFF')
.width('100%')
.margin({ bottom: 10 })
List() {
ForEach(this.discoveredDevices, (device: ClassicBluetoothDevice) => {
ListItem() {
Row() {
Column() {
Text(device.deviceName)
.fontSize(14)
.fontColor('#FFFFFF')
Text(device.deviceId.substring(0, 8) + '...')
.fontSize(12)
.fontColor('#AAAAAA')
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Button('连接')
.width(70)
.height(35)
.backgroundColor('#4CAF50')
.onClick(() => {
this.connectToDevice(device);
})
}
.width('100%')
.padding(10)
.backgroundColor('#16213E')
.borderRadius(8)
}
}, (device: ClassicBluetoothDevice) => device.deviceId)
}
.width('100%')
.height('40%')
}
.width('90%')
.padding(15)
.backgroundColor('#1A1A2E')
.borderRadius(12)
.margin({ top: 20 })
}
}
.width('100%')
.layoutWeight(1)
}
/**
* 聊天界面
*/
@Builder
ChatView() {
Column() {
// 连接信息
Row() {
Text(this.isServer ? '服务端' : '客户端')
.fontSize(14)
.fontColor('#AAAAAA')
Text(this.connectedDeviceName)
.fontSize(14)
.fontColor('#FFFFFF')
.margin({ left: 10 })
Blank()
Button('断开')
.fontSize(12)
.height(30)
.backgroundColor('#FF5722')
.onClick(() => {
this.btManager.disconnect();
})
}
.width('90%')
.padding(10)
.backgroundColor('#16213E')
.borderRadius(8)
.margin({ top: 20 })
// 消息列表
List() {
ForEach(this.messages, (msg: { text: string, isMine: boolean }, index: number) => {
ListItem() {
Row() {
if (msg.isMine) {
Blank()
}
Text(msg.text)
.fontSize(14)
.fontColor('#FFFFFF')
.padding(10)
.backgroundColor(msg.isMine ? '#4A90E2' : '#4CAF50')
.borderRadius(12)
.maxWidth('70%')
if (!msg.isMine) {
Blank()
}
}
.width('100%')
.justifyContent(msg.isMine ? FlexAlign.End : FlexAlign.Start)
}
.margin({ bottom: 10 })
})
}
.width('90%')
.layoutWeight(1)
.margin({ top: 20 })
// 输入区域
Row() {
TextInput({ text: $$this.inputMessage, placeholder: '输入消息' })
.width('70%')
.height(45)
.backgroundColor('#16213E')
.fontColor('#FFFFFF')
Button('发送')
.width('25%')
.height(45)
.backgroundColor('#4A90E2')
.onClick(() => {
this.sendMessage();
})
}
.width('90%')
.justifyContent(FlexAlign.SpaceBetween)
.margin({ top: 15, bottom: 20 })
}
.width('100%')
.layoutWeight(1)
}
}
3.3 蓝牙打印机示例
import { ClassicBluetoothManager } from './ClassicBluetoothManager';
/**
* 蓝牙打印机控制器
* 通过RFCOMM发送打印指令
*/
export class BluetoothPrinter {
private btManager: ClassicBluetoothManager = ClassicBluetoothManager.getInstance();
// 串口服务UUID(SPP)
private readonly SPP_UUID = '00001101-0000-1000-8000-00805F9B34FB';
// 打印机设备地址
private printerAddress: string = '';
/**
* 连接打印机
*/
public async connect(address: string): Promise<boolean> {
this.printerAddress = address;
return await this.btManager.connectRfcomm(address, this.SPP_UUID);
}
/**
* 断开连接
*/
public async disconnect(): Promise<void> {
await this.btManager.disconnect();
}
/**
* 打印文本
*/
public async printText(text: string): Promise<boolean> {
// ESC/POS指令
const commands: number[] = [];
// 初始化打印机
commands.push(0x1B, 0x40);
// 设置字体大小
commands.push(0x1D, 0x21, 0x00); // 正常大小
// 打印文本
for (let i = 0; i < text.length; i++) {
commands.push(text.charCodeAt(i));
}
// 换行
commands.push(0x0A);
return await this.sendCommands(commands);
}
/**
* 打印条形码
*/
public async printBarcode(data: string, type: number = 0x41): Promise<boolean> {
const commands: number[] = [];
// 初始化
commands.push(0x1B, 0x40);
// 设置条形码高度
commands.push(0x1D, 0x68, 0x50);
// 设置条形码宽度
commands.push(0x1D, 0x77, 0x02);
// 打印条形码
commands.push(0x1D, 0x6B, type, data.length);
// 条形码数据
for (let i = 0; i < data.length; i++) {
commands.push(data.charCodeAt(i));
}
return await this.sendCommands(commands);
}
/**
* 打印二维码
*/
public async printQRCode(data: string): Promise<boolean> {
const commands: number[] = [];
// 初始化
commands.push(0x1B, 0x40);
// 设置二维码模型
commands.push(0x1D, 0x28, 0x6B, 0x04, 0x00, 0x31, 0x41, 0x32, 0x00);
// 设置二维码大小
commands.push(0x1D, 0x28, 0x6B, 0x03, 0x00, 0x31, 0x43, 0x08);
// 设置纠错等级
commands.push(0x1D, 0x28, 0x6B, 0x03, 0x00, 0x31, 0x45, 0x31);
// 发送数据
const dataLen = data.length + 3;
commands.push(0x1D, 0x28, 0x6B, dataLen & 0xFF, (dataLen >> 8) & 0xFF, 0x31, 0x50, 0x30);
for (let i = 0; i < data.length; i++) {
commands.push(data.charCodeAt(i));
}
// 打印二维码
commands.push(0x1D, 0x28, 0x6B, 0x03, 0x00, 0x31, 0x51, 0x30);
return await this.sendCommands(commands);
}
/**
* 切纸
*/
public async cutPaper(): Promise<boolean> {
const commands: number[] = [
0x1D, 0x56, 0x00 // 全切
];
return await this.sendCommands(commands);
}
/**
* 发送指令
*/
private async sendCommands(commands: number[]): Promise<boolean> {
const buffer = new ArrayBuffer(commands.length);
const view = new Uint8Array(buffer);
for (let i = 0; i < commands.length; i++) {
view[i] = commands[i];
}
return await this.btManager.sendData(buffer);
}
}
四、踩坑与注意事项
4.1 权限配置
// module.json5
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.USE_BLUETOOTH",
"reason": "使用蓝牙功能"
},
{
"name": "ohos.permission.DISCOVER_BLUETOOTH",
"reason": "发现蓝牙设备"
},
{
"name": "ohos.permission.MANAGE_BLUETOOTH",
"reason": "管理蓝牙设置"
},
{
"name": "ohos.permission.LOCATION",
"reason": "蓝牙扫描需要位置权限"
}
]
}
}
4.2 配对流程处理
坑点:配对可能需要用户确认PIN码。
/**
* 监听配对请求
*/
bluetooth.on('pinRequest', (data: { deviceId: string, pinCode: string }) => {
console.info(`[配对] 设备 ${data.deviceId} 请求配对,PIN: ${data.pinCode}`);
// 自动确认(谨慎使用)
bluetooth.pairDeviceConfirm(data.deviceId, true);
});
// 监听配对确认请求
bluetooth.on('bondStateChange', (data: { deviceId: string, bondState: bluetooth.BondState }) => {
if (data.bondState === bluetooth.BondState.BOND_STATE_BONDING) {
console.info('[配对] 正在配对...');
} else if (data.bondState === bluetooth.BondState.BOND_STATE_BONDED) {
console.info('[配对] 配对成功');
} else if (data.bondState === bluetooth.BondState.BOND_STATE_NONE) {
console.info('[配对] 配对失败');
}
});
4.3 连接超时处理
/**
* 带超时的RFCOMM连接
*/
async function connectWithTimeout(
deviceId: string,
uuid: string,
timeout: number = 20000
): Promise<boolean> {
return new Promise(async (resolve) => {
let resolved = false;
const timer = setTimeout(() => {
if (!resolved) {
resolved = true;
resolve(false);
console.warn('[RFCOMM] 连接超时');
}
}, timeout);
try {
const success = await btManager.connectRfcomm(deviceId, uuid);
if (!resolved) {
resolved = true;
clearTimeout(timer);
resolve(success);
}
} catch (error) {
if (!resolved) {
resolved = true;
clearTimeout(timer);
resolve(false);
}
}
});
}
4.4 大数据分块发送
坑点:RFCOMM有MTU限制,大数据需要分块。
/**
* 分块发送大数据
*/
async function sendDataInChunks(
data: ArrayBuffer,
chunkSize: number = 1024
): Promise<boolean> {
const totalLength = data.byteLength;
const chunkCount = Math.ceil(totalLength / chunkSize);
for (let i = 0; i < chunkCount; i++) {
const start = i * chunkSize;
const end = Math.min(start + chunkSize, totalLength);
const chunk = data.slice(start, end);
const success = await btManager.sendData(chunk);
if (!success) {
console.error(`[RFCOMM] 发送第 ${i + 1} 块失败`);
return false;
}
// 等待确认
await delay(50);
}
console.info(`[RFCOMM] 发送完成,共 ${chunkCount} 块`);
return true;
}
function delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
4.5 设备兼容性
坑点:不同设备的UUID可能不同。
/**
* 常见服务UUID
*/
const COMMON_UUIDS = {
// 串口服务(SPP)
SPP: '00001101-0000-1000-8000-00805F9B34FB',
// 音频网关
HSP_AG: '00001112-0000-1000-8000-00805F9B34FB',
// 免提
HFP_AG: '0000111F-0000-1000-8000-00805F9B34FB',
// 文件传输
FTP: '00001106-0000-1000-8000-00805F9B34FB',
// 对象推送
OPP: '00001105-0000-1000-8000-00805F9B34FB'
};
/**
* 尝试连接多个UUID
*/
async function tryConnectMultipleUuids(
deviceId: string,
uuids: string[]
): Promise<boolean> {
for (const uuid of uuids) {
try {
const success = await btManager.connectRfcomm(deviceId, uuid);
if (success) {
console.info(`[RFCOMM] 使用UUID ${uuid} 连接成功`);
return true;
}
} catch (error) {
console.warn(`[RFCOMM] UUID ${uuid} 连接失败`);
}
}
return false;
}
五、HarmonyOS 6适配
5.1 API变更
| API | HarmonyOS 5 | HarmonyOS 6 | 说明 |
|---|---|---|---|
| createBluetoothSocket() | 同步创建 | 异步创建 | 统一异步模式 |
| connect() | 基础功能 | 支持超时设置 | 新增timeout参数 |
| 新增 | - | getSocketInfo() | 获取Socket详细信息 |
适配代码:
/**
* HarmonyOS 6 RFCOMM适配
*/
async function createSocketAdaptive(): Promise<bluetooth.BluetoothSocket | null> {
const apiVersion = getApiVersion();
try {
if (apiVersion >= 6) {
// HarmonyOS 6
const socket = await bluetooth.createBluetoothSocket(
bluetooth.SocketType.SOCKET_RFCOMM
);
return socket;
} else {
// HarmonyOS 5
const socket = bluetooth.createBluetoothSocket(
bluetooth.SocketType.SOCKET_RFCOMM
);
return socket;
}
} catch (error) {
console.error('[RFCOMM] 创建Socket失败:', error);
return null;
}
}
5.2 新增功能
HarmonyOS 6增强了经典蓝牙能力:
// 1. 获取Socket统计信息
const socketInfo = await socket.getSocketInfo();
console.info(`发送: ${socketInfo.bytesSent} 字节`);
console.info(`接收: ${socketInfo.bytesReceived} 字节`);
// 2. 设置Socket选项
await socket.setSocketOption({
sendBufferSize: 8192,
receiveBufferSize: 8192,
keepAlive: true,
noDelay: true
});
// 3. 蓝牙设备电量查询
const batteryLevel = await bluetooth.getDeviceBattery(deviceId);
console.info(`设备电量: ${batteryLevel}%`);
// 4. 连接质量评估
const linkQuality = await bluetooth.getLinkQuality(deviceId);
console.info(`连接质量: ${linkQuality}`);
5.3 性能优化
// 1. 调整传输功率
await bluetooth.setTransmitPowerLevel(deviceId, 'high');
// 2. 设置链路超时
await bluetooth.setLinkTimeout(deviceId, 30000); // 30秒
// 3. 启用数据压缩(如果设备支持)
await socket.enableCompression(true);
六、总结
经典蓝牙的RFCOMM协议为设备间的流式数据传输提供了可靠的解决方案。本文全面讲解了鸿蒙系统中经典蓝牙开发的核心要点:
核心要点回顾:
- 设备发现与配对:理解配对流程,处理用户交互
- RFCOMM服务端:创建服务、注册SDP记录、等待连接
- RFCOMM客户端:发现服务、发起连接、数据通信
- 数据传输:处理MTU限制、分块发送、流控管理
- 设备兼容性:处理不同设备的UUID差异
最佳实践建议:
- 封装统一的蓝牙管理类,简化使用
- 实现完善的配对和连接错误处理
- 使用操作队列避免并发问题
- 做好设备兼容性测试
下一步学习:
- NFC标签读写(下一篇文章)
- 蓝牙音频传输
- 多设备同时连接
- 蓝牙安全机制
经典蓝牙虽然"老",但在音频传输、串口通信等场景下依然不可替代。希望本文能帮助你掌握RFCOMM开发的核心技能!
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)