版本:v1.0
适用对象:车牌识别相机、道闸控制器、边缘网关固件和设备代理开发人员
边缘端在首次安装、恢复出厂或更换管理系统地址后,需要支持以下两种接入方式:
两种方式完成配置后,边缘端都必须主动连接 MQTT Broker,并发布 retained 的 gate.state.v1。管理系统只有收到该状态消息后,才把设备标记为在线。
发现和配置通道只用于接入,不承载车牌、图片、开闸指令等正常业务数据。设备上线、状态、识别事件、开闸指令和回执使用 MQTT;抓拍图片由边缘端通过 HTTPS 主动上传到管理系统,MQTT 事件只引用上传成功返回的 image_id。
设备出厂时必须生成并永久保存以下信息:
| 字段 | 要求 |
|---|---|
device_id |
永久硬件 ID,不能由管理员输入、修改或因 IP 变化而改变;建议使用安全芯片序列号或出厂序列号 |
device_code |
初始可以为空或使用出厂临时编码;管理员绑定业务通道后会下发正式编码 |
device_type |
例如 lpr-gate、gate-controller、camera |
device_model |
固件型号 |
firmware_version |
当前固件版本 |
| 设备签名密钥 | 每台设备独立的 Ed25519 私钥,私钥不得通过 UDP、日志或 MQTT 暴露 |
device_id 是设备身份主键。设备发现响应里的 device_id、HTTPS 身份接口返回的 device_id 和 MQTT 状态对应的设备编码必须能够关联;发现阶段和 HTTPS 阶段出现不同永久 ID 时必须拒绝继续配置。
设备必须在配置端口监听 HTTPS,默认端口为 8443,实际端口可由固件配置。证书可以是出厂自签名证书,但证书必须稳定保存,不能每次启动随机生成。
必须提供以下接口:
| 方法 | 路径 | 用途 |
|---|---|---|
GET |
/api/v1/identity |
返回设备公开身份、证书/公钥指纹和是否需要配对码 |
POST |
/api/v1/pair |
校验一次性配对码,建立本次管理系统信任关系 |
POST |
/api/v1/provision |
原子保存 MQTT 和停车场通道配置 |
当前管理系统会先读取 /api/v1/identity,再由管理员核对证书指纹并调用 /api/v1/pair,最后调用 /api/v1/provision。接口必须只返回必要信息,不返回私钥、MQTT 长期密码或其他设备密钥。
设备必须支持 MQTT 3.1.1、QoS 1、Keep Alive 30 秒、LWT 和 retained 消息。设备是 MQTT 客户端,管理系统是 Broker;设备不得等待管理系统反向连接设备。
0.0.0.0:31001。discovery.request.v1。管理系统会在本机 IPv4 网卡的定向广播地址上发送 3 次请求,间隔 500ms,收集窗口约 3 秒。设备不能假设管理系统使用固定源端口。
discovery.request.v1{
"schema": "discovery.request.v1",
"request_id": "7ba3a5ea-3ca4-45fe-b59d-1dc47b6ab5a1",
"issued_at": 1760000000,
"expires_at": 1760000030,
"system_id": "smart-parking-HOST01",
"nonce": "base64-32-random-bytes"
}
设备处理要求:
schema 必须等于 discovery.request.v1。request_id 必须作为本次请求关联 ID 原样带回。expires_at 必须大于当前时间;建议允许不超过 30 秒的时钟误差。nonce 必须原样带回,不能生成新的 nonce 替换它。request_id + nonce 的重复请求可以重复响应,但不能触发配置或重启 MQTT。discovery.response.v1{
"schema": "discovery.response.v1",
"request_id": "7ba5a3ea-3ca4-45fe-b59d-1dc47b6ab5a1",
"device_id": "SN-20260819-0001",
"device_code": "EDGE-A01",
"device_type": "lpr-gate",
"device_model": "LPR-GW-200",
"firmware_version": "2.1.0",
"ip": "192.168.10.26",
"config_url": "https://192.168.10.26:8443/api/v1/provision",
"public_key_fingerprint": "SHA256:4A8F...",
"signing_public_key": "base64-ed25519-public-key",
"nonce": "base64-32-random-bytes",
"signature": "base64-ed25519-signature"
}
字段要求:
| 字段 | 要求 |
|---|---|
ip |
必须是设备当前 IPv4 地址,并且等于设备发送 UDP 响应时的本地地址;不要填管理系统地址或旧地址 |
config_url |
必须使用 https,主机必须等于 ip;端口必须是设备实际 HTTPS 配置端口 |
public_key_fingerprint |
signing_public_key 原始 32 字节 Ed25519 公钥的 SHA-256;支持 SHA256: 前缀,推荐十六进制大写或小写均可 |
signing_public_key |
Base64 编码的 Ed25519 公钥;每台设备固定,不因每次响应变化 |
signature |
Base64 编码的 Ed25519 签名 |
当前管理系统验证的签名原文是以下 6 个字段按顺序以单个换行符连接,不能添加空格、JSON 格式或末尾换行:
schema
request_id
device_id
ip
config_url
nonce
例如:
discovery.response.v1
7ba5a3ea-3ca4-45fe-b59d-1dc47b6ab5a1
SN-20260819-0001
192.168.10.26
https://192.168.10.26:8443/api/v1/provision
base64-32-random-bytes
管理系统会校验 schema、request_id、nonce、UDP 源 IP、config_url 主机、签名和公钥指纹。设备不得把发现响应当作已信任或已配置;自动发现后仍必须通过 HTTPS 身份读取和管理员确认。
管理系统请求:
GET /api/v1/identity HTTP/1.1
Host: <device-ip>:<https-port>
Accept: application/json
成功响应 HTTP 200:
{
"device_id": "SN-20260819-0001",
"device_code": "EDGE-A01",
"device_type": "lpr-gate",
"device_model": "LPR-GW-200",
"firmware_version": "2.1.0",
"certificate_fingerprint": "SHA256:ABCDEF...",
"public_key_fingerprint": "SHA256:4A8F...",
"pairing_required": true
}
字段处理:
device_id 必填且永久不变;为空时管理系统会拒绝设备。certificate_fingerprint 应为当前 HTTPS 服务器证书 DER 原文的 SHA-256。设备返回值必须与实际 TLS 证书一致。public_key_fingerprint 是设备签名公钥指纹,可与 UDP 响应中的值一致。pairing_required=true 时,管理系统会要求管理员输入一次性配对码。设备必须:
管理系统首次读取身份时会获取 TLS 对端证书指纹;管理员必须把页面指纹与设备标签或二维码核对。管理系统不会因为 IP 可达就自动信任设备。
当身份响应 pairing_required=true 时,管理系统请求:
POST /api/v1/pair HTTP/1.1
Content-Type: application/json
{"pairing_code":"ONE-TIME-CODE"}
成功返回任意 2xx,建议返回:
{
"result": "paired",
"device_id": "SN-20260819-0001",
"expires_at": 1760003600
}
设备实现要求:
pairing_required=false 时,设备仍必须完成 HTTPS 证书指纹核对;不能仅凭此字段跳过管理员确认。管理系统请求:
POST /api/v1/provision HTTP/1.1
Content-Type: application/json
当前管理系统实际发送的 JSON 字段如下:
{
"schema": "provision.request.v1",
"request_id": "f0f2f011-cc14-4965-8a61-9a65b3ec2f0b",
"device_id": "SN-20260819-0001",
"device_code": "GATE-A01",
"mqtt": {
"host": "192.168.10.10",
"port": 1883,
"tls": false,
"client_id": "GATE-A01"
},
"image_upload": {
"url": "http://192.168.10.10:8888/device-images/upload",
"max_bytes": 8388608
},
"route": {
"parking_lot_id": 2,
"booth_id": 5,
"channel_id": 8,
"direction": "in"
}
}
字段要求:
| 字段 | 设备端处理 |
|---|---|
schema |
必须为 provision.request.v1 |
request_id |
保存最近处理结果;相同请求重试应幂等返回,不重复生成凭据或反复重启 |
device_id |
必须与本机永久 ID 完全一致,不一致返回 409 或 403 |
device_code |
成功配置后作为 MQTT ClientID、消息 device_code 和业务设备编码;不能为空 |
mqtt.host |
管理系统下发给设备的可达地址;不能把它替换为设备自身 IP |
mqtt.port |
1-65535;按 mqtt.tls 选择 TLS 或普通 MQTT |
mqtt.tls |
为 true 时必须校验 Broker 证书;不能降级为明文连接 |
mqtt.client_id |
当前应与 device_code 相同,必须全局唯一 |
image_upload.url |
边缘端主动上传抓拍图片的管理系统内网地址 |
image_upload.max_bytes |
单张图片最大字节数;当前为 8MB |
route.* |
保存停车场、岗亭、通道数字 ID 和方向;Topic 使用这些数字 ID,不是编码字符串 |
当前版本管理系统没有在 JSON 中发送 MQTT 用户名、密码、topic_prefix、config_version、nonce 或请求签名字段。设备端必须忽略未知字段,但不能自行要求当前版本不存在的必填字段,否则会导致接入失败。生产环境启用设备级账号、双向 TLS 或配置签名时,应通过协议版本升级增加这些字段,并保持旧版本的明确兼容策略。
设备收到合法配置后必须按以下顺序处理:
device_id、device_code、端口、Topic 坐标和方向。mqtt.host 是设备实际可达的单播地址;禁止 127.0.0.1、localhost、0.0.0.0、广播和组播地址。{
"result": "accepted",
"request_id": "f0f2f011-cc14-4965-8a61-9a65b3ec2f0b",
"message": "configuration saved; mqtt reconnecting"
}
accepted 只表示设备已经保存配置,不表示已经在线。返回响应后设备应异步重启 MQTT 客户端,不要让 HTTPS 请求等待 Broker 连接完成。
建议错误响应:
| HTTP | 场景 | 设备行为 |
|---|---|---|
| 400 | JSON、字段、端口或 Topic 坐标无效 | 不改配置,返回字段错误 |
| 401/403 | 未配对或权限不足 | 不改配置,记录安全事件 |
| 409 | device_id、request_id 或配置版本冲突 |
返回现有结果或冲突原因,不覆盖配置 |
| 429 | 配置/配对请求过于频繁 | 延迟重试,不修改配置 |
| 500 | 本地存储失败 | 保留旧配置,返回失败 |
设备需要额外实现 UDP 监听和签名响应。完整过程:
启动设备
└─ 监听 UDP :31001、启动 HTTPS 身份服务
管理系统广播 discovery.request.v1
└─ 设备校验 request_id/nonce/过期时间
└─ 设备单播 discovery.response.v1
管理系统通过 HTTPS GET /identity
└─ 管理员核对证书指纹和配对码
管理系统 HTTPS POST /provision
└─ 设备原子保存 MQTT 配置并返回 accepted
设备连接 MQTT
└─ 清除旧 LWT retained
└─ 发布 retained gate.state.v1
UDP 响应不等于配置授权。设备不能收到发现请求后自动修改 device_code、业务通道或 MQTT 配置,所有修改必须来自已配对的 HTTPS 配置请求。
设备不需要收到 UDP 请求,也不需要知道管理系统 IP;只要管理员能够从管理系统访问设备的 HTTPS IP 和端口即可:
设备启动 HTTPS :8443
管理员在系统输入设备 IP/端口
└─ 管理系统 GET /api/v1/identity
└─ 管理员核对证书指纹,输入配对码
└─ 管理系统 POST /api/v1/pair(需要时)
└─ 管理系统 POST /api/v1/provision
设备连接 MQTT
└─ 清除旧 LWT retained
└─ 发布 retained gate.state.v1
手工 IP 常用于跨 VLAN、UDP 广播被禁、无线隔离、VPN 或设备不支持发现协议的网络。设备端无需为这些场景降低 TLS、永久 ID 或配对校验要求。
设备从 provision 中读取:
Broker: mqtt[ s ]://<mqtt.host>:<mqtt.port>
ClientID: <mqtt.client_id>,当前必须等于 device_code
Keep Alive: 30 秒
QoS: 1
Topic 模板:
parking/lot/{parking_lot_id}/booth/{booth_id}/channel/{channel_id}/gate/state
parking/lot/{parking_lot_id}/booth/{booth_id}/channel/{channel_id}/gate/lwt
parking/lot/{parking_lot_id}/booth/{booth_id}/channel/{channel_id}/gate/cmd
parking/lot/{parking_lot_id}/booth/{booth_id}/channel/{channel_id}/gate/cmd/ack
parking/lot/{parking_lot_id}/booth/{booth_id}/channel/{channel_id}/camera/event
设备只订阅自己的 gate/cmd。不要订阅 # 或其他停车场的 Topic。
连接 MQTT 后必须:
gate/lwt,Payload 为: {"schema":"gate.lwt.v1","device_code":"GATE-A01"}
LWT 使用 QoS 1、retained=true。
gate/cmd。gate/lwt 发布空 retained payload,清除旧的离线遗嘱。gate.state.v1: {
"schema":"gate.state.v1",
"device_code":"GATE-A01",
"state":"closed",
"ts":1760000000
}
gate.state.v1;断开连接时由 Broker 发布 LWT。管理系统等待最多约 30 秒接收 gate.state.v1。只有收到状态消息,设备才会从“配置已保存”变成“在线”。
收到 gate.cmd.v1 后,设备必须校验 device_code、action 和 valid_time,执行动作后在 5 秒内向 gate/cmd/ack 发布:
{
"schema":"gate.ack.v1",
"cmd_id":"3f2a1b9c-4d5e-4f6a-8b9c-0d1e2f3a4b5c",
"device_code":"GATE-A01",
"result":"success",
"error":""
}
失败时 result 为 failed,error 使用可读但不包含密钥的诊断信息。相同 cmd_id 的重复命令应幂等处理,不能因为 MQTT QoS 1 重投而连续开闸。
每次识别后,边缘端必须先通过 image_upload.url 上传图片,成功获得 image_id 后再发布 MQTT 识别事件。不得在 MQTT 中发送 Base64 图片或图片二进制数据。
POST /device-images/upload HTTP/1.1
Content-Type: multipart/form-data
表单字段:device_code(当前 MQTT 设备编码)、image_id(每张图片全局唯一 UUID)、event_id(同一次识别 UUID)、direction(in / out)、image(JPEG/PNG/WebP 图片,最大 8MB)。成功响应的 data.image_id 与请求值相同。网络超时后可使用相同的 device_code + image_id 重试,管理系统会返回已有图片记录,不重复保存。
联调时管理端会记录以下结构化日志(不记录图片内容):
边缘设备图片上传结果:包含 result、remote_addr、device_code、image_id、event_id、content_type、size、idempotent 和 duration。边缘设备图片上传失败:包含失败阶段、来源地址、设备编码、图片 ID、耗时和错误原因。常见阶段包括设备未配置、缺少文件、大小超限、格式校验、对象存储和数据库保存。收到车辆识别 MQTT 事件、车辆识别事件字段已解析:确认 MQTT 事件已经到达并展示 event_id、image_id、车牌和方向。车辆识别事件图片引用校验成功:确认 MQTT 引用已经匹配到该设备上传的图片;若失败会记录 车辆识别事件引用的图片不可用。处理车辆识别事件:包含通行处理结果和总耗时。上传成功后发布:
{
"schema": "camera.event.v1",
"event_id": "3e0d2bb5-74bd-4a2c-a5d8-6c3ac6472a1b",
"image_id": "447a82ca-7e02-4dd4-9e78-d865f84e3c1d",
"device_code": "GATE-A01",
"plate_number": "粤A12345",
"direction": "in",
"ts": 1760000000
}
管理系统只接受已由同一 device_code 上传的 image_id;图片尚未上传、上传失败或属于其他设备时,该识别事件不进入通行流程。为兼容存量固件,当前管理系统暂时仍接受旧 image_path 字段,但新固件必须迁移到 image_id。
建议实现以下状态:
| 状态 | 进入条件 | 退出条件 |
|---|---|---|
unprovisioned |
出厂或恢复出厂 | 收到合法并已配对的配置 |
identity_ready |
HTTPS 身份服务可用 | 配置成功或重新配对 |
provisioning |
正在校验/保存配置 | 原子保存成功或失败 |
provisioned |
已保存 MQTT 配置 | 发布在线状态或配置失败 |
online |
收到 MQTT CONNACK 并发布 gate.state.v1 |
MQTT 断开 |
offline |
LWT、连接失败或心跳超时 | MQTT 重连并重新发布状态 |
failed |
配置或本地存储失败 | 管理员修正后重新配置 |
重试要求:
request_id 重试必须返回同一处理结果,不能重复生成凭据或重复清理数据。device_id 和签名密钥。边缘端必须满足:
device_id 永久且不可由页面输入覆盖。mqtt.tls=true 时必须验证 Broker 证书,禁止自动降级为明文。0.0.0.0:31001 监听并能收到定向广播。ip、config_url、签名和公钥指纹满足格式要求。8443,证书指纹稳定。device_id 不一致的 /provision 不修改配置。/provision 成功时配置原子保存并返回 result=accepted。gate.state.v1。gate.cmd.v1 在 5 秒内返回匹配 cmd_id 的 gate.ack.v1。request_id 和 cmd_id 重试不会造成重复配置或重复开闸。当前管理系统实现以本文第 5.1 节的 provision.request.v1 字段为准,MQTT 默认可能使用匿名连接,具体地址由管理系统 mqtt.advertised-host 和 mqtt.advertised-port 下发。边缘端应忽略未知 JSON 字段并保留向后兼容能力。
后续启用设备级 MQTT 账号、双向 TLS、配置请求签名、expires_at、nonce 或 config_version 时,管理系统会增加协议版本或字段约束。设备端应将协议版本、已处理的 request_id 和配置版本持久化,以便平滑升级和幂等重试。