ESP-Cam 统一 API 设计(契约 v1.9)
四块主板暴露同一份 REST 契约:无差异部分完全一致;有差异部分只允许通过"能力门控 + 动态元数据"产生,禁止字段名、数值刻度或语义分叉。GET /api/capabilities 的 api_version 即契约版本。本文是契约 v1.9 的完整说明;修改任何一块板的 API 前,先改契约源文件(各仓 docs/api-contract.md,四仓 md5 一致)。
信封与鉴权
所有 JSON 端点用统一信封:
- 成功:
{"ok":true,"data":...}+ HTTP 200 - 失败:
{"ok":false,"error":"<消息>"}+ 400/404/500/503
契约 v1.9(2026-09-18)起没有设备级密码:Web 管理密码(X-Password 请求头、web_password 配置键、GET /api/auth)与 RTSP digest 凭证(rtsp_user/rtsp_pass)均已移除——全部 HTTP/RTSP 端点在可信局域网内开放,信任边界是路由器的 WPA2。遗留的密码类字段(WiFi 凭据)在 GET 响应中仍掩码为 "****",POST 回传掩码值视为"未修改"。CORS 全开(OPTIONS /* → 204)。
MJPEG 流在独立端口 :81/stream,客户端上限按板为 ai-thinker 1 / n16r8 2 / luatos 2 / seeed 3,通过 status.stream_clients_max 下发。
核心端点(四板 100% 一致)
| Method | Path | 说明 |
|---|---|---|
| GET | /api/status |
设备状态(字段见下节) |
| GET | /api/config |
当前配置(密码掩码) |
| POST | /api/config |
部分更新;WiFi 变更写 NVS、重启生效 |
| GET | /api/capabilities |
能力矩阵 + api_version |
| GET | /api/capture |
单帧 JPEG |
| GET | /api/scan |
WiFi 扫描 {networks:[{ssid,rssi,auth}]},RSSI 降序 |
| POST | /api/time |
手动设时间 {year,month,day,hour,min,sec}(v1.3 补齐 ai-thinker) |
| POST | /api/reset |
恢复出厂并重启 |
| POST | /api/reboot |
重启 |
| GET | /metrics |
Prometheus 文本 |
能力门控
规则:capabilities.X == true ⇒ 对应端点必须存在且语义一致;== false ⇒ 端点不注册(404/405),前端保证永不调用。
能力值语义(v1.3 条款):布尔能力在单次运行的设备上必须恒定(编译期常量或 Kconfig 条件);唯一例外是硬件在场性探测(seeed 的 sd 随插拔变化)。api_version 必须与契约一致,禁止漂移。
| 能力(端点) | 统一语义 | AI-Thinker | N16R8 | Luatos | Seeed |
|---|---|---|---|---|---|
led:POST/GET /api/led |
{"brightness":0-100};简单 GPIO 板 0=灭 |
✅ | ✅ | — | — |
ai:POST /api/ai + GET /api/ai/status |
{face,motion,qr} 开关与结果 |
— | ✅ | — | — |
录像:POST /api/record + GET /api/record |
?action=start|stop 与状态 |
✅ | — | — | ✅ |
sd:/api/files · /api/download · POST /api/files/batch · POST /api/format · GET /api/storage |
文件与存储管理(见下节) | ✅ | — | — | ✅ |
ota:/api/ota/info · upload · spiffs · POST /api/ota {"url":...} |
裸二进制流 OTA + URL 触发(v1.3 四板统一,仅 http://) | ✅ | ✅ | —(单分区无 OTA) | ✅ |
audio:GET /api/audio |
G.711 μ-law 8 kHz 裸流 | — | — | — | ✅ |
websocket:GET /ws |
事件推送(见下节) | — | — | ✅ | ✅ |
ONVIF:/onvif/device_service 等 |
SOAP(config onvif_enable 可关) |
✅ | ✅ | ✅ | ✅ |
ONVIF 事件:/onvif/events_service |
Pull-Point 订阅(v1.5:MotionAlarm ← CSI 运动,NVR 联动录像;事件生成由 config onvif_events 门控,默认关) |
— | ✅ | — | ✅ |
RTSP :554/stream |
无需鉴权(v1.9 移除 digest 凭证) | — | ✅ | — | ✅ |
非布尔扩展键:api_version、wifi_scan。编译期能力位:csi_motion(v1.4,ESPectre CSI 运动检测,仅 seeed/n16r8——ai/luatos 为 CSI-off 生产形态)、onvif_events(v1.5,仅 seeed/n16r8)。
/api/status 核心字段
| 字段 | 说明 |
|---|---|
device_name / firmware_version / uptime |
基本信息(uptime 用 esp_timer,不受 SNTP 影响) |
wifi_state |
小写枚举 ap|connecting|connected|disconnected |
ip / wifi_rssi / wifi_channel / current_ssid |
current_ssid 是实际连接的 SSID(区别于配置值) |
wifi_net |
当前配置槽位 primary|secondary(仅双 WiFi 板) |
camera / resolution |
实测传感器型号与当前分辨率——信设备不信文档 |
free_heap / min_heap / free_psram |
无 PSRAM 的板省略 free_psram 字段 |
stream_clients / stream_clients_max |
MJPEG 观众数与上限 |
chip_temp |
有温度传感器的板返回(°C) |
sd_present / sd_total_bytes / sd_free_bytes / sd_free_percent / recording |
SD 能力板的存储状态 |
"不适用即省略"是通用规则:不支持的字段直接不出现在 JSON 里,前端按字段缺省隐藏控件。板级扩展字段允许追加。
相机控制与分辨率刻度(v1.3 统一)
GET /api/camera 返回 resolution、cam_framesize、cam_quality、supported_resolutions:[{label,value}]、quality_min/quality_max(滑杆边界 10-63)、res_cap_source(上限被 sensor/board/memory 哪层钳制)及该板支持的微调字段(cam_brightness/contrast/saturation/sharpness、cam_hmirror、cam_vflip、day_night_mode)。
value 数值刻度全家族统一为 esp32-camera 组件的 framesize_t 枚举(v1.3 起:QVGA=6, VGA=10, SVGA=11, XGA=12, HD=13, SXGA=14, UXGA=15;四仓组件 md5 一致)。旧板自有刻度(ai 0-3 / seeed 0-5 / luatos 0-3)在固件升级时由各仓 config 迁移函数自动翻译 NVS 存量值,无需人工干预。前端禁止硬编码分辨率表,只从 supported_resolutions 填充下拉框,POST 只回传列表内的 value(越界一律 400)。分辨率上限是三层交集 min(传感器, 板级实测, 运行时 fb 预算)。
配置契约(v1.3 起独立成文)
配置子系统(持久化格式、字段名与取值域、校验矩阵、默认值、迁移策略、SD 卡 provisioning 格式)由 docs/config-contract.md v1.0 统一:全家族逐键 NVS(mibee_cfg 命名空间 + schema_ver 版本键),cam_framesize 用上节统一刻度,motion 为超集模型(enabled/sensitivity 0-100/cooldown_s/active_interval_s),timelapse 为 8 字段动态模型(静态/动态、min/max 衰减)。HTTP /api/config 的字段名以契约 §3 为准,密码类掩码,GET 含 "schema_version"。
WebSocket 事件(/ws,websocket 能力板)
统一格式 {"type":"<event>","timestamp":<unix_s>,"data":{...}}:
motion_started/motion_cleared:移动侦测翻转,data 含score0-100;v1.4 起可选source("csi"区分 CSI 视觉帧差)csi_status(v1.4):CSI 门控板(csi_motion:true)约 1s 心跳{"state":"warming|IDLE|MOTION","score":0-1,"thr":0-1}recording_started/recording_stopped:录像启停wifi_state_changed:{"state":"connected|..."}- 板级扩展:
health_warning、upload_success/failed、wifi_switched_ssid等
ONVIF 事件服务(v1.5,onvif_events 能力板)
/onvif/events_service 实现 WS-BaseNotification Pull-Point 最小子集:CreatePullPointSubscription → 循环 PullMessages(+ Renew/Unsubscribe)。单订阅模型(新订阅顶替旧订阅);TerminationTime 固定 1h,120s 无拉取自动过期;PullMessages 立即返回不阻塞(轮询节奏由 NVR 决定)。事件主题 tns1:VideoSource/MotionAlarm(Source=CSI、State true/false、Score 0-100 家族刻度);订阅服务常注册,事件生成由 config 键 onvif_events 运行时门控。测试:各仓 tools/onvif_events_probe.py(raw SOAP,无三方依赖)。
SD 文件管理(sd 能力板)
GET /api/files?type=all|photos|recordings&offset=&limit=:分页(limit ≤ 200),响应含totalDELETE /api/files?name=&type=photo|recording:type缺省 photoPOST /api/files/batch:{"names":[...]}或{"scope":"all|photos|recordings"};跳过正在写入的录像段(计 failed);拒绝含..的路径POST /api/format:seeed 运行时格式化;ai-thinker 走"申请 → 重启 → 开机格式化"(GPIO14 相机/SD 共享总线,运行中格式化必挂死)GET /api/storage(v1.3 收编):存储详情total_mb/free_mb/usage_pct/mounted
OTA(ota 能力板)
吃裸二进制流,不是 multipart:
curl -X POST http://<ip>/api/ota/upload \
-H 'Content-Type: application/octet-stream' \
--data-binary @build/mibee_cam.bin # 固件 → 备用槽 → 自动重启
curl -X POST http://<ip>/api/ota/spiffs \
--data-binary @build/spiffs.bin # UI → 整擦 SPIFFS → 自动重启
curl -X POST http://<ip>/api/ota \
-H 'Content-Type: application/json' \
-d '{"url":"http://192.168.1.10:8000/mibee_cam.bin"}' # v1.3:URL 触发(仅 http://)
镜像必须 ≤ OTA 槽尺寸;上传中途失败 SPIFFS 即丢(只能串口救)。成功后用 /api/ota/info 看 running_partition 切换验证。Luatos 为单 factory 分区,按设计无 OTA,始终串口烧录。
串口 AT 控制台(契约 docs/at-command.md v1.1)
四板共用同一份 AT 核心实现(main/at_command.c md5 一致 + 板级 main/at_port.c):核心指令 AT / AT+HELP / AT+GMR / AT+WIFI?|= / AT+WIFISCAN / AT+IP? / AT+STATUS / AT+CAMRES?|= / AT+CAMQUAL?|= / AT+REBOOT / AT+RESTORE,可选 AT+CFGGET= / AT+CFGSET= / AT+SAVE(白名单读写,密码类只写不读)。响应框架:数据行 +NAME:内容、成功 OK、失败 ERROR: 原因;输入大小写不敏感;分辨率 value 同 HTTP 统一刻度。通道:ai-thinker/n16r8(CH340 UART0)、luatos(CH343 UART0,开串口即复位——PIT-003)、seeed(USB-JTAG CDC)。
契约治理
- 版本演进:v1.1 统一默认密码与遗留差异收敛;v1.2 统一 SD 批量管理与格式化语义;v1.3 分辨率刻度统一 framesize_t、配置契约独立成文、OTA URL 触发四板统一;v1.4(2026-09-07)WS
csi_status心跳 +motion_*可选source字段 +csi_motion能力位;v1.5(2026-09-08)ONVIF Pull-Point 事件服务/onvif/events_service(MotionAlarm ← CSI,NVR 联动)+onvif_events能力位与 config 门控;v1.9(2026-09-18)彻底移除设备级密码(Web 管理密码 + RTSP digest 凭证)。破坏性变更必须 bumpapi_version并在docs/api-contract.md记录迁移说明。 - 任何板的 API/配置/AT 改动先改对应契约文档,四仓同步(三份契约文档四仓 md5 一致是 CI 前的人工检查项)。