IoT 协议(MQTT)¶
Inklet 设备通过 AWS IoT Core 使用 MQTT 协议与后端通信。所有连接均使用 X.509 证书双向 TLS(mTLS)进行认证。本页面介绍主题结构、消息格式和安全策略。
连接信息¶
| 参数 | 值 |
|---|---|
| 协议 | MQTT over TLS(端口 8883) |
| 认证方式 | X.509 客户端证书(mTLS) |
| 代理 | AWS IoT Core(xxxx-ats.iot.us-east-1.amazonaws.com) |
| QoS | 1(至少一次送达) |
主题结构¶
所有主题遵循 inklet/dev/{thingName}/direction/type 的模式,其中 {thingName} 是在配网过程中分配的 AWS IoT Core Thing 名称。
inklet/dev/{thingName}/
├── up/ # 设备 → 后端
│ ├── heartbeat # 定时健康上报
│ ├── state # 任意设备状态
│ └── request_claim # 请求配对码
└── down/ # 后端 → 设备
└── cmd # 后端下发的命令
上行主题(设备到后端)¶
inklet/dev/{thingName}/up/heartbeat¶
设备按可配置的间隔(默认 30 秒)发送的定时健康上报。
载荷:
{
"hwId": "a1b2c3d4-5678-9012-abcd-ef0123456789",
"ts": 1705395000,
"firmware": "1.2.0",
"battery": 85
}
| 字段 | 类型 | 描述 |
|---|---|---|
hwId |
string | 硬件 UUID |
ts |
integer | Unix 时间戳(秒) |
firmware |
string | 固件版本字符串 |
battery |
integer | 电池百分比(0--100) |
后端行为:
- 如果数据库中不存在该设备记录,则根据
hwId和thingName创建 - 更新
firmware、battery、lastSeenAt和online状态 - 如果设备未绑定且没有配对码,后端会生成一个并发送
claim_code命令
inklet/dev/{thingName}/up/state¶
设备以 JSON 对象形式上报任意状态。后端将其作为 JSON 字符串存储在 state 列中。
载荷:
{
"screen": "text",
"lastCmd": "01912345-9999-7abc-def0-aaaaaaaaaaaa",
"brightness": 50,
"temperature": 22.5
}
载荷可以包含任意有效的 JSON。后端不解析其内容,直接存储。
后端行为:
- 将完整的 JSON 载荷存储为设备的
state - 更新
stateUpdatedAt
inklet/dev/{thingName}/up/request_claim¶
设备请求配对码以进行用户配对。在设备未绑定且需要显示配对码时发送。
载荷:
后端行为:
- 如果设备已绑定到用户,发送
already_bound命令 - 如果设备未绑定,生成一个 6 位字母数字配对码
- 将配对码存储到数据库
- 向设备发送
claim_code命令
下行主题(后端到设备)¶
inklet/dev/{thingName}/down/cmd¶
后端向设备发送的命令。所有命令共享一个通用结构,通过 kind 字段区分命令类型。
命令:text¶
向设备发送要在电子墨水屏上渲染的文本内容。
| 字段 | 类型 | 描述 |
|---|---|---|
kind |
string | "text" |
id |
UUID | 用于跟踪的唯一命令 ID |
text |
string | 要渲染的文本内容 |
命令:claim_code¶
向设备发送配对码供其显示。用户输入此配对码即可将设备绑定到自己的账户。
| 字段 | 类型 | 描述 |
|---|---|---|
kind |
string | "claim_code" |
code |
string | 6 位字母数字配对码 |
命令:bound¶
通知设备已被绑定到某个用户。
| 字段 | 类型 | 描述 |
|---|---|---|
kind |
string | "bound" |
userId |
UUID | 绑定该设备的用户 ID |
命令:unbound¶
通知设备已与所有者解绑。设备应清除屏幕内容并重新请求配对码。
| 字段 | 类型 | 描述 |
|---|---|---|
kind |
string | "unbound" |
命令:already_bound¶
当设备请求配对码但已绑定到用户时发送。设备不应显示配对界面。
| 字段 | 类型 | 描述 |
|---|---|---|
kind |
string | "already_bound" |
AWS IoT Core 策略¶
三个 IAM 策略管控 MQTT 访问权限,每个策略都遵循最小权限原则。
inklet-device-policy¶
附加到每个设备证书的逐设备隔离策略。使用 IoT Core 策略变量 ${iot:Connection.Thing.ThingName},确保设备只能访问自己的主题。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iot:Connect",
"Resource": "arn:aws:iot:us-east-1:*:client/${iot:Connection.Thing.ThingName}"
},
{
"Effect": "Allow",
"Action": "iot:Publish",
"Resource": [
"arn:aws:iot:us-east-1:*:topic/inklet/dev/${iot:Connection.Thing.ThingName}/up/*"
]
},
{
"Effect": "Allow",
"Action": "iot:Subscribe",
"Resource": [
"arn:aws:iot:us-east-1:*:topicfilter/inklet/dev/${iot:Connection.Thing.ThingName}/down/*"
]
},
{
"Effect": "Allow",
"Action": "iot:Receive",
"Resource": [
"arn:aws:iot:us-east-1:*:topic/inklet/dev/${iot:Connection.Thing.ThingName}/down/*"
]
}
]
}
逐设备隔离
${iot:Connection.Thing.ThingName} 变量由 AWS IoT Core 在连接时解析。关联到 Thing inklet-a1b2c3d4 的设备证书只能发布到 inklet/dev/inklet-a1b2c3d4/up/* 并订阅 inklet/dev/inklet-a1b2c3d4/down/*,无法访问其他设备的主题。
inklet-backend-policy¶
后端 MQTT 客户端(inklet-backend)的特权策略。后端订阅所有设备的上行主题,并可向任意设备的下行主题发布命令。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iot:Connect",
"Resource": "arn:aws:iot:us-east-1:*:client/inklet-backend"
},
{
"Effect": "Allow",
"Action": "iot:Subscribe",
"Resource": [
"arn:aws:iot:us-east-1:*:topicfilter/inklet/dev/+/up/*"
]
},
{
"Effect": "Allow",
"Action": "iot:Receive",
"Resource": [
"arn:aws:iot:us-east-1:*:topic/inklet/dev/+/up/*"
]
},
{
"Effect": "Allow",
"Action": "iot:Publish",
"Resource": [
"arn:aws:iot:us-east-1:*:topic/inklet/dev/+/down/*"
]
}
]
}
inklet-claim-policy¶
用于 Fleet Provisioning 的 claim 证书的受限策略。claim 证书只能执行 Fleet Provisioning 的 MQTT 事务 --- 不能发布或订阅应用主题。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "iot:Connect",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "iot:Publish",
"Resource": [
"arn:aws:iot:us-east-1:*:topic/$aws/certificates/create/*",
"arn:aws:iot:us-east-1:*:topic/$aws/provisioning-templates/*/provision/*"
]
},
{
"Effect": "Allow",
"Action": "iot:Subscribe",
"Resource": [
"arn:aws:iot:us-east-1:*:topicfilter/$aws/certificates/create/*",
"arn:aws:iot:us-east-1:*:topicfilter/$aws/provisioning-templates/*/provision/*"
]
},
{
"Effect": "Allow",
"Action": "iot:Receive",
"Resource": [
"arn:aws:iot:us-east-1:*:topic/$aws/certificates/create/*",
"arn:aws:iot:us-east-1:*:topic/$aws/provisioning-templates/*/provision/*"
]
}
]
}
Claim 证书安全性
claim 证书在所有设备间共享,且只授予 Fleet Provisioning 主题的访问权限。它们应被安全存储,但敏感程度不及逐设备证书 --- 泄露的 claim 证书只能创建新的 Thing,不能冒充已有设备。
Fleet Provisioning¶
新设备使用 Fleet Provisioning by Claim 在首次启动时获取设备专属证书。
流程:
设备(首次启动)
│
├── 使用 claim 证书连接到 IoT Core
│
├── 发布到 $aws/certificates/create/json
│ └── 接收新证书 + 私钥
│
├── 发布到 $aws/provisioning-templates/{template}/provision/json
│ └── 发送: { "SerialNumber": "{hwId}" }
│ └── 接收: { "thingName": "inklet-{prefix}" }
│
├── 存储设备证书、私钥和 thingName
│
└── 使用设备证书重新连接
└── 开始正常运行(心跳、命令)
配网完成后,设备在本地存储证书和 Thing 名称,之后不再使用 claim 证书。