EasyLive 协议
适用于 Easy/EasyLive 系列设备的控制协议
概述
EasyLive Protocol(EL 协议)用于 Easy 系列设备的控制,支持振动、吮吸、伸缩等多功能模式。与 PrivateProtocol 不同,EL 协议使用功能模式码(mode code)来区分不同类型的电机控制。
适用设备
通过设备型号名称(modelName)前缀判断是否使用此协议:
Easy开头的设备EasyLive开头的设备
BLE UUID
| 特征 | UUID |
|---|---|
| Service | fff0(16-bit UUID) |
| Write | 0000fff5-0000-1000-8000-00805f9b34fb |
| Notify | 0000fff4-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 | 变长 |
+----------+------+--------+------------+-----------------+| 字段 | 长度 | 说明 |
|---|---|---|
| Header | 4 字节 | 固定帧头 55 AA F0 01 |
| Seq | 1 字节 | 序列号(1-254),每次递增 |
| Length | 1 字节 | 有效载荷长度 |
| DeviceType | 1 字节 | 固定设备类型 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 的差异
| 特性 | PrivateProtocol | EL Protocol |
|---|---|---|
| Service UUID | 0000ff00-... | fff0 |
| 帧头 | 0xAB | 55 AA F0 01 |
| 电机控制 | 按位置索引 [Motor1, Motor2, Motor3] | 按功能模式码 [振动, 伸缩, 吮吸] |
| 强度范围 | 0-10 | 0-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)