作为一名嵌入式开发工程师,最近在做一个基于STM32的智慧物联网项目,需要将设备连接到ThingsCloud物联网平台。这个项目让我对物联网设备与云平台的交互有了更深入的理解,特别是MQTT协议在实际应用中的实现方式。
ThingsCloud是一个功能强大的物联网云平台,支持多种通信协议和设备管理功能。通过这个项目,我成功实现了STM32设备通过Wi-Fi模块(安信可AI-WB2-12F)连接到ThingsCloud平台,并完成了数据上报和命令下发的完整流程。
在这个项目中,我选择了以下硬件组件:
主控芯片:STM32F103C8T6
Wi-Fi模块:安信可AI-WB2-12F
其他外设:
硬件连接相对简单,主要是将Wi-Fi模块通过串口与STM32连接:
code复制STM32 USART3_TX -> AI-WB2-12F RX
STM32 USART3_RX -> AI-WB2-12F TX
STM32 GND -> AI-WB2-12F GND
STM32 3.3V -> AI-WB2-12F VCC
注意:确保Wi-Fi模块的供电稳定,不稳定的电源可能导致模块工作异常。
首先需要在ThingsCloud平台注册账号并创建项目:
在项目中需要先创建设备类型,然后添加具体设备:
为了方便查看设备数据和控制设备,可以创建一个用户应用:
本项目使用Keil MDK作为开发环境:
为了方便控制Wi-Fi模块,我封装了一系列AT指令发送函数:
c复制// WIFI发送命令,检查命令返状态信息
static u8 ESP8266_SendCmd(const char *cmd, const char *stat)
{
u8 i = 0;
u16 j = 0;
for (i = 0; i < 3; i++) {
usart3_cnt = 0;
usart3_flag = 0;
USARTx_SendStr(USART3, (u8 *)cmd); // 发送命令
for (j = 0; j < 5000; j++) {
if (usart3_flag) {
usart3_buffer[usart3_cnt] = '\0';
if (strstr((char *)usart3_buffer, stat)) {
usart3_cnt = 0;
usart3_flag = 0;
return 0; // 发送成功
} else {
usart3_cnt = 0;
usart3_flag = 0;
}
}
Delay_Ms(1);
}
Delay_Ms(100);
}
return 1; // 命令发送失败
}
这个函数实现了命令的自动重发机制,提高了通信的可靠性。
STA模式初始化函数实现了Wi-Fi连接的完整流程:
c复制u8 ESP8266_STA_Init(const char *wifi_name, const char *wifi_key)
{
// 1. 退出透传模式
int i = 0;
for (i = 0; i < 3; i++) {
if (ESP8266_SendCmd("AT\r\n", "OK\r\n") == 0) {
i = 0;
break;
}
printf("第%d次退出透传\r\n", i + 1);
USARTx_SendStr(USART3, (u8 *)"+++\r\n"); // 退出透传模式
Delay_Ms(500);
}
if (i >= 3) return 1; // 退出透传失败
char buffer[100];
printf("2. 关回显ATE0\r\n");
if (ESP8266_SendCmd("ATE0\r\n", "OK")) return 2;
printf("3. 查询WIFI自动连接:\r\n");
if (ESP8266_SendCmd("AT+WAUTOCONN?\r\n", "+WAUTOCONN") == 0) {
printf("rx3=%s\n", usart3_buffer);
if (strstr((char *)usart3_buffer, "+WAUTOCONN:0")) {
printf("3.1 设置WIFI自动重连:\r\n");
snprintf(buffer, sizeof(buffer), "AT+WAUTOCONN=1,%s,%s\r\n", wifi_name, wifi_key);
if (ESP8266_SendCmd(buffer, "OK\r\n")) return 3;
printf("3.2 重启WIFI模块\r\n");
if (ESP8266_SendCmd("AT+RST\r\n", "ready")) return 4;
}
}
printf("等待WIFI连接成功\r\n");
if (ESP8266_WaitConnect()) {
printf("4. 查询连接WIFI信息:\r\n");
u8 ret = ESP8266_CheckStaInfo();
if (ret == 0 || ret == 4) { // wifi信息错误
printf("ret=%d\n", ret);
printf("4.1 关闭WIFI自动重连:\r\n");
if (ESP8266_SendCmd("AT+WAUTOCONN=0\r\n", "OK\r\n")) return 3;
printf("3.2 重启WIFI模块\r\n");
if (ESP8266_SendCmd("AT+RST\r\n", "ready")) return 4;
// 设置WIFI模式为STA并保存到flash
printf("4.1 设置WIFI为STA模式\r\n");
if (ESP8266_SendCmd("AT+WMODE=1,1\r\n", "OK\r\n")) return 5;
// 设置WIFI连接
printf("4.2 设置WIFI连信息\r\n");
snprintf(buffer, sizeof(buffer), "AT+WJAP=%s,%s\r\n", wifi_name, wifi_key);
printf("buffer=%s", buffer);
if (ESP8266_SendCmd(buffer, "WIFI_GOT_IP")) {
printf("err=%s\n", usart3_buffer);
return 6;
}
}
}
return 0;
}
c复制u8 MQTT_Config(const char *ip, u16 port, const char *client_id, const char *user_name, const char *password)
{
char buffer[200];
// 断开连接
ESP8266_SendCmd("AT+MQTTDISCONN\r\n", "OK\r\n");
// 1. 设置域名
snprintf(buffer, sizeof(buffer), "AT+MQTT=1,%s\r\n", ip);
if (ESP8266_SendCmd(buffer, "OK\r\n")) return 2;
// 2. 设置端口号
snprintf(buffer, sizeof(buffer), "AT+MQTT=2,%d\r\n", port);
if (ESP8266_SendCmd(buffer, "OK\r\n")) return 3;
// 3. 设置连接方式1为TCP、2为SSL
if (ESP8266_SendCmd("AT+MQTT=3,1\r\n", "OK\r\n")) return 4;
// 4. 设置用户ID
snprintf(buffer, sizeof(buffer), "AT+MQTT=4,%s\r\n", client_id);
printf("设置客户端id:%s,len=%d\n", buffer, strlen(buffer));
if (ESP8266_SendCmd(buffer, "OK\r\n")) return 5;
// 5. 设置用户名
snprintf(buffer, sizeof(buffer), "AT+MQTT=5,%s\r\n", user_name);
if (ESP8266_SendCmd(buffer, "OK\r\n")) return 6;
// 6. 设置密码
snprintf(buffer, sizeof(buffer), "AT+MQTT=6,%s\r\n", password);
if (ESP8266_SendCmd(buffer, "OK\r\n")) return 7;
// 7. MQTT接入
snprintf(buffer, sizeof(buffer), "AT+MQTT\r\n");
if (ESP8266_SendCmd(buffer, "MQTT_CONNECT")) {
printf("rx3=%s\n", usart3_buffer);
return 8;
}
return 0;
}
c复制// 消息订阅
u8 MQTT_SubTopic(const char *topic, int qos, int flag)
{
char buffer[200];
if (flag)
snprintf(buffer, sizeof(buffer), "AT+MQTTSUB=%s,%d\r\n", topic, qos);
else // 取消订阅
snprintf(buffer, sizeof(buffer), "AT+MQTTUNSUB=%s\r\n", topic);
if (ESP8266_SendCmd(buffer, "OK")) return 1;
return 0;
}
// 消息发布
u8 MQTT_PublishMesg(const char *topic, int qos, const char *msg)
{
char buffer[512];
int len = strlen(msg);
snprintf(buffer, sizeof(buffer), "AT+MQTTPUBRAW=%s,%d,0,%d\r\n", topic, qos, len);
if (ESP8266_SendCmd(buffer, ">")) return 1;
if (ESP8266_SendCmd(msg, "OK")) return 2;
return 0;
}
主程序实现了完整的设备工作流程:
c复制int main()
{
// 硬件初始化
LED_Init();
Key_Init();
USARTx_Init(USART1, 115200);
USARTx_Init(USART2, 115200);
USARTx_Init(USART3, 115200);
OLED_Init();
RTC_Init();
// Wi-Fi连接
u8 ret = 1;
while (ret) {
ret = ESP8266_STA_Init(WIFI_NAME, PASSWORD);
printf("ret=%d\n", ret);
if (ret == 0) break;
USARTx_SendStr(USART3, "AT+RST\r\n");
Delay_Ms(2000);
}
// 网络校时
USARTx_SendStr(USART3, (u8 *)"AT+HTTPCLIENTLINE=2,2,,sapi.k780.com,,,?app=life.time&appkey=25273&sign=eae95a712a66e7a97dfd39534e24ffb1&format=json\r\n");
// MQTT连接
ret = 1;
while (ret) {
ret = MQTT_Config(SERVER_IP, SERVER_PORT, ClientID, Username, Password);
printf("连接:ret=%d\n", ret);
if (ret == 0) break;
Delay_Ms(3000);
}
// 消息订阅
ret = MQTT_SubTopic(SET_TOPIC, 0, 1);
printf("消息订阅ret=%d\n", ret);
// 主循环
u32 time = 0;
u32 time2 = 5000;
char mqtt_message[256];
float temp = 26;
int light = 300;
while (1) {
time++;
time2++;
Delay_Ms(1);
// 定时上报数据
if (time2 >= 5000) {
time2 = 0;
temp += 1.5;
light += 5;
sprintf(mqtt_message, "{\"temperature\":%.1f,\"luminosity\":%d}", temp, light);
ret = MQTT_PublishMesg(POST_TOPIC, 0, mqtt_message);
printf("消息发布ret=%d\n", ret);
}
// 处理接收到的消息
if (usart3_flag) {
usart3_buffer[usart3_cnt] = '\0';
printf("rx3=%s\n", usart3_buffer);
if (strstr((char *)usart3_buffer, "\"state\":true")) {
LED1 = 0;
printf("开灯\n");
} else if (strstr((char *)usart3_buffer, "\"state\":false")) {
LED1 = 1;
printf("关灯\n");
}
usart3_cnt = 0;
usart3_flag = 0;
}
// 串口调试
if (usart1_flag) {
usart1_buffer[usart1_cnt] = '\0';
printf("rx1=%s", usart1_buffer);
USARTx_SendStr(USART3, usart1_buffer);
usart1_cnt = 0;
usart1_flag = 0;
}
}
}
在项目开发过程中,我遇到了以下几个典型问题:
Wi-Fi连接不稳定
MQTT连接失败
数据上报延迟
内存优化
功耗优化
代码结构优化
通过这个项目,我成功实现了以下功能:
基于这个基础框架,可以进一步扩展以下功能:
真实传感器接入
设备联动
数据存储与分析
在实际开发过程中,我总结了以下几点经验:
AT指令调试:Wi-Fi模块的AT指令调试需要耐心,建议先使用串口调试工具单独测试模块,确保基本功能正常后再集成到主程序中。
错误处理:网络通信中各种异常情况很常见,完善的错误处理机制非常重要。我在代码中加入了多次重试和状态检查,大大提高了系统的稳定性。
日志记录:良好的日志记录对于调试非常有帮助。我在项目中实现了多级日志输出,可以根据需要调整日志详细程度。
协议理解:深入理解MQTT协议的特性对于物联网开发至关重要。我花了不少时间研究MQTT的QoS级别、保留消息等特性,这对优化系统性能很有帮助。
这个项目让我对物联网设备开发有了更深入的理解,特别是在设备与云平台交互方面积累了不少实战经验。希望我的分享对其他开发者有所帮助,也欢迎交流讨论。