这章来讲讲鸿蒙北向吧 如果你是学鸿蒙北向的同学,多半会遇到这样的需求:
“我们南向的设备把心率数据传到云上了,你做个 App 把数据显示出来,再做个按钮能控制风扇开关。”
这一章就是为解决此问题而出的教程
第一部分
物联网项目里其实有三条链路,初学最容易在脑子里搅成一团:
flowchart LR
subgraph S["南向:设备侧"]
D["① 硬件设备传感器 / 单片机"]
end
subgraph C["云:华为云 IoTDA"]
P["② 物联网平台"]
end
subgraph N["北向:应用侧"]
A["③ 手机 App鸿蒙 / 网页 / 上位机"]
end
D -->|"MQTT 上报数据"| P
P -->|"MQTT 下发命令"| D
A -->|"HTTPS 调 API 读数据"| P
A -->|"HTTPS 调 API 下发命令"| P三条链路的分工:
| 链路 | 谁到谁 | 用什么协议 | 谁负责 |
|---|---|---|---|
| ① → ② | 设备 → 云 | MQTT | 硬件同学 |
| ② → ① | 云 → 设备 | MQTT(下行) | 硬件同学 |
| ③ ↔ ② | App ↔ 云 | HTTPS / REST API | 你 |
设备用 MQTT 跟云说话,App 用 HTTPS 跟云说话。
设备走的是 MQTT,你走的是 HTTP 那一套,所以第二章学的 HTTP 请求、请求头、JSON、Content-Type,在这里全都能直接用上。
:::
1.1 为什么设备用 MQTT,而不用 HTTP?
你可能会问:既然 HTTP 这么好用,为什么设备不也用 HTTP?
sequenceDiagram
autonumber
participant D as 设备
participant C as 云平台
Note over D,C: HTTP:设备必须主动问
D->>C: 有没有给我的命令?
C-->>D: 没有
D->>C: 有没有给我的命令?
C-->>D: 没有
Note over D,C: MQTT:云可以直接推
D->>C: 建立长连接 + 订阅我的主题
C-->>D: 有命令了,推给你!| HTTP | MQTT | |
|---|---|---|
| 通信模式 | 请求-响应(只能客户端先发起) | 发布-订阅(双方都能主动推) |
| 连接 | 短连接,用完就断 | 长连接,一直挂着 |
| 开销 | 头部信息大 | 头部极小(最小只要 2 字节) |
| 适合 | 网页、调 API | 设备、传感器、弱网、省电场景 |
回想第二章讲的”HTTP 是无状态的、服务器不能主动给你发消息”,这正是设备场景不能忍的地方。
一台体温计不可能每隔几秒就跑去问云”有没有新命令”(那又费电又费流量)。它需要的是:连上就别断,云那边一有命令,立刻推过来。这就是 MQTT 的发布-订阅模型。
MQTT 的核心概念:主题(Topic)
MQTT 不靠”你问我答”,而是靠主题:
- 设备订阅某个主题,就相当于”我盯着这个频道的消息”
- 谁发布到这个主题,订阅者就会收到
比如设备订阅了 $oc/devices/{设备ID}/sys/commands/#,那么云平台往这个主题发布一条命令,设备立刻就能收到。
# 是通配符,表示”这个目录下的所有子主题”。
2. 云平台在中间干了什么
华为云 IoTDA(设备接入服务)扮演的是中间人 + 账本的角色:
- 设备管理:谁注册过、在不在线、是什么型号
- 消息转发:把设备上报的数据收下来
- 数据存储:把设备最新状态存成”设备影子”,这就是你待会儿要读的东西
- 命令下发:把你调的 API 请求,转成 MQTT 消息推给设备
第二部分 南向:设备是怎么接进来的
这部分内容平时是硬件同学在做,但你必须看懂,因为你要拿的就是这些配置信息。
3. 在 IoTDA 控制台上做四件事
设备侧的四步
创建产品
产品 = 一类设备的”型号定义”。比如”智慧养老手环”就是一个产品。
创建服务
服务 = 一个产品下面的一组功能集合。你可以先粗略理解成”一个大产品里包含的某个小硬件”。
这一步基本上只需要你填一个服务 ID,记住它,后面会反复用到。
创建属性
属性 = 具体的数据项,比如心率、血氧、体温。
创建时要填名称和数据类型(整数、小数、字符串、布尔……),名称就是数据名。
注册设备
设备 = 这个产品下的一台具体硬件。设备标识码和密钥可以自己填,注册完成后平台会给你一份信息,里面有设备 ID 和密钥:这两个东西千万别弄丢,也千万别外传。
3.1 属性到底要不要建?
有个常见的纠结:我到底该不该建属性?
按数据流向分两种情况:
- 需要把数据实时读到 App 里 → 建属性,让设备上报
- 只是 App 往设备下发命令(比如开关风扇),不需要回传数据 → 可以不建属性
4. MQTT 三元组:设备登录云的”账号密码”
设备要连上云,需要三个东西,俗称 MQTT 三元组:
| 名称 | 作用 | 从哪来 |
|---|---|---|
| ClientId(客户端 ID) | 我是哪台设备 | 由工具根据设备 ID 生成 |
| Username(用户名) | 我属于哪个设备 | 通常是 {设备ID} |
| Password(密码) | 证明我真的是那台设备 | 由密钥签名算出 |
华为云提供了一个在线工具,把设备 ID 和密钥填进去,三元组自动生成:
https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com/生成出来的东西大概长这样(下面是占位符,不是真实值):
ClientId:{设备ID}_0_0_{时间戳}Username:{设备ID}Password:{根据密钥算出的签名}第三部分 没有硬件,怎么先跑起来
手头没有硬件怎么办?用 MQTT 客户端软件模拟一台设备。 这样你可以先把整条链路跑通,等硬件到了直接替换。
推荐用 MQTTX(跨平台,界面清爽)。
5. 连接信息从哪找
在 IoTDA 控制台的设备详情页里,能找到设备接入地址:
| 配置项 | 值 |
|---|---|
| 服务器地址 | {你的设备接入域名}(形如 xxxxxx.st1.iotda-device.cn-north-4.myhuaweicloud.com) |
| 端口 | 1883(非加密)/ 8883(TLS 加密)—— 端口号是固定的 |
| ClientId / Username / Password | 上一节生成的三元组 |
6. 发布和订阅
连上之后,就要用**主题(Topic)**收发数据了。IoTDA 的主题格式是固定的,你只需要把 {设备ID} 换成自己的:
| 你要干什么 | 主题 | 动作 |
|---|---|---|
| 上报属性(设备 → 云) | $oc/devices/{设备ID}/sys/properties/report | 发布(Publish) |
| 订阅命令(云 → 设备) | $oc/devices/{设备ID}/sys/commands/# | 订阅(Subscribe) |
| 订阅属性设置(云 → 设备) | $oc/devices/{设备ID}/sys/properties/set/# | 订阅(Subscribe) |
6.1 上报的数据长什么样
上报属性的内容就是 JSON,格式是固定的两层结构:services 数组 → 里面是 service_id 和 properties:
{ "services": [ { "service_id": "health", "properties": { "heart_rate": 78, "spo2": 98, "temperature": 36.5 } } ]}6.2 完整的通信流程
sequenceDiagram
autonumber
participant D as 设备(或 MQTTX)
participant C as 华为云 IoTDA
participant A as 你的 App
D->>C: ① 用三元组建立 MQTT 长连接
D->>C: ② 订阅命令主题
D->>C: ③ 发布属性(心率/血氧/体温)
C->>C: ④ 更新设备影子
A->>C: ⑤ 调 API 读设备影子(HTTPS)
C-->>A: ⑥ 返回 JSON(含 reported 数据)
A->>C: ⑦ 调 API 修改设备属性(下发命令)
C-->>D: ⑧ MQTT 推送命令到设备
D-->>A: (可选)设备上报新状态,App 再读一次影子第四部分 北向:App 要调哪些接口
动手写代码之前,有三个问题绕不开:
- 调用什么 API 接口获取设备上传的数据?
- 调用什么 API 接口下发命令给设备?
- 调用这些接口需要什么参数?
7. 先拿凭证:IAM Token
App 要调华为云的接口,得先证明”我有权限”。这个凭证就是 Token。
7.1 准备好这几样东西
| 名称 | 说明 |
|---|---|
| IAM 账号 / 密码 | 你创建的子账号和密码 |
| 主账号名称 | 企业管理员名称(华为云账号名) |
| AK / SK | Access Key / Secret Key,程序调用时用的密钥对 |
| 项目 ID | Project ID,标识你的资源在哪个项目下 |
项目 ID 在哪找:控制台右上角 → 我的凭证 → 项目列表 → 点进你在做的项目,就能看到项目 ID 和所属区域。
AK/SK 怎么拿:找到右上角统一身份认证,创建一个子账号,创建并配置完成后就会得到 user name(IAM 账号名)、AK 和 SK。
7.2 用 API Explorer 生成 Token
华为云的 API Explorer 可以在线帮你拼请求、直接调试、还会自动生成 SDK 代码,这是北向开发最省事的入口。
https://apiexplorer.developer.huaweicloud.com/按顺序把参数填进去,遇到不确定的字段就用默认生成的,只要最后右边的「生成 Token」是成功的就说明配对了。
Token 有有效期,通常是 24 小时,过期就会返回 401。所以 App 里要处理”token 失效就重新获取”这件事。
8. 读数据:查询设备影子
这是回答”调用什么接口获取设备上传数据”的答案。
| 项目 | 内容 |
|---|---|
| 接口名 | 查询设备影子数据(ShowDeviceShadow) |
| 方法 | GET |
| 文档 | API Explorer 搜 ShowDeviceShadow |
请求地址的格式是:
GET https://{应用侧接入点}/v5/iot/{项目ID}/devices/{设备ID}/shadowHeader: X-Auth-Token: {你的 Token}9. 下命令:两种方式
回答第二个问题,怎么让 App 控制硬件。IoTDA 提供两条路:
| 方式 | 特点 |
|---|---|
| 法一:下发命令(同步命令) | 语义明确,适合”立刻执行一次”的动作 |
| 法二:修改设备属性 | 语义是”把设备改成这个状态”,更贴近物联网场景 |
两者除了接口不同,功能上没有本质区别。 下面用**法二(修改设备属性)**来演示,因为它跟前面的”属性”概念是连贯的。
| 项目 | 内容 |
|---|---|
| 接口名 | 修改设备属性(UpdateProperties) |
| 方法 | PUT |
| 地址 | https://{应用侧接入点}/v5/iot/{项目ID}/devices/{设备ID}/properties |
下发命令的三个前提
保证设备在线
设备离线的话,命令发不出去。先在控制台确认设备状态是”在线”。
填写数据
想改哪个属性就写哪个,注意类型和名字要和建属性时一致。
构造并调试请求体
请求体是 JSON,格式必须自己写对,写完先在 API Explorer 里调试通过。
请求体长这样:
{ "services": [ { "service_id": "health", "properties": { "fan_switch": true } } ]}调试成功后,服务器就会把这条指令通过 MQTT 推给设备。设备收到 true,代码里做个字符串判断,就可以打开风扇。
第五部分 落到鸿蒙:北向 App 怎么写
前面都是”看懂”,现在讲”写出来”。
10. 整体思路:四步走
flowchart LR
A["① 拿 TokenPOST IAM 接口"] --> B["② 读影子GET shadow"]
B --> C["③ 显示到界面ArkTS UI"]
C --> D["④ 下发命令PUT properties"]
D --> B11. ArkTS 里怎么发 HTTPS 请求
鸿蒙应用侧用 @kit.NetworkKit 的 http 模块就能发 HTTPS 请求。
11.1 拿 Token
import { http } from '@kit.NetworkKit';
const IAM_ENDPOINT = 'https://iam.cn-north-4.myhuaweicloud.com';const PROJECT_NAME = 'cn-north-4';
export async function getToken(account: string, password: string, domain: string): Promise<string> { const req = http.createHttp(); try { const resp = await req.request(`${IAM_ENDPOINT}/v3/auth/tokens`, { method: http.RequestMethod.POST, header: { 'Content-Type': 'application/json' }, extraData: JSON.stringify({ auth: { identity: { methods: ['password'], password: { user: { name: account, password, domain: { name: domain } } } }, scope: { project: { name: PROJECT_NAME } } } }) });
// Token 不在 body 里,而是在响应头 X-Subject-Token 中 return resp.header['X-Subject-Token'] as string; } finally { req.destroy(); }}11.2 读设备影子
const APP_ENDPOINT = '{你的应用侧接入点}';const PROJECT_ID = '{你的项目 ID}';const DEVICE_ID = '{你的设备 ID}';
export async function getShadow(token: string): Promise<ShadowResp> { const req = http.createHttp(); try { const resp = await req.request( `https://${APP_ENDPOINT}/v5/iot/${PROJECT_ID}/devices/${DEVICE_ID}/shadow`, { method: http.RequestMethod.GET, header: { 'X-Auth-Token': token } } ); return JSON.parse(resp.result as string) as ShadowResp; } finally { req.destroy(); }}11.3 下发命令(改属性)
export async function setFan(token: string, on: boolean) { const req = http.createHttp(); try { return await req.request( `https://${APP_ENDPOINT}/v5/iot/${PROJECT_ID}/devices/${DEVICE_ID}/properties`, { method: http.RequestMethod.PUT, header: { 'X-Auth-Token': token, 'Content-Type': 'application/json' // 别忘了它,服务器才知道怎么解包 }, extraData: JSON.stringify({ services: [{ service_id: 'health', properties: { fan_switch: on } }] }) } ); } finally { req.destroy(); }}12. 写在最后:几个能让作品加分的地方
-
别把凭证写死在代码里 Token 有 24 小时有效期,AK/SK 更是永久凭证。比赛演示可以在本地配置,但提交的代码里一定要留占位符。
-
给状态加个”刷新”和”自动轮询” 设备状态会变,界面得能跟着变。最简单的做法是定时轮询影子接口(比如 3~5 秒一次),进阶可以用 WebSocket。
-
命令下发要有反馈 按钮点下去之后,别只是”发了请求”。要提示”发送中 / 成功 / 失败”,最好再读一次影子确认状态真的变了。
-
UI 是评委最直观的感受 数据卡片、状态色块、开关动画,这些”面子工程”在比赛里真的很重要,而且这正是鸿蒙北向同学的核心优势。
13. 三条链路对照
| 环节 | 谁做 | 用什么 | 你要关心的 |
|---|---|---|---|
| 设备连上云 | 硬件同学 | MQTT 三元组 | 知道设备 ID 就行 |
| 设备上报数据 | 硬件同学 | MQTT 发布到 properties/report | 知道服务 ID、属性名 |
| 云保存状态 | 华为云 IoTDA | 设备影子 | 记住 reported / desired |
| App 读数据 | 你 | HTTPS GET .../shadow | 带 Token |
| App 下命令 | 你 | HTTPS PUT .../properties | 设备在线 + JSON 正确 |
最后如果你在开发需要好用的API接口,你可以看看你想接入的任何产品接口文档,还有华为云自己的华为开发者联盟
分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
换条路线
从其他文章中稳定抽取
最后更新于 ,距今已过 36 天
部分内容可能已过时
