ESP-BLE-UART Vibe Indicator 信号灯控制协议 v1(JSONL)完整指南
2026/9/20 2:49:11 网站建设 项目流程

ESP-BLE-UART Vibe Indicator 信号灯控制协议 v1(JSONL)完整指南

【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution

ble_uart_vibe_indicator是 esp-iot-solution 仓库中面向vibe coding场景的 ESP-IDF 设备端示例:开发板通过 ESP-BLE-UART 暴露一组或多组红 / 黄 / 绿信号灯,上位机用换行分隔的 JSON(JSONL)下发query/control命令,固件解析后驱动 GPIO 实现灭灯、亮灯、慢闪与快闪。本文以该示例的协议规范文档 json_format.md 为主体,完整梳理 Signal Light Control Protocol v1 的报文信封、命令格式、错误码与源码级实现,并给出基于 ESP-BLE-UART Bridge 的主机侧实测命令与预期结果,帮助你快速接入自己的自动化脚本或 AI 编程工作流。

一、协议总览:基于 ESP-BLE-UART 的 JSONL 通道

Signal Light Control Protocol v1 是一个极简的请求—响应协议,运行在 ESP-BLE-UART(NUS / GATT)透传通道之上,线格式为Newline-delimited JSON(JSONL)

  • 每条请求必须以换行符\n结尾(固件在ble_jsonl.c中按\n切分行,见 ble_jsonl.c);
  • 每条请求必须携带非空id字段;
  • 设备收到一行完整请求后,始终通过 TX characteristic 回发一行响应(send_json_line统一在报文末尾补\n后经ble_uart_tx发送,见 ble_jsonl.c);
  • 一次典型会话为:建立 BLE 连接 → 查询indicator_count→ 发送controlpayload可含一条或多条灯控命令)。

固件内部将light_id0 / 1 / 2 分别映射到每个指示灯组(channel)的红 / 黄 / 绿 GPIO,该映射在 indicator.h 与 indicator.c 中实现:gpio_for_light_id()light_ids_gpio_map[channel]中取出对应 GPIO 号。

二、信封(Envelope)结构

所有交互都包裹在统一的信封结构中,方向与格式如下:

方向格式
主机 → 设备{"v":1,"id":"<req-id>","op":"command","data":{...}}
设备 → 主机(成功){"v":1,"id":"<req-id>","ok":true,"data":{...}}
设备 → 主机(失败){"v":1,"id":"<req-id>","ok":false,"error":"<code>","data":{...}}

各字段说明:

字段说明
v协议版本,必须为1
id非空请求 id。为空或缺失 → 返回id_not_specified
op必须为"command"
data命令负载对象

从源码看,信封校验逻辑集中在 ble_jsonl.c 的envelope_valid()v必须是数值且等于BLE_JSONL_PROTOCOL_VERSION(宏定义为 1)、opid必须是非空字符串、data必须是对象,任一条件不满足即判定信封非法。校验失败后,固件会优先复用已解析出的id回发bad_request;若连id都为空或缺失,则回发id_not_specified(见 ble_jsonl.c 的handle_line())。

light_action枚举

含义
0灭灯(Off)
1亮灯(On)
2慢闪(约 1 Hz,半周期由CONFIG_VIBE_INDICATOR_SLOW_BLINK_HALF_MS决定)
3快闪(约 3 Hz,半周期由CONFIG_VIBE_INDICATOR_FAST_BLINK_HALF_MS决定)

该枚举在 indicator.h 定义为indicator_light_action_tINDICATOR_LIGHT_ACTION_OFF = 0ON = 1SLOW_BLINK = 2FAST_BLINK = 3,与协议数值一一对应。

三、查询指示灯数量(Query indicator count)

请求

{"v":1,"id":"req-001","op":"command","data":{"cmd":"query","type":"indicator_count"}}

成功响应data

{"count":3}

count直接取自 Kconfig 编译期配置CONFIG_VIBE_INDICATOR_CHANNEL_COUNT,由 indicator.c 的indicator_get_channel_count()返回,默认值为 3(见 Kconfig.projbuild,取值范围 1–8)。

源码中的分发路径:handle_command()识别cmd == "query"后交给handle_query_indicator_count(),该函数进一步校验type必须为字符串且等于"indicator_count",否则返回unsupported_command+"unknown query type"(见 ble_jsonl.c)。这一“子命令 + 子类型”的二次校验是协议健壮性的关键设计。

四、控制灯具(Control lamps)

请求——payload携带一条或多条灯控命令:

{"v":1,"id":"req-002","op":"command","data":{"cmd":"control","payload":[{"indicator_id":0,"light_id":0,"light_action":0},{"indicator_id":1,"light_id":2,"light_action":2}]}}

字段说明:

字段类型说明
cmdstring"control"
payloadarray非空灯控命令列表
payload[].indicator_idint组 id,取值范围0 .. count - 1
payload[].light_idint0/1/2
payload[].light_actionint0..3

关键行为:所有条目先整体校验,全部通过后才执行任何 GPIO 变更(即“先验证后执行”的原子性语义)。成功时设备回显请求中的整个payload

{"v":1,"id":"req-002","ok":true,"data":{"payload":[{"indicator_id":0,"light_id":0,"light_action":0},{"indicator_id":1,"light_id":2,"light_action":2}]}}

源码实现细节(见 ble_jsonl.c):

  • 先检查payload是否为非空数组,否则返回invalid_parameter+"payload must be a non-empty array"
  • 单个请求的 payload 条目数上限为BLE_JSONL_MAX_PAYLOAD_ITEMS(宏定义为 32),超限返回invalid_parameter+"payload too many items"
  • 第一轮循环对每个条目调用parse_control_item()做字段存在性与取值范围校验,任一失败立即返回invalid_parameter并附带具体message不修改任何灯状态
  • 第二轮循环才真正逐个调用indicator_set_light()应用 GPIO 输出,并用cJSON_Duplicate深拷贝原始 payload 作为data.payload回显。

indicator_set_light()在 indicator.c 中会先通过indicator_validate_light()复核(防越界),然后加互斥锁更新灯状态并立即调用apply_light_locked()刷新对应 GPIO 电平。

五、错误码(Error codes)

协议级错误

error触发时机
bad_json该行不是合法 JSON
bad_request信封字段缺失或非法
id_not_specifiedid为空或缺失
unknown_opop不是command

应用级错误

error触发时机
unsupported_commanddata.cmd未知,或查询type未知
invalid_parameterpayload非法或数值越界

应用级错误响应可能携带data.message说明具体原因:

{"v":1,"id":"req-002","ok":false,"error":"invalid_parameter","data":{"message":"light_id out of range, expected 0-2"}}

协议级错误在源码中的对应:bad_jsoncJSON_ParseWithLength失败触发(见 ble_jsonl.c);unknown_opop != "command"时触发(ble_jsonl.c)。应用级校验消息来自 indicator.c 的indicator_validate_light(),真实字符串包括:

  • "indicator_count is 0"
  • "indicator_id out of range"
  • "light_id out of range, expected 0-2"
  • "unsupported light_action, expected 0-3"
  • "payload item must be an object"
  • "payload item missing indicator_id, light_id, or light_action"

(前两条校验对应 ble_jsonl.c 中parse_control_item()的返回逻辑。)协议文档中的light_id out of range, expected 0-2与源码完全一致,可作为自动化测试的精确断言依据。

六、接收端的 JSONL 解析实现

理解协议行为,还需要了解固件如何把 BLE 收到的字节流切分成行并排队处理(见 ble_jsonl.c):

  • ble_jsonl_rx_feed()ble_uart_on_rx回调入口(在 app_main.c 注册),把原始 RX 数据按BLE_JSONL_RX_CHUNK_MAX(512 字节)分块送入深度为 8 的队列,队列满时丢弃剩余数据并打印告警;
  • 独立 worker 任务jsonl_worker_task(栈 6144 字节、优先级 5)从队列取块,交给rx_feed()逐字节切行:以\n为行终止符,\r被忽略(兼容 CRLF),单行上限BLE_JSONL_RX_LINE_MAX(2048 字节),超长则丢弃该行并标记溢出、直到下一个\n才恢复;
  • 协议 JSON 嵌套约 4 层,示例的 sdkconfig.defaults 将CONFIG_CJSON_NESTING_LIMIT设为 32,以降低 worker 任务上的解析/打印栈占用。

这些约束意味着:主机侧必须保证每条协议报文以\n结尾、单行不超过 2048 字节,且不要向一个请求中塞入超过 32 条灯控命令。

七、配置项与协议的对应关系

协议中的count、闪烁频率直接挂钩 Kconfig 配置(Kconfig.projbuild):

配置项默认值取值范围说明
VIBE_INDICATOR_CHANNEL_COUNT31–8独立 R/Y/G 灯组数量,即query indicator_count返回的count
VIBE_INDICATOR_SLOW_BLINK_HALF_MS500 ms50–5000light_action=2慢闪的 GPIO 翻转间隔,约 1 Hz
VIBE_INDICATOR_FAST_BLINK_HALF_MS167 ms20–2000light_action=3快闪的 GPIO 翻转间隔,约 3 Hz,同时决定共享闪烁定时器的 tick 周期
VIBE_INDICATOR_DEVICE_NAME_PREFIXVibe-Indicator广播名<prefix>-XXXX,XXXX 为蓝牙 MAC 末两字节
VIBE_INDICATOR_CHx_GPIO_{R,Y,G}-1-1–54各组红/黄/绿 GPIO;-1表示未连接(协议仍工作,灯珠不动作)

闪烁的实现细节:blink_timer_cb()快闪半周期为 tick 创建周期定时器(esp_timer,见 indicator.c),快闪灯每个 tick 翻转一次相位,慢闪灯则按slow_blink_divisor() = ceil(慢闪半周期 / 快闪半周期)分频翻转。只有存在闪烁中的灯时才刷新 GPIO,节省中断开销。

八、主机侧实测:通过 ESP-BLE-UART Bridge 联调

固件之外,还需要在 PC 上运行 ESP-BLE-UART Bridge 的daemon:它维持 BLE 连接、订阅通知,把 JSON 命令经 GATT 转发给设备并回传响应。首次使用先安装依赖:

cd $IDF_PATH . ./export.sh python -m pip install -r tools/ble/ble_uart_bridge/requirements.txt

<DEVICE_ID>替换为list-devices或串口 monitor 日志中打印的 BLE 地址(设备广播名形如Vibe-Indicator-XXXX):

cd $IDF_PATH/tools/ble/ble_uart_bridge python main.py connection-check <DEVICE_ID> python main.py daemon <DEVICE_ID> # 查询组数 python main.py daemon-send --op command \ --json '{"cmd":"query","type":"indicator_count"}' --timeout 5 # 控制单路灯 python main.py daemon-send --op command \ --json '{"cmd":"control","payload":[{"indicator_id":0,"light_id":0,"light_action":2}]}' \ --timeout 5 # 批量控制 python main.py daemon-send --op command \ --json '{"cmd":"control","payload":[{"indicator_id":0,"light_id":0,"light_action":1},{"indicator_id":0,"light_id":1,"light_action":0}]}' \ --timeout 5

预期结果

步骤预期
查询indicator_countok: truedata.count与 Kconfig 配置一致
control单条ok: truedata.payload回显请求
control批量所有条目生效,回显payload与请求一致
非法light_idok: falseerror: invalid_parameterdata.message有说明
idok: falseerror: id_not_specified

九、工程结构与进一步阅读

文件说明
app_main.cNVS、indicator 与 JSONL 初始化,BLE UART 安装/打开
ble_jsonl.cJSONL 信封校验、query/control分发
indicator.c单灯 GPIO 驱动、慢闪/快闪定时器
Kconfig.projbuild组数、GPIO 映射、闪烁周期、设备名前缀
json_format.md协议参考(本文依据)
README.md / README_cn.md构建烧录、硬件接线与联调说明

构建烧录可参考 README:当前 IDF 版本中主要 bring-up 目标为 ESP32-H4 与 ESP32-H21(preview 目标,idf.py子命令需保留--preview参数),BLE 以encrypted = false运行便于调试;灯珠要产生可见输出,必须先通过idf.py menuconfig为所用开发板配置各组 GPIO(默认-1表示未连接)。把本协议接入脚本或 vibe coding 工作流时,只需遵循“请求以\n结尾、带非空id、先querycontrol”三条规则,即可把编码或任务进度映射为实时的红 / 黄 / 绿灯语反馈。

【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询