README
约 3174 字大约 11 分钟
2025-10-23
📋 目录
🎯 概述
本教程详细介绍如何将ESP32设备接入联犀(UnitedRhino)开源物联网云平台,并实现远程OTA(空中下载)固件升级功能。
学习目标
通过本教程,您将掌握:
- ✅ 云平台产品与设备的创建流程
- ✅ 设备接入云平台的代码实现
- ✅ 远程OTA升级功能的配置与实现
- ✅ 常见问题的排查与解决
🔧 前提条件
在开始之前,请确保您已具备以下条件:
| 项目 | 要求 | 说明 |
|---|---|---|
| 硬件 | ESP32开发板 | 推荐使用ESP32-WROOM-32或ESP32-S3 |
| 开发环境 | ESP-IDF 4.4+ | 已安装并配置完成 |
| 网络 | 稳定的WiFi连接 | 用于设备联网和OTA升级 |
| 账号 | 联犀云平台账号 | 可使用体验账户进行测试 |
🌐 云平台设备管理
3.1 登录云平台
访问平台入口
- 打开浏览器,访问:联犀云平台体验入口
- 使用提供的体验账号登录
进入物联网管理
- 登录成功后,系统会自动跳转至物联网管理主页面
- 界面如下所示:

3.2 创建产品
产品是设备的基础模板,定义了设备的基本属性和通信协议。
进入产品管理
- 点击左侧菜单:
设备接入→产品管理 - 点击
新建产品按钮
- 点击左侧菜单:
填写产品信息
- 产品类型:选择
设备、网关或子设备 - 通讯协议:选择合适的协议类型(推荐MQTT)
- 产品名称:输入有意义的名称
- 产品描述:简要描述产品功能
- 产品类型:选择
完成创建
- 点击
确认完成产品创建

- 点击
3.3 添加设备
设备是产品的具体实例,每个设备都有唯一的标识符。
进入设备管理
- 点击左侧菜单:
设备接入→设备管理 - 点击
新建设备按钮
- 点击左侧菜单:
填写设备信息
- 产品名称:选择刚创建的产品
- 设备ID:输入唯一标识符(建议使用MAC地址或序列号)
- 设备名称:输入设备的可读名称
- 设备描述:可选,描述设备用途
完成添加
- 点击
确认完成设备添加

- 点击
3.4 配置物模型
物模型定义了设备的功能属性,包括属性、命令和事件。
进入设备详情
- 在设备列表中找到刚创建的设备
- 点击
查看进入设备详情页面
配置物模型
- 切换到
物模型标签页 - 点击
添加物模型→新建自定义模型 - 根据设备功能配置相应的属性、命令和事件

- 切换到
3.5 生成MQTT连接凭证
MQTT三元组是设备连接云平台的关键凭证。
进入基本信息
- 在设备详情页面,切换到
基本信息标签页
- 在设备详情页面,切换到
生成凭证
- 点击
生成按钮 - 系统将自动生成以下信息:
- Client ID:客户端标识符
- Username:用户名
- Password:密码
- 点击
保存凭证
- 请妥善保存这些信息,后续设备端开发将使用

💻 设备接入代码实现
4.1 项目结构
建议的项目结构如下:
esp32_iot_project/
├── main/
│ ├── main.c # 主程序入口
│ ├── mqtt_client.c # MQTT客户端实现
│ ├── ota_handler.c # OTA升级处理
│ └── wifi_manager.c # WiFi管理
├── components/ # 自定义组件
├── CMakeLists.txt # 构建配置
└── sdkconfig # SDK配置4.2 MQTT客户端配置
创建MQTT客户端配置文件:
// mqtt_config.h
#ifndef MQTT_CONFIG_H
#define MQTT_CONFIG_H
// MQTT服务器配置
#define MQTT_BROKER_URL "mqtt://your-broker-url"
#define MQTT_BROKER_PORT 1883
// 设备凭证(从云平台获取)
#define MQTT_CLIENT_ID "your-client-id"
#define MQTT_USERNAME "your-username"
#define MQTT_PASSWORD "your-password"
// 主题配置
#define MQTT_TOPIC_DATA "/device/data"
#define MQTT_TOPIC_COMMAND "/device/command"
#define MQTT_TOPIC_OTA "/device/ota"
#endif4.3 MQTT客户端实现
// mqtt_client.c
#include "mqtt_client.h"
#include "mqtt_config.h"
#include "esp_log.h"
#include "cJSON.h"
static const char *TAG = "MQTT_CLIENT";
static esp_mqtt_client_handle_t mqtt_client = NULL;
// MQTT事件处理函数
static void mqtt_event_handler(void *handler_args, esp_event_base_t base,
int32_t event_id, void *event_data)
{
esp_mqtt_event_handle_t event = event_data;
esp_mqtt_client_handle_t client = event->client;
int msg_id;
switch (event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT连接成功");
// 订阅命令主题
msg_id = esp_mqtt_client_subscribe(client, MQTT_TOPIC_COMMAND, 1);
ESP_LOGI(TAG, "订阅命令主题成功, msg_id=%d", msg_id);
// 订阅OTA主题
msg_id = esp_mqtt_client_subscribe(client, MQTT_TOPIC_OTA, 1);
ESP_LOGI(TAG, "订阅OTA主题成功, msg_id=%d", msg_id);
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT连接断开");
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "收到MQTT消息");
ESP_LOGI(TAG, "主题: %.*s", event->topic_len, event->topic);
ESP_LOGI(TAG, "数据: %.*s", event->data_len, event->data);
// 处理接收到的消息
handle_mqtt_message(event->topic, event->topic_len,
event->data, event->data_len);
break;
case MQTT_EVENT_ERROR:
ESP_LOGE(TAG, "MQTT错误");
break;
default:
ESP_LOGI(TAG, "其他MQTT事件: %d", event_id);
break;
}
}
// 初始化MQTT客户端
esp_err_t mqtt_client_init(void)
{
esp_mqtt_client_config_t mqtt_cfg = {
.broker.address.uri = MQTT_BROKER_URL,
.session.protocol_ver = MQTT_PROTOCOL_V_5,
.network.disable_auto_reconnect = false,
.credentials.client_id = MQTT_CLIENT_ID,
.credentials.username = MQTT_USERNAME,
.credentials.authentication.password = MQTT_PASSWORD,
.session.last_will.topic = "/device/status",
.session.last_will.msg = "offline",
.session.last_will.msg_len = strlen("offline"),
.session.last_will.qos = 1,
.session.last_will.retain = true,
};
mqtt_client = esp_mqtt_client_init(&mqtt_cfg);
if (mqtt_client == NULL) {
ESP_LOGE(TAG, "MQTT客户端初始化失败");
return ESP_FAIL;
}
esp_mqtt_client_register_event(mqtt_client, ESP_EVENT_ANY_ID,
mqtt_event_handler, NULL);
esp_err_t ret = esp_mqtt_client_start(mqtt_client);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "MQTT客户端启动失败: %s", esp_err_to_name(ret));
return ret;
}
ESP_LOGI(TAG, "MQTT客户端初始化成功");
return ESP_OK;
}
// 发布数据到云平台
esp_err_t mqtt_publish_data(const char *data)
{
if (mqtt_client == NULL) {
ESP_LOGE(TAG, "MQTT客户端未初始化");
return ESP_FAIL;
}
int msg_id = esp_mqtt_client_publish(mqtt_client, MQTT_TOPIC_DATA,
data, 0, 1, 0);
if (msg_id < 0) {
ESP_LOGE(TAG, "数据发布失败");
return ESP_FAIL;
}
ESP_LOGI(TAG, "数据发布成功, msg_id=%d", msg_id);
return ESP_OK;
}4.4 消息处理实现
// 处理MQTT消息
static void handle_mqtt_message(const char *topic, int topic_len,
const char *data, int data_len)
{
// 检查是否为OTA升级消息
if (strncmp(topic, MQTT_TOPIC_OTA, topic_len) == 0) {
handle_ota_message(data, data_len);
return;
}
// 检查是否为命令消息
if (strncmp(topic, MQTT_TOPIC_COMMAND, topic_len) == 0) {
handle_command_message(data, data_len);
return;
}
ESP_LOGW(TAG, "未知消息主题: %.*s", topic_len, topic);
}
// 处理命令消息
static void handle_command_message(const char *data, int data_len)
{
cJSON *root = cJSON_ParseWithLength(data, data_len);
if (root == NULL) {
ESP_LOGE(TAG, "命令消息解析失败");
return;
}
cJSON *command = cJSON_GetObjectItem(root, "command");
if (command && cJSON_IsString(command)) {
ESP_LOGI(TAG, "收到命令: %s", command->valuestring);
// 根据命令执行相应操作
if (strcmp(command->valuestring, "restart") == 0) {
ESP_LOGI(TAG, "执行重启命令");
esp_restart();
} else if (strcmp(command->valuestring, "get_status") == 0) {
// 发送设备状态
send_device_status();
}
}
cJSON_Delete(root);
}
// 发送设备状态
static void send_device_status(void)
{
cJSON *status = cJSON_CreateObject();
cJSON_AddStringToObject(status, "device_id", MQTT_CLIENT_ID);
cJSON_AddStringToObject(status, "status", "online");
cJSON_AddNumberToObject(status, "uptime", esp_timer_get_time() / 1000000);
cJSON_AddNumberToObject(status, "free_heap", esp_get_free_heap_size());
char *status_string = cJSON_Print(status);
mqtt_publish_data(status_string);
free(status_string);
cJSON_Delete(status);
}🔄 设备OTA升级实现
5.1 云平台OTA配置
进入OTA管理
- 登录联犀云平台
- 点击左侧菜单:
运维管理→OTA升级

创建升级包
- 点击
新建升级包按钮 - 填写升级包信息:
- 升级包名称:输入有意义的名称(如:v1.0.1)
- 升级包描述:详细描述本次升级内容
- 选择设备类型:选择对应的产品
- 上传固件:上传编译好的ESP32固件(.bin文件)
- 点击
确认完成升级包创建

- 点击
5.2 OTA处理代码实现
// ota_handler.c
#include "ota_handler.h"
#include "esp_log.h"
#include "esp_https_ota.h"
#include "esp_ota_ops.h"
#include "cJSON.h"
static const char *TAG = "OTA_HANDLER";
// 处理OTA升级消息
void handle_ota_message(const char *data, int data_len)
{
cJSON *root = cJSON_ParseWithLength(data, data_len);
if (root == NULL) {
ESP_LOGE(TAG, "OTA消息解析失败");
return;
}
cJSON *method = cJSON_GetObjectItem(root, "method");
if (method && cJSON_IsString(method) &&
strcmp(method->valuestring, "upgrade") == 0) {
ESP_LOGI(TAG, "收到OTA升级指令");
handle_ota_upgrade(root);
} else {
ESP_LOGW(TAG, "未知的OTA消息类型");
}
cJSON_Delete(root);
}
// 处理OTA升级
static void handle_ota_upgrade(cJSON *root)
{
cJSON *params = cJSON_GetObjectItem(root, "params");
if (params == NULL) {
ESP_LOGE(TAG, "OTA参数缺失");
return;
}
cJSON *url = cJSON_GetObjectItem(params, "url");
cJSON *version = cJSON_GetObjectItem(params, "version");
cJSON *size = cJSON_GetObjectItem(params, "size");
if (url == NULL || version == NULL) {
ESP_LOGE(TAG, "OTA升级信息不完整");
return;
}
ESP_LOGI(TAG, "开始OTA升级");
ESP_LOGI(TAG, "版本: %s", version->valuestring);
ESP_LOGI(TAG, "URL: %s", url->valuestring);
if (size) {
ESP_LOGI(TAG, "大小: %d bytes", size->valueint);
}
// 执行OTA升级
esp_err_t ret = perform_ota_upgrade(url->valuestring);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "OTA升级成功,即将重启");
esp_restart();
} else {
ESP_LOGE(TAG, "OTA升级失败: %s", esp_err_to_name(ret));
}
}
// 执行OTA升级
static esp_err_t perform_ota_upgrade(const char *url)
{
esp_http_client_config_t config = {
.url = url,
.cert_pem = NULL, // 如果使用HTTPS,需要提供证书
.timeout_ms = 30000,
};
esp_https_ota_config_t ota_config = {
.http_config = &config,
.http_client_init_cb = NULL,
.bulk_flash_erase = true,
.partial_http_download = true,
.max_http_request_size = 1024,
};
esp_err_t ret = esp_https_ota(&ota_config);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "OTA升级失败: %s", esp_err_to_name(ret));
return ret;
}
ESP_LOGI(TAG, "OTA升级完成");
return ESP_OK;
}
// 获取当前固件版本
const char* get_firmware_version(void)
{
const esp_app_desc_t *app_desc = esp_app_get_description();
return app_desc->version;
}
// 检查是否有新版本
bool check_for_updates(void)
{
// 这里可以实现版本检查逻辑
// 例如:向服务器查询最新版本信息
return false;
}5.3 主程序集成
// main.c
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "wifi_manager.h"
#include "mqtt_client.h"
#include "ota_handler.h"
static const char *TAG = "MAIN";
void app_main(void)
{
ESP_LOGI(TAG, "设备启动中...");
ESP_LOGI(TAG, "固件版本: %s", get_firmware_version());
// 初始化WiFi
if (wifi_init() != ESP_OK) {
ESP_LOGE(TAG, "WiFi初始化失败");
return;
}
// 等待WiFi连接
if (wifi_wait_connected(10000) != ESP_OK) {
ESP_LOGE(TAG, "WiFi连接超时");
return;
}
ESP_LOGI(TAG, "WiFi连接成功");
// 初始化MQTT客户端
if (mqtt_client_init() != ESP_OK) {
ESP_LOGE(TAG, "MQTT客户端初始化失败");
return;
}
ESP_LOGI(TAG, "设备初始化完成");
// 主循环
while (1) {
// 发送心跳数据
send_heartbeat();
// 检查OTA更新
if (check_for_updates()) {
ESP_LOGI(TAG, "发现新版本,准备升级");
}
vTaskDelay(pdMS_TO_TICKS(30000)); // 30秒间隔
}
}
// 发送心跳数据
static void send_heartbeat(void)
{
cJSON *heartbeat = cJSON_CreateObject();
cJSON_AddStringToObject(heartbeat, "type", "heartbeat");
cJSON_AddNumberToObject(heartbeat, "timestamp", esp_timer_get_time() / 1000000);
cJSON_AddNumberToObject(heartbeat, "free_heap", esp_get_free_heap_size());
cJSON_AddStringToObject(heartbeat, "version", get_firmware_version());
char *heartbeat_string = cJSON_Print(heartbeat);
mqtt_publish_data(heartbeat_string);
free(heartbeat_string);
cJSON_Delete(heartbeat);
}5.4 执行OTA升级
触发升级
- 在云平台的OTA升级页面,找到刚创建的升级包
- 点击
验证按钮,系统将向目标设备下发升级指令
升级过程
- 设备接收到升级指令后,自动下载新固件
- 下载完成后,验证固件完整性
- 验证通过后,执行固件更新
- 更新完成后,设备自动重启
升级结果
- 设备重启后运行新版本固件
- 可通过日志查看升级过程
- 升级失败时,设备会回滚到原版本
❓ 常见问题与解决方案
6.1 连接问题
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 设备无法连接云平台 | WiFi连接异常 | 检查WiFi配置和信号强度 |
| MQTT连接失败 | 凭证错误 | 验证Client ID、Username、Password |
| 连接频繁断开 | 网络不稳定 | 检查网络质量,增加重连机制 |
6.2 OTA升级问题
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| OTA升级失败 | 固件格式错误 | 确认固件为.bin格式 |
| 下载超时 | 网络连接不稳定 | 检查网络连接,增加超时时间 |
| 升级后无法启动 | 固件损坏 | 检查固件完整性,使用备份分区 |
6.3 数据通信问题
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 数据上报失败 | 消息格式错误 | 检查JSON格式和字段名称 |
| 命令下发无响应 | 主题订阅错误 | 确认订阅的主题名称正确 |
| 消息丢失 | QoS设置不当 | 使用QoS 1或2确保消息送达 |
6.4 调试技巧
启用详细日志
esp_log_level_set("*", ESP_LOG_DEBUG);使用串口监控
idf.py monitor检查网络连接
esp_ping_config_t ping_config = ESP_PING_DEFAULT_CONFIG(); ping_config.target_addr = esp_ip4addr_aton("8.8.8.8"); esp_ping_handle_t ping; esp_ping_new_session(&ping_config, &ping);
📚 资源链接
官方文档
开发工具
社区支持
🎉 总结
本教程详细介绍了联犀物联网平台的完整接入流程,包括:
核心功能实现
- ✅ 云平台配置:产品创建、设备管理、物模型配置
- ✅ 设备接入:MQTT连接、消息处理、数据上报
- ✅ OTA升级:远程固件更新、版本管理、错误处理
- ✅ 故障排查:常见问题解决、调试技巧、性能优化
最佳实践建议
- 安全性:使用TLS加密连接,定期更新设备凭证
- 可靠性:实现断线重连、消息重发、错误恢复机制
- 性能:优化内存使用、减少网络流量、提高响应速度
- 维护性:完善的日志记录、版本管理、测试覆盖
下一步计划
- 实现设备分组管理
- 添加数据可视化功能
- 集成边缘计算能力
- 支持更多通信协议
通过本教程,您已经掌握了物联网设备接入和OTA升级的核心技术。如有疑问或需要技术支持,请参考相关文档或联系技术团队。
📝 文档版本:v1.0
🕒 最后更新:2024年12月
👨💻 维护团队:联犀技术团队
更新日志
2025/12/28 18:01
查看所有更新日志
bddea-docs: 更新文档结构、添加新内容并优化现有文档于
