当前位置: 首页 > news >正文

基于MQTT的智能家居系统的学习

1.移植MQTT代码

将已经编写好的MQTT代码移植到自己程序上,首先出现的问题是编译后找不到头文件:

遇到这种情况,可以手动将该头文件的路径添加到项目路径 (Include Paths) 中:

2. 编写ESP8266驱动

2.1 程序分层

* platform_net_socket.c:执行什么AT命令才能连接、收、发网络数据
* ESP8266:提供AT命令函数
* UART驱动抽象层:执行UART的写、读(从buffer读)
* UART硬件驱动(最底层):发送UART数据,接收UART数据存入buffer

2.1.1 UART硬件驱动

这部分对串口进行驱动,通过串口将数据读进某个缓冲区buffer中,这里通过UART3将单片机与ESP8266连接起来,通过UART1将单片机与电脑连接起来。

2.1.2 AT发送命令

参考代码路径:

E:\桌面\韦东山\01_裸机_RTOS项目\02_毕业设计级别项目\01_使用MQTT实现智能家居\source\01_100ASK_STM32F103_Pro\供参考的RTT代码

ESP8266模块接收和发送数据的格式:

接收:

+IPD格式自动报告

工作原理:
  • 模块自动计算:ESP8266自动计算接收到的数据长度

  • 格式+IPD,<length>:<data>

接收示例:
+IPD,15:Hello, ESP8266!  // 收到15字节数据
+IPD,28:HTTP/1.1 200 OK\r\nContent-Type:  // 收到28字节数据

也就是说,当检测到“IPD,<length>:”时,表示后面的内容即为要接收的数据内容,这也是AT命令判断是否有数据要接收的判断方法。

发送:

数据发送状态标识

对于数据发送,ESP8266有专门的响应:

  • SEND OK:数据发送到网络成功

  • SEND FAIL:数据发送失败

  • ERROR:命令格式错误

正确的数据发送成功判断方法

检查SEND OK

at_response_t resp = at_create_resp(128, 0, 5000);// 发送数据准备命令
at_obj_exec_cmd(client, resp, "AT+CIPSEND=%d", data_length);// 等待">"提示符,然后发送实际数据
rt_thread_mdelay(100);
at_client_obj_send(client, data, data_length);// 检查响应
if (strstr(resp->buf, "SEND OK") != NULL) {LOG_I("Data sent successfully to network");
} else if (strstr(resp->buf, "SEND FAIL") != NULL) {LOG_E("Data send failed");
} else if (strstr(resp->buf, "ERROR") != NULL) {LOG_E("Command error");
} else {LOG_W("Unexpected response: %s", resp->buf);
}at_delete_resp(resp);

实用的判断函数

typedef enum {SEND_STATUS_SUCCESS,    // 发送成功SEND_STATUS_FAIL,       // 发送失败SEND_STATUS_ERROR,      // 命令错误SEND_STATUS_TIMEOUT,    // 超时SEND_STATUS_UNKNOWN     // 未知状态
} send_status_t;send_status_t check_send_status(at_response_t resp)
{if (resp == NULL) return SEND_STATUS_UNKNOWN;if (strstr(resp->buf, "SEND OK") != NULL) {return SEND_STATUS_SUCCESS;} else if (strstr(resp->buf, "SEND FAIL") != NULL) {return SEND_STATUS_FAIL;} else if (strstr(resp->buf, "ERROR") != NULL) {return SEND_STATUS_ERROR;} else if (strstr(resp->buf, "CLOSED") != NULL) {return SEND_STATUS_FAIL; // 连接关闭导致失败} else {return SEND_STATUS_UNKNOWN;}
}

核心数据结构

1. AT响应结构 (at_response_t)

struct at_response {char *buf;           // 响应数据缓冲区rt_size_t buf_size;  // 缓冲区大小rt_size_t buf_len;   // 当前数据长度rt_size_t line_num;  // 期望的行数rt_size_t line_counts; // 实际接收的行数rt_int32_t timeout;  // 超时时间
};

2. AT客户端结构 (at_client_t)

struct at_client {rt_device_t device;      // 底层设备(如串口)rt_mutex_t lock;         // 互斥锁rt_sem_t rx_notice;      // 接收信号量rt_sem_t resp_notice;    // 响应信号量at_response_t resp;      // 当前响应对象// ... 其他字段
};

核心功能模块

1. 响应管理

  • at_create_resp(): 创建响应对象

  • at_delete_resp(): 删除响应对象

  • at_resp_set_info(): 设置响应信息

  • at_resp_get_line(): 获取指定行响应

  • at_resp_get_line_by_kw(): 通过关键字获取响应行

2. 命令执行

  • at_obj_exec_cmd(): 执行AT命令并等待响应

  • 支持可变参数格式化命令

  • 支持超时控制和响应状态检查

3. 数据收发

  • at_client_obj_send(): 发送数据

  • at_client_obj_recv(): 接收数据

  • at_client_getchar(): 获取单个字符

4. URC处理 (Unsolicited Result Code)

  • at_obj_set_urc_table(): 设置URC处理表

  • get_urc_obj(): 匹配URC模式

  • 支持多组URC表,动态扩展

5. 解析器线程

  • client_parser(): 主解析循环

  • at_recv_readline(): 读取一行数据

  • 自动区分响应数据和URC数据

工作流程

  1. 初始化at_client_init() 创建客户端,启动解析线程

  2. 发送命令at_obj_exec_cmd() 发送AT命令

  3. 等待响应: 解析线程收集响应数据

  4. 处理完成: 收到结束标志后通知发送线程

  5. URC处理: 异步处理设备主动上报的数据

关键技术特点

1. 线程安全

  • 使用互斥锁保护共享资源

  • 信号量用于线程间同步

2. 内存管理

  • 动态内存分配和释放

  • 缓冲区大小可配置

3. 超时控制

  • 可配置的命令响应超时

  • 连接等待超时

4. 灵活的数据解析

  • 支持按行号获取数据

  • 支持按关键字搜索

  • 支持sscanf格式解析

5. 多客户端支持

  • 支持多个AT设备同时工作

  • 客户端对象表管理

2.1.3 自己编写AT发送命令

自己编写AT发送命令,需要写3个过程:

1.数据发送过程:A线程

2.数据解析过程:解析线程

3.UART3串口的中断设置:UART3_IRQHandle

具体实现:

在UART3_IRQHandle中,当串口接收到数据时,每接收到一个数据字符就会将其存放进循环缓冲区Buffer中,然后产生中断唤醒解析线程,发出任务通知。而在解析线程中,时刻等待任务通知,当UART3_IRQHandle产生中断,发出任务通知后,解析线程就会读取环形缓冲区Buffer中的数据,但是并不是每读取一个字符就分析一次数据,而是要读取字符“\r”和“\n”之后,表示读取到完整的数据之后,再分析数据。

发送AT命令的线程(任务):

发送AT命令:

等待结果:

等待解析线程:

解析线程会一直读取串口数据:

串口3会一直读取环形缓冲区的内容

但是如果环形缓冲区内没有内容,就会陷入休眠状态:

当有数据输入串口时,串口中断(stm32_uart3.c)会释放信号量,唤醒解析线程:

3. 网络分层

3.1网络连接函数

host:连接某个服务器

port:连接某个端口

proto:选择使用TCP协议或者UDP协议传输数据

3.2 断开网络连接

AT+CIPCLOSE为ESP8266模块断开网络连接的命令

3.3 网络发送函数

sprintf() 是一个非常重要且常用的C语言标准库函数。

核心定义

sprintf() 是一个用于将格式化数据写入字符串的函数

4.部分代码分析

在头文件中使用宏声明函数:

在.c文件定义函数:

所以上面代码实现的含义是将结构Client中成员mqtt_port="1883":

左边发布主题,右边订阅主题topic,下面是MQTT服务器

从软件上发送消息

5. 调试过程中遇到的问题

1.内存分配失败

使用pvPortMalloc()分配内存时失败,无法进入后续程序:

解决方法:将堆设置的大一些。

2.超时时间timeout设置太短

使用宏定义设计超时时间为1秒,但是模块连接WiFi时需要一定的时间,当timeout=1秒时,模块还没有连接上WiFi就已经超时,所以返回结果显示WiFi连接失败。

解决方法:将超时时间设置的大一点,可以将timeout=5秒。

6. 该项目实现的流程

将单片机如STM32F103系列通过串口与WiFi模块ESP8266相连,单片机可以通过AT命令向ESP8266发送数据,同时单片机也可以通过编写的解析线程部分,分析由ESP8266发送的数据内容。ESP8266通过路由器与MQTT服务器(称为MQTT Broker)相连接,MQTT Broker可以部署在云服务器如腾讯云、阿里云上,也可以部署在本地电脑上。然后在电脑上或手机上可以安装MQTT软件向MQTT Broke订阅和发送主题,这样就可以通过MQTT Broke将MQTT软件上订阅或发布主题内容通过ESP8266传送到单片机上,从而控制单片机进行下一步操作,采集、控制、逻辑等进行点灯,读取温湿度传感器(DHT11)的数据等操作。

http://www.dtcms.com/a/554973.html

相关文章:

  • 广平企业做网站推广河北软文搜索引擎推广公司
  • 通信的经典知识点和问题(3)
  • 边界扫描测试原理 12 -- BSDL 6 INTEST 描述
  • python图像识别系统 AI多功能图像识别检测系统(11种识别功能)银行卡、植物、动物、通用票据、营业执照、身份证、车牌号、驾驶证、行驶证、车型、Logo✅
  • 数字化转型可以解决传统供应链的哪些问题?
  • 亚马逊海外版网站软文范例500字
  • 长沙找人做企业网站文案南宁网站快速排名提升
  • 慈溪做网站什么价无极网
  • 杭州网站的制作wordpress更换logo
  • 国外网站策划做艺术字的网站
  • 共享空间网站开发公司轻松seo优化排名
  • 【nfs服务搭建题】
  • FOC采样相电流随电流变大而整体发生偏移原因
  • 做网站开发有前途吗wordpress内存慢慢身高
  • seo网站建站做网站做什么类型 比较赚钱
  • 网站型建设模板wordpress 查看文章404
  • 网站优化seo技术佛山网站建设公司排行
  • 做视频网站要什么软件咋样做班级主页网站
  • 旅游推荐网站怎么做广州化妆品网站建设
  • 系统文件I/O
  • 中文编程:特定场景的“桥梁”,而非通用工具的“替代者”
  • 国外活动策划网站网站链接优化怎么做
  • 做动态图网站有哪些线上分销平台
  • 电商网站硬件配置淮安网站seo
  • 贪心-试填法
  • 网站开发需要学什么技能平价网站平价网站建设建设
  • 做淘宝网站用什么浏览器手机搭建网站
  • 案例学习网站建设方案摸摸学校网站优化客户报表
  • 夜莺监控设计思考(五)告警原理和处理流程深度剖析
  • 有没有设计网站在广州的企业vi设计说明