DraARLv1 协议规范¶
协议概述¶
DraARLv1 (Digital Radio Advanced Application Protocol v1) 是 DraARL 平台的设备通信协议,用于设备与服务器之间的实时通信。
报文结构¶
整体布局¶
+--------+--------+--------+--------+--------+--------+--------+--------+
| 0-3 | 4-5 | 6-37 | 38-47 | 48 | 49 | 50 | 51-53 |
| Ver | Length |Username|DevPass | Type |DevModel| SSID | DMRID |
| 4B | 2B | 32B | 10B | 1B | 1B | 1B | 3B |
+--------+--------+--------+--------+--------+--------+--------+--------+
| 54-85 | 86-89 | 90+ |
|CallSign| Rsv | DATA |
| 32B | 4B | 变长 |
+--------+--------+--------+-------------------------------------------------+
固定头部:90 字节
最小报文:90 字节
最大报文:800 字节(UDP 服务端限制,超出将被静默丢弃)
字节序¶
所有多字节字段使用 大端序 (Big-Endian)
字段详解¶
Header 字段¶
| 偏移 | 长度 | 字段名 | 类型 | 说明 |
|---|---|---|---|---|
| 0 | 4B | Version | string | 协议版本标识,固定为 "DraA" (0x44726141) |
| 4 | 2B | Length | uint16 BE | 报文总长度(包含头部和数据) |
| 6 | 32B | Username | string | 用户名,UTF-8 编码,不足部分用 \0 填充 |
| 38 | 10B | DevicePassword | string | 设备准入密码,ASCII 字母数字,不足部分用 \0 填充 |
| 48 | 1B | Type | byte | 数据包类型,见 数据包类型 |
| 49 | 1B | DevModel | byte | 设备型号,见 设备型号 |
| 50 | 1B | SSID | byte | 设备子号 (0-255) |
| 51 | 3B | DMRID | uint24 BE | DMR ID |
| 54 | 32B | CallSign | string | 业余电台呼号,服务器填充,设备发送时留空 |
| 86 | 4B | Reserved | - | 保留字段,填 0 |
| 90 | 变长 | DATA | []byte | 负载数据 |
数据包类型¶
| 值 | 常量名 | 说明 | DATA 内容 |
|---|---|---|---|
| 0 | TypeControl | 控制指令 | 控制命令数据 |
| 1 | TypeJWTAuth | JWT 认证 | JWT Token 字符串(见 JWT 认证包格式) |
| 2 | TypeHeartbeat | 心跳包 | 可选携带 GPS 位置信息 |
| 3 | TypeConfig | 设备配置 | TLV 格式配置数据(见 Config 包协议) |
| 4 | TypeTextMessage | 文本消息 | UTF-8 编码文本 |
| 5 | TypeOpus16K | Opus 16K 语音 | Opus 16kHz 编码语音帧 |
| 6 | TypeServerVoice | 服务器互联语音 | 见 服务器互联语音格式 |
| 7 | TypeATPassThrough | AT 透传 | AT 命令透传 |
设备型号¶
| 值 | 常量名 | 说明 | 认证方式 |
|---|---|---|---|
| 0 | DevModelUnknown | 未知设备 | - |
| 100 | DevModelWeChatMini | 微信小程序 | - |
| 101 | DevModelAndroid | Android 客户端 | JWT Token |
| 102 | DevModelIOS | iOS 客户端 | JWT Token |
| 103 | DevModelWindows | Windows 客户端 | JWT Token |
| 104 | DevModelMacOS | macOS 客户端 (预留) | JWT Token |
| 105 | DevModelBrowser | 浏览器客户端 | JWT Token |
| 106 | DevModelInterconnect | 互联设备 | 设备密码 |
| 107 | DevModelESP32 | ESP32 链路台/手咪 | 设备密码 |
SSID 分配规则¶
| SSID 范围 | 用途 | 认证方式 | 说明 |
|---|---|---|---|
| 1-99 | 普通设备 | 设备密码 | 用户自定义,适用于嵌入式设备 |
| 100 | 预留 | - | 原微信小程序 |
| 101-104 | UDP 幽灵设备 | JWT Token | App/PC 客户端,SSID = DevModel |
| 105 | Web 幽灵设备 | JWT Token | WebSocket 客户端 |
| 106-235 | 普通设备扩展 | 设备密码 | 用户自定义,适用于嵌入式设备 |
| 236-255 | 服务器互联保留 | - | 系统保留,用户不可分配 |
普通设备可用范围: 1-99 和 106-235 (共 229 个),使用设备密码认证 幽灵设备范围: 101-105,使用 JWT Token 认证,SSID 固定等于 DevModel
Config 包协议¶
TypeConfig (Type=3) 仅用于 UDP 普通设备(SSID 1-99, 106-235)的配置同步,不适用于幽灵设备(App/PC 客户端)。
包类型¶
| DATA[0] | 方向 | 说明 |
|---|---|---|
| 0x01 | 服务端 → 设备 | 查询配置请求 |
| 0x02 | 双向 | 配置下发/上报(支持动态下发部分配置项) |
| 0x03 | 服务端 → 设备 | 时间同步 |
TLV 数据格式¶
当 DATA[0] = 0x02 时,DATA 区域格式:
DATA[0] = 0x02 (配置标识)
DATA[1] = N (配置项数量)
DATA[2...] = TLV 列表
TLV 格式:
┌──────────┬──────────┬─────────────────┐
│ Type (1B)│ Len (1B) │ Value (N bytes) │
└──────────┴──────────┴─────────────────┘
配置项 Type 定义¶
| Type | 配置键 | 长度 | 格式 | 说明 |
|---|---|---|---|---|
| 0x01 | rx_freq | 8 | big-endian uint64 | 接收频率 (Hz) |
| 0x02 | tx_freq | 8 | big-endian uint64 | 发射频率 (Hz) |
| 0x03 | rx_ctcss | 4 | big-endian float32 | 接收亚音 (Hz, 0=关闭) |
| 0x04 | tx_ctcss | 4 | big-endian float32 | 发射亚音 (Hz, 0=关闭) |
| 0x05 | sql_level | 1 | uint8 | 静噪等级 (0-9) |
| 0x06 | power_level | 1 | uint8 | 功率等级 (1=低, 2=中, 3=高) |
| 0x07 | tx_bandwidth | 1 | uint8 | 发射带宽 (1=窄带, 2=宽带) |
| 0x10 | timestamp | 8 | big-endian int64 | Unix 时间戳 (毫秒) |
示例¶
下发单个配置项(接收频率 439.500MHz):
DATA = [0x02, 0x01, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x15, 0x7C, 0x00]
│ │ │ │ └─────────────────────────────────────────────┘
│ │ │ │ 439500000 Hz (0x1A157C00)
│ │ │ └─ 长度 8
│ │ └─ Type=rx_freq
│ └─ 配置项数量 1
└─ 配置下发标识
设备上报全部配置:
DATA = [0x02, 0x07, TLV1, TLV2, TLV3, TLV4, TLV5, TLV6, TLV7]
└─ 全部 7 个配置项
同步流程¶
设备上线同步:
设备上线 (认证成功)
│
├─ 数据库有配置记录
│ └─ 服务端发送 Config(0x02 + TLV) 下发配置
│
└─ 数据库无配置记录
└─ 服务端发送 Config(0x01) 查询请求
│
└─ 设备上报 Config(0x02 + TLV)
│
└─ 服务端解析存储到数据库
控制台修改同步:
用户通过控制台修改配置
│
├─ 保存变更到数据库
│
└─ 设备在线?
├─ 是 → 发送 Config(0x02 + 变更项TLV) 动态下发
└─ 否 → 等待设备上线时同步
时间同步:
服务端定期或设备上线时发送 Config(0x03 + 8字节时间戳),设备同步系统时间。
特殊数据包格式¶
JWT 认证包格式 (TypeJWTAuth)¶
当 Type = 1 (TypeJWTAuth) 时,用于幽灵设备(App/PC 客户端)的 JWT Token 认证。
认证请求¶
客户端发送 JWT Token 进行身份认证:
┌──────────────────────────────────────────────────────────────┐
│ Header (90 字节) │
│ Version: "DraA" │
│ Length: 90 + Token长度 │
│ Username: 空 (可选,用于日志) │
│ DevicePassword: 空 │
│ Type: 1 (TypeJWTAuth) │
│ DevModel: 101/102/103/104 │
│ SSID: 0 (服务器会用 DevModel 作为 SSID) │
│ DMRID: 0 │
│ CallSign: 空 (服务器填充) │
│ Reserved: 4字节保留 │
├─────────────────────────────────────────────────────────—────┤
│ DATA 区域 │
│ JWT Token 字符串 (UTF-8 编码,直到包尾) │
└──────────────────────────────────────────────────────────────┘
认证响应¶
服务器返回认证结果:
┌──────────────────────────────────────────────────────────────┐
│ Header (90 字节) │
│ Version: "DraA" │
│ Length: 90 + DATA长度 │
│ Username: 回显用户名 │
│ DevicePassword: 空 │
│ Type: 1 │
│ DevModel: 回显 │
│ SSID: 服务器分配 (等于 DevModel) │
│ DMRID: 0 │
│ CallSign: 成功时填充用户呼号,失败时为空 │
│ Reserved: 4字节保留 │
├──────────────────────────────────────────────────────────────┤
│ DATA 区域 │
│ [0]: 状态码 │
│ 0 = 认证成功 │
│ 1 = Token 无效或过期 │
│ 2 = 用户不存在 │
│ 3 = 用户已禁用 │
│ 4 = 用户未审核 │
│ 5 = 无效的设备型号 (非 101-104) │
│ [1:]: 成功时为空,失败时为错误消息文本 │
└──────────────────────────────────────────────────────────────┘
认证流程¶
┌─────────┐ ┌─────────┐
│ App/PC │ │ 服务器 │
└────┬────┘ └────┬────┘
│ │
│ JWT Auth (Type=1) │
│ DevModel: 101/102/103/104 │
│ DATA: <JWT Token> │
│───────────────────────────────────────>│
│ │
│ │ 1. 验证 JWT Token
│ │ 2. 获取用户信息
│ │ 3. 检查用户状态
│ │ 4. 注册幽灵设备
│ │
│ JWT Auth Response │
│ SSID: 101/102/103/104 │
│ CallSign: "BG1AAA" │
│ DATA[0]: 0 (成功) │
│<───────────────────────────────────────│
│ │
注意:JWT 认证包仅用于幽灵设备(DevModel 101-104)。普通设备(ESP32 等)仍使用设备密码认证,通过心跳包完成。
Opus 语音格式 (TypeOpus16K)¶
当 Type = 5 (TypeOpus16K) 时,DATA 区域携带 Opus 编码的语音数据。
编码参数¶
| 参数 | 值 | 说明 |
|---|---|---|
| 采样率 | 16000 Hz | 16kHz 宽带语音 |
| 声道数 | 1 | 单声道 |
| 帧时长 | 60 ms | 每个 Opus 帧 60ms |
| 每帧采样数 | 960 | 16000 × 0.06 |
| 比特率 | 16-32 kbps | VOIP 模式 |
合并帧格式(WebSocket 客户端)¶
为优化弱网性能,WebSocket 客户端采用 2 帧合并发送 策略: - 每 120ms 发送一个数据包 - 每个数据包包含 2 个 60ms 的 Opus 帧 - 使用帧长度前缀标识每帧边界
DATA 区域格式:
+-------------+-------------+-------------+-------------+
| Frame1 Len | Frame1 Data | Frame2 Len | Frame2 Data |
| 2B | N bytes | 2B | M bytes |
+-------------+-------------+-------------+-------------+
| 字段 | 长度 | 说明 |
|---|---|---|
| Frame1 Len | 2B | 第 1 帧长度(大端序 uint16) |
| Frame1 Data | 变长 | 第 1 帧 Opus 数据(60ms) |
| Frame2 Len | 2B | 第 2 帧长度(大端序 uint16) |
| Frame2 Data | 变长 | 第 2 帧 Opus 数据(60ms) |
性能优化说明¶
| 项目 | 优化前 | 优化后 |
|---|---|---|
| 帧时长 | 20ms | 60ms |
| 发送间隔 | 20ms | 120ms |
| 每秒发包数 | 50 | ~8.3 |
| 单包大小 | 110-170B | 330-570B |
| 头部开销占比 | 50-80% | ~25-35% |
| 额外延迟 | - | +100ms |
注意:120ms 延迟对半双工语音通信完全可接受,且大幅降低弱网丢包影响。
解码兼容性¶
接收端应支持: 1. 合并帧格式:按长度前缀解析多个 Opus 帧 2. 单帧格式:兼容无长度前缀的单帧数据(用于 UDP 设备直发)
判断逻辑: - 若首字节值 < 0x80,视为合并帧格式(长度前缀) - 否则视为单帧格式
服务器互联语音格式 (TypeServerVoice)¶
当 Type = 6 (TypeServerVoice) 时,DATA 区域前 68 字节用于存储原始发送方信息:
+--------+--------+--------+--------+
| 0-31 | 32-63 | 64-67 | 68+ |
|OrigUser|OrigCall|OrigIP | Voice |
| 32B | 32B | 4B | 变长 |
+--------+--------+--------+--------+
| 偏移 | 长度 | 字段名 | 说明 |
|---|---|---|---|
| 0 | 32B | OriginalUsername | 原始发送方用户名 |
| 32 | 32B | OriginalCallSign | 原始发送方呼号 |
| 64 | 4B | OriginalIP | 原始服务器 IP |
| 68 | 变长 | VoiceData | 实际语音数据 |
心跳包扩展格式¶
心跳包的 DATA 区域可携带位置信息:
+--------+--------+--------+--------+
| 0-7 | 8-15 | 16-23 | 24+ |
| Lat | Lon | Alt | Extra |
| 8B | 8B | 8B | 变长 |
+--------+--------+--------+--------+
| 偏移 | 长度 | 字段名 | 说明 |
|---|---|---|---|
| 0 | 8B | Latitude | 纬度 (float64 BE) |
| 8 | 8B | Longitude | 经度 (float64 BE) |
| 16 | 8B | Altitude | 海拔高度 (float64 BE) |
设备认证流程¶
认证方式(三轨制)¶
平台支持三种认证方式,根据设备类型和场景自动选择:
| 设备类型 | SSID 范围 | 认证方式 | 说明 |
|---|---|---|---|
| 普通设备 | 1-99, 106-235 | 设备密码 | ESP32、射频互联盒子等嵌入式设备 |
| 幽灵设备 | 101-105 | JWT Token | App/PC 客户端 (Android/iOS/Windows/macOS/Web) |
| 新设备绑定 | 1-99, 106-235 | 动态码绑定 | 新设备首次上线,通过动态码完成账号绑定 |
设计说明: - 普通设备(如 ESP32 链路台/手咪)性能有限,不适合进行 JWT 解析,继续使用传统的设备密码认证 - 幽灵设备(App/PC 客户端)性能充足,使用 JWT 认证可以复用现有登录接口,无需预共享密钥 - 动态码绑定:新设备首次上线时,通过 HTTP 接口完成与用户账号的绑定,绑定成功后获取认证信息
1. 普通设备上线认证(设备密码)¶
┌─────────┐ ┌─────────┐
│ 设备 │ │ 服务器 │
└────┬────┘ └────┬────┘
│ │
│ Heartbeat (Type=2) │
│ Username: "user1" │
│ DevicePassword: "Abc12345" │
│ CallSign: "" (空) │
│───────────────────────────────────────>│
│ │
│ │ 1. 验证 Username + DevicePassword
│ │ 2. 查询用户信息获取 CallSign
│ │ 3. 检查是否被封禁
│ │
│ Heartbeat Response │
│ CallSign: "BG1AAA" (服务器填充) │
│<───────────────────────────────────────│
│ │
│ 设备缓存 CallSign 用于后续通信 │
│ │
2. 幽灵设备认证(JWT Token)¶
幽灵设备(App/PC 客户端)使用 JWT Token 进行认证,详见 JWT 认证包格式。
3. 新设备动态码绑定流程¶
新设备首次上线时,通过动态码绑定流程完成与用户账号的关联。此流程适用于没有密码输入功能的设备(如无键盘/触摸屏的嵌入式硬件),用户通过 Web 端输入设备显示的动态码完成绑定。
绑定流程时序图¶
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 新设备 │ │ 服务器 │ │ Web用户 │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
│ 1. POST /api/device/pre-check │ │
│ { mac, username, device_password } │ │
│───────────────────────────────────────>│ │
│ │ │
│ { status: "need_bind" } │ │
│<───────────────────────────────────────│ │
│ │ │
│ 2. POST /api/device/request-code │ │
│ { mac } │ │
│───────────────────────────────────────>│ │
│ │ │
│ { dynamic_code: "123456", │ │
│ expires_in: 60 } │ │
│<───────────────────────────────────────│ │
│ │ │
│ [设备显示动态码: 123456] │ │
│ │ │
│ │ 3. POST /api/device/bind │
│ │ { dynamic_code: "123456" } │
│ │<───────────────────────────────────────│
│ │ │
│ │ { device_mac, call_sign } │
│ │───────────────────────────────────────>│
│ │ │
│ │ 4. POST /api/device/submit-config │
│ │ { device_mac, ssid } │
│ │<───────────────────────────────────────│
│ │ │
│ │ { message, udp_auth_info, dmr_id } │
│ │───────────────────────────────────────>│
│ │ │
│ 5. POST /api/device/confirm-bind │ │
│ { mac } │ │
│───────────────────────────────────────>│ │
│ │ │
│ { status: "ready", │ │
│ username, device_password, │ │
│ ssid, dmr_id } │ │
│<───────────────────────────────────────│ │
│ │ │
│ [设备保存配置,准备 UDP 认证] │ │
│ │ │
接口说明¶
| 接口 | 方法 | 认证 | 说明 |
|---|---|---|---|
/api/device/pre-check |
POST | 无 | 设备上电后检查存储的账号密码是否有效 |
/api/device/request-code |
POST | 无 | 请求生成 6 位动态码 |
/api/device/bind |
POST | JWT | 用户通过动态码绑定设备 |
/api/device/submit-config |
POST | JWT | 用户提交设备配置(SSID) |
/api/device/confirm-bind |
POST | 无 | 设备轮询获取绑定状态和配置 |
详细步骤¶
步骤 1:预检查 (pre-check)
设备上电后,使用本地存储的账号密码(如果有)进行预检查:
- 验证成功:返回 status: "authenticated",设备可直接进入 UDP 认证流程
- 验证失败/无存储:返回 status: "need_bind",设备进入动态码绑定流程
步骤 2:请求动态码 (request-code)
设备请求生成动态码: - 服务器生成 6 位数字动态码 - 动态码有效期 60 秒 - 设备在屏幕/LED 上显示动态码
步骤 3:用户绑定 (bind)
用户在 Web 端输入设备显示的动态码: - 验证动态码有效性和设备状态 - 将设备 MAC 与用户账号关联 - 返回设备 MAC 和用户呼号
步骤 4:提交配置 (submit-config)
用户为设备配置 SSID: - SSID 范围:1-99 或 106-235(普通设备可用范围) - 服务器将用户名、设备密码、SSID、DMRID 写入设备配置
步骤 5:确认绑定 (confirm-bind)
设备轮询此接口获取配置:
- 状态 waiting:等待用户完成绑定
- 状态 ready:绑定完成,返回认证信息
安全机制¶
- 动态码单次使用:绑定成功后动态码立即失效
- 有效期限制:动态码 60 秒过期,绑定状态 10 分钟过期
- 限速保护:各接口均有独立的限速策略
- 账号状态校验:用户必须已审核通过才能绑定设备
4. 认证失败处理¶
- 连续认证失败 3 次:封禁 10 秒
- 再次失败:封禁 30 秒
- 继续失败:封禁 60 秒、300 秒(阶梯递增)
封禁 Key:IP + Username
语音转发流程¶
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 设备 A │ │ 服务器 │ │ 设备 B │
│user1 │ │ │ │user2 │
│BG1AAA │ │ │ │BG2BBB │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
│ Opus16K │ │
│ Username:user1 │ │
│ CallSign:BG1AAA│ │
│───────────────>│ │
│ │ │
│ │ 转发(保留头部)│
│ │───────────────>│
│ │ │
│ │ B 显示:
│ │ "BG1AAA 正在发言"
│ │ 或 "user1 (BG1AAA)"
│ │ │
字符编码规则¶
| 字段 | 编码 | 字符集 | 说明 |
|---|---|---|---|
| Username | UTF-8 | 字母、数字、下划线 | 用户名 |
| DevicePassword | ASCII | 大小写字母、数字 | 设备准入密码,6-10 位 |
| CallSign | ASCII | 大写字母、数字 | 业余电台呼号 |
| TextMessage | UTF-8 | 任意 Unicode | 文本消息 |
安全限制¶
UDP 服务端实施以下安全策略,超出限制的数据包将被静默丢弃:
| 限制项 | 阈值 | 说明 |
|---|---|---|
| 最大包体 | 800 字节 | 含 90 字节头部,防止异常大包攻击 |
| 包速率 | 150 包/秒 | 按 IP+Port 维度限速,兼顾 FRP 隧道共享 IP 场景 |
注意:正常语音通信(合并帧 ~8.3 包/秒,单帧 ~16.7 包/秒)远低于限速阈值,不会受到影响。 150 包/秒的阈值设计考虑了: - FRP/NAT 场景下多设备共享同一出口 IP - 客户端网络抖动后的"追赶发送"突发场景 - 60-180ms 大包架构下丢包的严重影响
版本历史¶
| 版本 | 日期 | 说明 |
|---|---|---|
| v1.0 | 2026-03 | 初始版本,替代 NRL2 协议 |
| v1.1 | 2026-03 | 移除 G.711 编解码支持,统一使用 Opus 16K 格式 |
| v1.2 | 2026-03 | 简化协议头,移除 Status 和 SeqNum 字段,头部从 93 字节简化为 90 字节 |
| v1.3 | 2026-03 | 优化弱网性能:Opus 帧时长从 20ms 改为 60ms,WebSocket 客户端采用 2 帧合并发送(120ms 间隔) |
| v1.4 | 2026-03 | 新增 JWT 认证方式(Type=1),支持幽灵设备(App/PC 客户端)接入;新增设备型号 104 (macOS)、107 (ESP32);定义 SSID 分配规则 |
| v1.5 | 2026-03 | 性能优化:PTT 会话阈值从 200ms 提升到 600ms(适配 60-180ms 大包架构);限速器从 25 PPS 放宽到 150 PPS;放弃批量发送缓冲,改用直接发送;补全 WS→UDP Ghost 路由;修复时长统计 |
| v1.6 | 2026-03 | 新增 Config 包协议 (Type=3),支持 UDP 普通设备配置同步(TLV 格式),包含查询、下发、上报、时间同步四种操作 |
| v1.7 | 2026-03 | 新增动态码绑定流程,支持无输入能力的普通设备通过 Web 端完成账号绑定 |