72小时极速上手OpenHarmony智能家居开发:从环境搭建到多设备联动全攻略
你是否还在为智能家居设备碎片化开发烦恼?是否因配网复杂、协议不统一而放弃项目?本文将带你从零开始,72小时内掌握OpenHarmony智能家居开发全流程,完成从环境搭建到多设备联动的实战项目。读完本文你将获得:
- 一套完整的OpenHarmony智能家居开发环境
- 智能设备无感配网核心技术
- 多设备联动场景开发能力
- 数字管家应用全流程开发经验
一、项目架构与核心价值
OpenHarmony-SIG/knowledge_demo_smart_home项目提供了从设备端到应用端的完整智能家居解决方案,通过数字管家应用实现多设备统一管理与场景联动。项目采用分层架构设计,确保各模块解耦与可扩展性:
1.1 核心功能模块
| 模块 | 功能描述 | 技术栈 |
|---|---|---|
| FA应用 | 设备控制、场景编排、用户管理 | ArkUI、JavaScript |
| Server服务 | 设备管理、日程调度、消息转发 | Spring Boot、MySQL、RabbitMQ |
| 设备端 | 传感器数据采集、执行器控制 | C、IoT SDK |
| Profile模型 | 设备能力描述、数据交互规范 | JSON Schema |
1.2 解决的核心痛点
- 配网复杂:采用NAN协议实现无感配网,无需手动输入WiFi密码
- 协议碎片化:统一Profile设备模型定义,支持跨厂商设备互联
- 场景联动难:可视化日程编排系统,轻松实现多设备协同场景
- 开发门槛高:提供完整demo代码与文档,降低智能家居开发难度
二、开发环境极速搭建
2.1 硬件准备清单
| 设备 | 推荐型号 | 用途 |
|---|---|---|
| 开发板 | BearPi-HM Nano | 智能设备原型开发 |
| 手机 | HarmonyOS 2.0+ | 数字管家应用运行 |
| NFC标签 | 兼容ISO 14443A | 设备身份标识 |
| 传感器模块 | E53-SC1拓展板 | 环境数据采集 |
2.2 软件环境配置
2.2.1 DevEco Studio安装
# 下载DevEco Studio 3.0 Beta1及以上版本
wget https://developer.harmonyos.com/cn/develop/deveco-studio#download
# 安装依赖
sudo apt-get install libnss3-dev libxss1 libasound2 libx11-xcb1
# 赋予执行权限并安装
chmod +x deveco-studio-*.bin
./deveco-studio-*.bin
2.2.2 SDK配置
启动DevEco Studio后,在SDK Manager中安装以下组件:
- OpenHarmony SDK API Version 6
- Node.js 14.19.1
- HarmonyOS Legacy SDK
2.2.3 代码仓库获取
git clone https://gitcode.com/openharmony-sig/knowledge_demo_smart_home
cd knowledge_demo_smart_home
三、设备端开发实战
3.1 智能台灯设备开发
以智能台灯为例,完整实现设备端开发流程:
3.1.1 硬件连接
E53-SC1拓展板与BearPi-HM Nano连接方式:
- SDA -> GPIO0
- SCL -> GPIO1
- PWM -> GPIO2
- 电源 -> 3.3V
- 地线 -> GND
3.1.2 设备端代码结构
team_x/smart_lamp/
├── include/ # 头文件目录
│ ├── lamp_control.h # 台灯控制接口
│ └── sensor.h # 传感器数据采集接口
├── src/ # 源代码目录
│ ├── main.c # 程序入口
│ ├── lamp_control.c # 台灯控制实现
│ └── sensor.c # 传感器数据采集实现
└── BUILD.gn # 编译配置文件
3.1.3 核心代码实现
主程序入口 (main.c)
#include "ohos_init.h"
#include "kernel_init.h"
#include "lamp_control.h"
#include "sensor.h"
#include "iot_wifi.h"
#include "network_config_service.h"
void LampDeviceInit(void)
{
// 初始化传感器
SensorInit();
// 初始化台灯控制
LampControlInit();
// 初始化WiFi和配网服务
IoTWiFiInit();
NetworkConfigServiceStart();
// 启动传感器数据上报线程
osThreadAttr_t attr;
attr.name = "SensorReportThread";
attr.stack_size = 4096;
attr.priority = osPriorityNormal;
osThreadNew(SensorReportThread, NULL, &attr);
}
SYS_RUN(LampDeviceInit);
无感配网实现 (network_config.c)
#include "network_config_service.h"
#include "iot_netcfg_nan.h"
#include "iot_wifi.h"
static void OnNetConfigSuccess(const char* ssid, const char* password)
{
// 配网成功,连接WiFi
printf("NetConfig success, connecting to %s...\n", ssid);
IoTWiFiConnect(ssid, password);
// 保存配网信息到KV存储
SaveWiFiConfig(ssid, password);
// 切换LED状态指示
SetLedState(LED_STATE_CONNECTED);
}
void NetworkConfigServiceStart(void)
{
// 检查是否已有保存的WiFi配置
char ssid[32] = {0};
char password[64] = {0};
if (LoadWiFiConfig(ssid, password) == 0) {
// 已有配置,直接连接
IoTWiFiConnect(ssid, password);
SetLedState(LED_STATE_CONNECTED);
return;
}
// 无保存配置,启动NAN配网
NetConfigNanConfig config = {0};
config.callback = OnNetConfigSuccess;
config.deviceType = DEVICE_TYPE_LAMP;
config.nodeId = "Lamp001";
config.devicePwd = "12345678";
IoTNetcfgNanStart(&config);
// 进入配网状态,LED快闪
SetLedState(LED_STATE_CONFIGURING);
}
3.2 设备固件烧录
3.2.1 烧录工具配置
- 安装CH340驱动
sudo apt-get install linux-headers-$(uname -r)
git clone https://github.com/juliagoda/CH341SER.git
cd CH341SER
make
sudo make load
- 使用HiBurn工具烧录
# 下载HiBurn工具
wget https://harmonyos.51cto.com/resource/29 -O HiBurn.zip
unzip HiBurn.zip
chmod +x HiBurn/HiBurn.exe
# 启动HiBurn并配置
wine HiBurn/HiBurn.exe
3.2.2 烧录步骤
- 选择串口:点击"Refresh"后选择识别到的COM端口
- 设置波特率:921600
- 选择固件:
dev/docs/quick_start/resource/image/Hi3861_wifiiot_app_allinone.bin - 勾选"Auto burn",点击"Connect"
- 按下开发板RESET键开始烧录
四、FA应用开发详解
4.1 数字管家应用结构
FA/DistSchedule/
├── entry/ # 主应用模块
│ ├── src/main/js/ # JS代码目录
│ │ ├── default/ # 默认应用
│ │ │ ├── pages/ # 页面目录
│ │ │ │ ├── index/ # 首页
│ │ │ │ ├── device/ # 设备控制页
│ │ │ │ └── scene/ # 场景编排页
│ │ │ ├── app.js # 应用入口
│ │ │ └── config.json # 应用配置
│ └── src/main/resource/ # 资源文件
└── netconfig/ # 配网模块
└── src/main/js/ # 配网页面代码
4.2 设备控制页面开发
设备控制页 (device/index.hml)
<div class="device-page">
<div class="device-header">
<image src="/common/lamp.png" class="device-icon"></image>
<text class="device-name">{{deviceName}}</text>
<switch checked="{{isOn}}" onchange="togglePower" class="power-switch"></switch>
</div>
<div class="device-control" if="{{isOn}}">
<text class="control-label">亮度调节</text>
<slider value="{{brightness}}" min="1" max="100" step="1" onchange="setBrightness" class="brightness-slider"></slider>
<text class="brightness-value">{{brightness}}%</text>
<text class="control-label">色温调节</text>
<div class="color-temp">
<div class="temp-option" style="background-color: #FFE0B2;" onclick="setTemp(2700)"></div>
<div class="temp-option" style="background-color: #FFF9C4;" onclick="setTemp(4000)"></div>
<div class="temp-option" style="background-color: #E3F2FD;" onclick="setTemp(6500)"></div>
</div>
</div>
<div class="scene-link" onclick="gotoScene">
<text class="link-text">添加到场景</text>
<image src="/common/arrow-right.png" class="link-icon"></image>
</div>
</div>
页面样式 (device/index.css)
.device-page {
flex-direction: column;
padding: 20px;
background-color: #F5F5F5;
}
.device-header {
flex-direction: row;
align-items: center;
padding: 15px;
background-color: #FFFFFF;
border-radius: 12px;
margin-bottom: 20px;
}
.device-icon {
width: 60px;
height: 60px;
margin-right: 15px;
}
.device-name {
flex: 1;
font-size: 20px;
font-weight: bold;
}
.power-switch {
width: 50px;
height: 30px;
}
.device-control {
flex-direction: column;
padding: 20px;
background-color: #FFFFFF;
border-radius: 12px;
}
.control-label {
font-size: 16px;
color: #666666;
margin: 15px 0 5px 0;
}
.brightness-slider {
width: 100%;
height: 6px;
margin: 10px 0;
}
.brightness-value {
font-size: 14px;
color: #333333;
text-align: right;
margin-bottom: 20px;
}
.color-temp {
flex-direction: row;
justify-content: space-between;
margin: 10px 0;
}
.temp-option {
width: 60px;
height: 60px;
border-radius: 8px;
}
.scene-link {
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 15px;
background-color: #FFFFFF;
border-radius: 12px;
margin-top: 20px;
}
.link-text {
font-size: 16px;
color: #007AFF;
}
.link-icon {
width: 20px;
height: 20px;
}
页面逻辑 (device/index.js)
import deviceService from '../../common/deviceService';
export default {
data: {
deviceName: '智能台灯',
deviceId: '',
isOn: false,
brightness: 80,
colorTemp: 4000
},
onInit() {
// 获取设备信息
this.deviceId = this.$routerParams.deviceId;
this.deviceName = this.$routerParams.deviceName || '智能台灯';
// 订阅设备状态更新
deviceService.subscribeDeviceStatus(this.deviceId, (status) => {
this.isOn = status.power;
this.brightness = status.brightness;
this.colorTemp = status.colorTemp;
});
// 获取当前设备状态
deviceService.getDeviceStatus(this.deviceId).then(status => {
this.isOn = status.power;
this.brightness = status.brightness;
this.colorTemp = status.colorTemp;
});
},
togglePower(checked) {
this.isOn = checked.checked;
deviceService.controlDevice(this.deviceId, {
power: this.isOn
});
},
setBrightness(e) {
this.brightness = e.value;
deviceService.controlDevice(this.deviceId, {
brightness: this.brightness
});
},
setTemp(temp) {
this.colorTemp = temp;
deviceService.controlDevice(this.deviceId, {
colorTemp: this.colorTemp
});
},
gotoScene() {
this.$router.push({
uri: '/pages/scene/add',
params: {
deviceId: this.deviceId,
deviceName: this.deviceName
}
});
}
};
4.3 应用签名与安装
-
在AGC创建应用
- 访问https://developer.huawei.com/consumer/cn/service/josp/agc/index.html
- 创建新项目并添加应用,获取应用包名
-
配置应用签名
# 在项目根目录执行
hdc shell bm get -u > signature.p7b
keytool -importcert -file signature.p7b -keystore debug.keystore -alias ohos
- 编译并安装应用
# 编译HAP包
hvigor build -p ohos-arm64 -r release
# 安装到设备
hdc install entry/build/outputs/hap/release/entry-release-arm64.hap
hdc install netconfig/build/outputs/hap/release/netconfig-release-arm64.hap
五、无感配网核心技术
5.1 NAN协议配网原理
NAN (Neighbor Awareness Networking) 协议是一种Wi-Fi直连技术,允许设备在不连接AP的情况下发现邻近设备并进行通信。智能家居无感配网流程如下:
5.2 NFC标签数据格式
NFC标签存储设备配网所需的关键信息,格式定义如下:
| 字段ID | 名称 | 长度 | 描述 | 示例值 |
|---|---|---|---|---|
| 1 | 产品ID | 24字节 | IoT平台设备产品标识 | 6128c7b60ad1ed0286680f19 |
| 2 | NodeID | 8字节 | 设备节点标识 | Lamp01 |
| 3 | 设备密钥 | 8字节 | 设备认证密码 | 12345678 |
| 4 | 配网类型 | 1字节 | 0:无需配网 1:NAN+SoftAP 2:SoftAP 3:BLE 4:NAN | 1 |
| 5 | AP热点名 | 12字节 | 设备自身热点名 | teamX-Lamp01 |
5.3 NFC标签写入工具
使用应用调测助手写入NFC标签:
- 安装应用调测助手APP
- 选择"NFC标签写入"功能
- 输入设备配网信息
- 将手机贴近NFC标签完成写入
六、服务端部署与集成
6.1 服务端架构
Server模块采用Spring Boot分层架构,实现设备管理、用户管理、日程调度等核心功能:
Server/
├── distschedule-core/ # 入口模块
│ ├── src/main/java/com/keno/distschedule/
│ │ ├── controller/ # API控制器
│ │ └── DistscheduleApplication.java # 应用入口
├── distschedule-service/ # 业务逻辑模块
│ └── src/main/java/com/keno/distschedule/service/
└── distschedule-dao/ # 数据访问模块
└── src/main/java/com/keno/distschedule/dao/
6.2 Docker容器化部署
使用Docker Compose一键部署服务端组件:
docker-compose.yml
version: '3'
services:
mysql:
image: mysql:5.7
container_name: distschedule-mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: distschedule
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
- ./distschedule-dao/src/main/resources/db/migration:/docker-entrypoint-initdb.d
rabbitmq:
image: rabbitmq:3-management
container_name: distschedule-rabbitmq
restart: always
ports:
- "5672:5672"
- "15672:15672"
environment:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
server:
build: .
container_name: distschedule-server
restart: always
depends_on:
- mysql
- rabbitmq
ports:
- "8080:8080"
environment:
SPRING_PROFILES_ACTIVE: docker
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/distschedule
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: 123456
SPRING_RABBITMQ_HOST: rabbitmq
volumes:
- ./application-docker.properties:/app/config/application-docker.properties
volumes:
mysql-data:
部署命令:
cd Server
mvn clean package -Dmaven.test.skip=true
docker-compose up -d
七、多设备联动场景开发
7.1 场景编排数据模型
场景由触发器和执行动作组成,数据模型定义如下:
场景数据结构
{
"sceneId": "scene_001",
"name": "回家模式",
"icon": "home",
"trigger": {
"type": "time",
"value": "18:00",
"repeat": "1111100" // 周一至周五
},
"actions": [
{
"deviceId": "lamp_001",
"property": "power",
"value": true
},
{
"deviceId": "curtain_001",
"property": "position",
"value": 50
},
{
"deviceId": "humidifier_001",
"property": "power",
"value": true,
"delay": 300 // 延迟5分钟执行
}
]
}
7.2 场景执行引擎实现
场景调度服务 (SceneScheduler.java)
@Service
public class SceneScheduler {
@Autowired
private SceneDao sceneDao;
@Autowired
private DeviceService deviceService;
@Autowired
private Scheduler scheduler;
@PostConstruct
public void init() {
// 加载所有场景并调度
List<Scene> scenes = sceneDao.findAllEnabledScenes();
for (Scene scene : scenes) {
scheduleScene(scene);
}
}
public void scheduleScene(Scene scene) {
// 根据触发器类型调度场景
if ("time".equals(scene.getTrigger().getType())) {
scheduleTimeTriggeredScene(scene);
} else if ("device".equals(scene.getTrigger().getType())) {
registerDeviceTriggeredScene(scene);
}
}
private void scheduleTimeTriggeredScene(Scene scene) {
String cronExpression = buildCronExpression(scene.getTrigger());
scheduler.schedule(new SceneJob(scene, deviceService), new CronTrigger(cronExpression));
}
private String buildCronExpression(Trigger trigger) {
// 将时间触发器转换为Cron表达式
String[] timeParts = trigger.getValue().split(":");
int hour = Integer.parseInt(timeParts[0]);
int minute = Integer.parseInt(timeParts[1]);
// 根据重复模式设置星期几
String repeat = trigger.getRepeat();
StringBuilder cron = new StringBuilder();
cron.append("0 ").append(minute).append(" ").append(hour).append(" ? * ");
// 解析重复模式(1111100 -> 周一至周五)
List<Integer> weekdays = new ArrayList<>();
for (int i = 0; i < repeat.length(); i++) {
if (repeat.charAt(i) == '1') {
// Cron星期几从1(周日)到7(周六)
int cronDay = i == 0 ? 7 : i;
weekdays.add(cronDay);
}
}
if (weekdays.isEmpty()) {
cron.append("?"); // 不重复
} else {
cron.append(StringUtils.join(weekdays, ","));
}
return cron.toString();
}
public void executeScene(Scene scene) {
log.info("Executing scene: {}", scene.getName());
// 执行场景中的所有动作
for (SceneAction action : scene.getActions()) {
if (action.getDelay() > 0) {
// 延迟执行
scheduler.schedule(() -> executeAction(action),
new Date(System.currentTimeMillis() + action.getDelay() * 1000));
} else {
// 立即执行
executeAction(action);
}
}
}
private void executeAction(SceneAction action) {
try {
deviceService.controlDevice(action.getDeviceId(),
Collections.singletonMap(action.getProperty(), action.getValue()));
} catch (Exception e) {
log.error("Failed to execute action for device {}: {}",
action.getDeviceId(), e.getMessage());
}
}
}
7.3 场景编排页面开发
场景编辑页面 (scene/edit.hml)
<div class="scene-edit">
<div class="scene-header">
<text class="title">编辑场景</text>
<button class="save-btn" onclick="saveScene">保存</button>
</div>
<div class="scene-form">
<div class="form-item">
<text class="label">场景名称</text>
<input type="text" value="{{sceneName}}" onchange="updateName" placeholder="请输入场景名称"></input>
</div>
<div class="form-item">
<text class="label">触发方式</text>
<select value="{{triggerType}}" onchange="changeTriggerType">
<option value="time">定时触发</option>
<option value="device">设备触发</option>
<option value="manual">手动执行</option>
</select>
</div>
<div class="trigger-config" if="{{triggerType == 'time'}}">
<div class="time-picker">
<timepicker type="time" selected="{{triggerTime}}" onchange="setTriggerTime"></timepicker>
</div>
<div class="repeat-config">
<text class="repeat-label">重复</text>
<div class="weekdays">
<div class="day-item" onclick="toggleRepeat(0)">
<text class="{{repeat[0] ? 'active' : ''}}">日</text>
</div>
<div class="day-item" onclick="toggleRepeat(1)">
<text class="{{repeat[1] ? 'active' : ''}}">一</text>
</div>
<!-- 其他星期 -->
</div>
</div>
</div>
<div class="action-list">
<div class="action-header">
<text class="header-text">执行动作 ({{actions.length}})</text>
<button class="add-action" onclick="addAction">+ 添加动作</button>
</div>
<list class="actions">
<list-item for="{{actions}}" class="action-item">
<div class="action-info">
<image src="{{$item.icon}}" class="device-icon"></image>
<div class="action-detail">
<text class="device-name">{{$item.deviceName}}</text>
<text class="action-desc">{{$item.actionDesc}}</text>
</div>
</div>
<button class="delete-action" onclick="deleteAction($idx)">删除</button>
</list-item>
</list>
</div>
</div>
</div>
八、项目部署与测试
8.1 完整部署流程
8.2 测试用例设计
设备配网测试
| 测试项 | 测试步骤 | 预期结果 |
|---|---|---|
| NFC唤醒 | 1. 手机开启NFC 2. 碰一碰设备NFC标签 | 成功唤醒数字管家配网页面 |
| 无感配网 | 1. 设备进入配网模式 2. 手机碰一碰设备 | 配网成功,设备上线 |
| 配网失败处理 | 1. 断开WiFi 2. 尝试配网 | 显示配网失败提示,提供重试按钮 |
设备控制测试
| 测试项 | 测试步骤 | 预期结果 |
|---|---|---|
| 电源控制 | 1. 在设备页面点击开关 2. 观察设备状态 | 设备状态与控制一致,响应时间<2s |
| 亮度调节 | 1. 拖动亮度滑块 2. 观察设备亮度变化 | 亮度平滑变化,与滑块位置一致 |
| 多设备并发控制 | 1. 同时控制3个设备 2. 观察各设备响应 | 所有设备均能正确响应,无超时 |
场景联动测试
| 测试项 | 测试步骤 | 预期结果 |
|---|---|---|
| 定时场景 | 1. 设置1分钟后执行场景 2. 等待触发 | 场景按时执行,所有动作完成 |
| 手动场景 | 1. 在场景列表点击执行 2. 观察设备状态 | 场景立即执行,设备状态正确变化 |
| 设备触发场景 | 1. 触发设备状态变化 2. 观察联动设备 | 联动设备按预期执行动作 |
九、总结与进阶方向
9.1 项目成果总结
通过本文指南,你已完成以下内容:
- 搭建了完整的OpenHarmony智能家居开发环境
- 开发了智能台灯设备端固件
- 实现了数字管家应用的设备控制与场景编排功能
- 掌握了NAN协议无感配网技术
- 部署了服务端并实现多设备联动
9.2 进阶学习方向
-
设备接入扩展
- 学习Profile模型定义,接入新类型设备
- 开发自定义传感器驱动
-
AI能力集成
- 集成语音识别,实现语音控制
- 开发基于用户行为的场景推荐算法
-
安全增强
- 实现设备端OTA升级功能
- 开发基于数字证书的设备认证机制
-
性能优化
- 优化设备端功耗,延长续航
- 实现服务端集群部署,提高并发处理能力
9.3 学习资源推荐
- 官方文档:https://developer.harmonyos.com/cn/docs/documentation/doc-guides/start-overview-0000001054129005
- 代码仓库:https://gitcode.com/openharmony-sig/knowledge_demo_smart_home
- 开发社区:https://harmonyos.51cto.com/forum-4-1.html
- 视频教程:https://developer.harmonyos.com/cn/develop/videos
通过本项目实战,你已具备OpenHarmony智能家居开发的核心能力。继续深入学习与实践,你将能够构建更复杂的智能家居系统,为用户创造更智能、更便捷的生活体验。现在就开始你的智能家居开发之旅吧!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



