蓝牙设备对接文档

EasyLive 协议

适用于 Easy/EasyLive 系列设备的控制协议

概述

EasyLive Protocol(EL 协议)用于 Easy 系列设备的控制,支持振动、吮吸、伸缩等多功能模式。与 PrivateProtocol 不同,EL 协议使用功能模式码(mode code)来区分不同类型的电机控制。

适用设备

通过设备型号名称(modelName)前缀判断是否使用此协议:

  • Easy 开头的设备
  • EasyLive 开头的设备

BLE UUID

特征UUID
Servicefff0(16-bit UUID)
Write0000fff5-0000-1000-8000-00805f9b34fb
Notify0000fff4-0000-1000-8000-00805f9b34fb
// ELProtocol.ts
public static readonly SERVICE_UUID = 'fff0'
public static readonly WRITE_UUID   = '0000fff5-0000-1000-8000-00805f9b34fb'
public static readonly NOTIFY_UUID  = '0000fff4-0000-1000-8000-00805f9b34fb'

帧格式

+----------+------+--------+------------+-----------------+
| Header   | Seq  | Length | DeviceType | Command+Content |
| 55AAF001 | 1B   | 1B     | 66         | 变长            |
+----------+------+--------+------------+-----------------+
字段长度说明
Header4 字节固定帧头 55 AA F0 01
Seq1 字节序列号(1-254),每次递增
Length1 字节有效载荷长度
DeviceType1 字节固定设备类型 0x66
Command+Content变长命令类型和内容

序列号管理

每次发送命令时序列号递增,到达 255 后重置为 1:

let sequence = 0

function nextSequence(): number {
  sequence++
  if (sequence >= 255) {
    sequence = 1
  }
  return sequence
}

功能模式码

EL 协议通过功能模式码(i1 参数)区分不同类型的电机控制。控制函数接收三个参数:

  • i1 -- 功能模式码
  • i2 -- 强度值
  • i3 -- 辅助参数

特殊模式码

i1 值功能帧内容格式说明
249振动hex(i2 + i3)振动模式
248伸缩02 + hex(i2 + i3)伸缩/抽插模式
247吮吸0101 + hex(i2 + i3)吮吸模式
241组合模式复合格式(见下文)多功能联动
其他默认hex(i2 + i3)通用模式

组合模式 (i1=241) 详细格式

i1=241 时,帧内容格式为:

02 01 0[i2_hex] 00 02 01 0[i2_hex] 02 [i2_hex]

这种格式同时控制多个功能单元,i2 值同时应用于所有功能。

控制帧构造

function makeMotorControl(i1: number, i2: number, i3: number): Uint8Array {
  const seq = nextSequence()
  let commandContent: string

  switch (i1) {
    case 249: // 振动
      commandContent = toHex(i2 + i3)
      break
    case 248: // 伸缩
      commandContent = '02' + toHex(i2 + i3)
      break
    case 247: // 吮吸
      commandContent = '0101' + toHex(i2 + i3)
      break
    case 241: // 组合模式
      commandContent = `02010${toHex(i2)}0002010${toHex(i2)}02${toHex(i2)}`
      break
    default:
      commandContent = toHex(i2 + i3)
  }

  const commandType = toHex(i1)  // 功能模式码作为命令类型
  const payload = `66${commandType}${commandContent}`
  const length = toHex(payload.length / 2)
  const header = '55AAF001'

  return hexToBytes(`${header}${toHex(seq)}${length}${payload}`)
}

帧构造示例

振动模式,强度 241+5=246 (0xF6):

Header:      55 AA F0 01
Seq:         01 (第1帧)
Length:      03
DeviceType:  66
CommandType: F9 (249 = 振动)
Content:     F6 (246 = 241 + 5)

完整帧: 55 AA F0 01 01 03 66 F9 F6

伸缩模式,强度 241+3=244 (0xF4):

Header:      55 AA F0 01
Seq:         02 (第2帧)
Length:      04
DeviceType:  66
CommandType: F8 (248 = 伸缩)
Content:     02 F4

完整帧: 55 AA F0 01 02 04 66 F8 02 F4

广播指令映射

Easy 设备通过广播指令码映射到 EL 协议的 [i1, i2, i3] 参数。

振动模式

广播指令映射参数 [i1, i2, i3]说明
模式 1[249, 241, 0]振动,强度 241
模式 2[249, 241, 1]振动,强度 242
模式 3[249, 241, 2]振动,强度 243
模式 4[249, 241, 3]振动,强度 244
模式 5[249, 241, 4]振动,强度 245

伸缩模式

广播指令映射参数 [i1, i2, i3]说明
模式 6[243, 241, 0]伸缩,强度 241
模式 7[243, 241, 1]伸缩,强度 242
模式 8[243, 241, 2]伸缩,强度 243
模式 9[243, 241, 3]伸缩,强度 244

组合模式

广播指令映射参数 [i1, i2, i3]说明
短指令 1[241, 10, 0]组合,强度 10
短指令 2[241, 20, 0]组合,强度 20
短指令 3[241, 30, 0]组合,强度 30
.........
短指令 9[241, 90, 0]组合,强度 90

停止命令

停止映射: [241, 0, 0] => 组合模式强度 0

与 PrivateProtocol 的差异

特性PrivateProtocolEL Protocol
Service UUID0000ff00-...fff0
帧头0xAB55 AA F0 01
电机控制按位置索引 [Motor1, Motor2, Motor3]按功能模式码 [振动, 伸缩, 吮吸]
强度范围0-100-255(协议级别)
序列号1-254 递增
sort 映射sort - 1 为数组位置功能模式码由设备定义
CRC/校验CRC8 + 转义编码无额外校验

Hex 规范化

所有 Hex 值确保偶数长度,不足补前导零:

function normalizeHex(value: number, targetLength?: number): string {
  let hex = value.toString(16).toUpperCase()
  if (hex.length % 2 !== 0) {
    hex = '0' + hex
  }
  if (targetLength && hex.length < targetLength) {
    hex = '0'.repeat(targetLength - hex.length) + hex
  }
  return hex
}

// 示例:
// normalizeHex(15)     => "0F"
// normalizeHex(255)    => "FF"
// normalizeHex(10, 4)  => "000A"

完整使用示例

import { ELProtocol } from '@/utils/bluetooth/protocol/ELProtocol'

// 1. 振动控制 -- 强度 246 (241 + 5)
const vibrateFrame = ELProtocol.makeMotorControl(249, 241, 5)
await unifiedBluetoothManager.write(vibrateFrame, connectedDevice)

// 2. 伸缩控制 -- 强度 244 (241 + 3)
const thrustFrame = ELProtocol.makeMotorControl(248, 241, 3)
await unifiedBluetoothManager.write(thrustFrame, connectedDevice)

// 3. 吮吸控制 -- 强度 243 (241 + 2)
const suctionFrame = ELProtocol.makeMotorControl(247, 241, 2)
await unifiedBluetoothManager.write(suctionFrame, connectedDevice)

// 4. 组合模式 -- 强度 50
const comboFrame = ELProtocol.makeMotorControl(241, 50, 0)
await unifiedBluetoothManager.write(comboFrame, connectedDevice)

// 5. 停止所有
const stopFrame = ELProtocol.makeMotorControl(241, 0, 0)
await unifiedBluetoothManager.write(stopFrame, connectedDevice)