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

第三章 Freertos物联网实战esp8266模块

        在本次物联网温湿度检测中,采用esp8266-01s模块作为wifi模块。实现ONENET云平台与STM32单片机的数据通信。看本文对部分内容感到迷惑的地方可以查看整个教程:【Freertos实战】零基础制作基于stm32的物联网温湿度检测(教程非常简易),如有错误之处,望大家批评指正。

一、ESP8266-01S固件烧入

1.引脚定义

        当我们拿到模块的时候首先要看下引脚定义和电压,为我们烧入固件做准备,对于刚到手的wifi模块内部是没有固件的,无法实现联网通信的功能。
        这里到手后,可以按照下面的引脚定义进行接线操作。在烧入固件下,IO0也必须要接地,IO0也必须要接地,IO0也必须要接地。着重强调。建议大家买一个专门的烧录器,这样更方便些

 

引脚名称

描述

GND

GND

IO2

通用IO内部已上拉

IO0

工作模式选择

RXD

串口接收

3V3

电源正极3.3V

RST

复位

EN

使能

TX

串口发送

         GPIO0为高电平正常Flash启动
   GPIO0为低电平代表进入刷固件状态,此时可以经过串口升级内部固件 RST(GPIO16)可做外部硬件复位使用

2.固件烧入

        在接好引脚后,大家可以去这个地址下载固件资料等。资料下载

            这个是我们待会要烧入的固件

        先进入烧写工具目录下,双击我们的烧入工具 

        选择esp8266,选择完成后点击OK 
 

         这几个参数大家也跟我一样

        下载完成后会显示“完成“ ,我们在按照下面的方式重新进行接线。

 二、数据通信

        在完成固件烧入以后,我们就可以开始测试通信,看看能否与云平台进行数据交互。采用的方式是ONENET云平台的MQTT协议。基于新版Onenet搭建云服务(stm32物联网)参考该文章完成云服务搭建。

 1.ESP8266的工作模式

        ESP8266WIFI 模式有两种,一种叫 AP 模式,一种叫 Station 模式,AP 就是我们平时所说的热点,如 WIFI 路由器,开了热点的手机,或者是公共热点等,这些 AP 设备可以允许其他设备(如手机,笔记本电脑等)输入热点名和密码(也可不设置密码)后接入,Station 则是前面说的连接 AP 的设备,如:手机,笔记本电脑等,ESP8266 还有第三种模式:AP+Station,即:将 AP 和 Station 的功能合二为一,但是应用的场景不多,这里不做展示。

2.测试数据流 

        大家可以用下面这段数据流去按顺序逐条发送,同时观察自己的云平台数据变化情况,我这边是没有问题的。

        这里有疑惑的小伙伴一定要去看下这篇文章:基于新版Onenet搭建云服务(stm32物联网)

1.AT //测试esp8266是否正常工作
2.AT+RST //将设备进行复位,类似重启
3.AT+CWMODE=1 //将esp8266的工作模式选择为station
4.AT+CWDHCP=1,1 //开启 Station 模式下的 DHCP 功能(自动获取 IP 地址)
5.AT+CWJAP="CMCC-yr24","4fy@brba" //WIFI名称CMCC-yr24  密码4fy@brba  大家根据自己的WIFI名称和密码进行修改6.AT+MQTTUSERCFG=0,1,"DB01","PtAFXPCG49","version=2018-10-31&res=products%2FPtAFXPCG49%2Fdevices%2FDB01&et=1756260513&method=sha1&sign=TcuMb4Vdl%2FQiGc6AkEceJHmt2pI%3D",0,0,""//设备名称或设备ID:DB01   产品ID:PtAFXPCG49 version为token 这部分也是大家根据自己的情况进行修改整合AT+MQTTCONN=0,"mqtts.heclouds.com",1883,1//连接到ONENT云平台上
AT+MQTTSUB=0,"$sys/PtAFXPCG49/DB01/thing/property/post/reply",0 //MQTT 主题订阅
AT+MQTTSUB=0,"$sys/PtAFXPCG49/DB01/thing/property/set",0 //订阅 “属性设置” 主题//以下就是stm32往云平台上同步数据流,这里用的是温湿度传感器的信息
AT+MQTTPUB=0,"$sys/PtAFXPCG49/DB01/thing/property/post","{\"id\":\"123\"\,\"params\":{\"Temp\":{\"value\":16\}\,\"Humi\":{\"value\":66\}}}",0,0
或
AT+MQTTPUBRAW=0,"$sys/PtAFXPCG49/DB01/thing/property/post",65,0,0
{"id":"123","params":{"Temp":{"value":33},"Humi":{"value":58}}}

        在发送每一条信息后,设备都会返回一段OK的消息。

三、STM32F103C8T6主程序设计

        我们之前都是用串口调试助手的方式去给esp8266发送报文信息,这里开始直接用单片机的串口通信去控制,不了解 串口通信的建议大家去看看这篇文章:串口通信(基于stm32)

        贴上单片机uart1的代码文件

#ifndef __USART_H
#define __USART_H
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "sys.h" 
//////////////////////////////////////////////////////////////////////////////////	 #define USART_REC_LEN  			512  	//接收消息长度
#define EN_USART1_RX 			1		//extern uint8_t USART_RxFlag;	 
extern uint8_t Recv_LED_Flag;
extern char  USART_RX_BUF[USART_REC_LEN]; 
extern int ok_received;
extern int count;
//extern u16 USART_RX_STA;         		void uart_init(u32 bound);
void Serial_SendByte(uint8_t Byte);
int wait_for_ok(char *str,long int wait,char *ack);
int parse_json_command(char *json_str, int *parsed_id, bool *led_status, bool *is_led_command);#endif
#include "sys.h"
#include "usart.h"	 /* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "Task.h"////////////////////////////////////////////////////////////////////////////////// 	 
//如果使用UCOS,则包括下面的头文件即可
#if SYSTEM_SUPPORT_OS
#include "includes.h"					//ucos 使用
#endif//////////////////////////////////////////////////////////////////
//加入以下代码,支持printf函数  
#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                
struct __FILE 
{ int handle; }; FILE __stdout;       
//定义  _sys_exit以避免使用半主机模式
void _sys_exit(int x) 
{ x = x; 
} 
//重定义fputc函数
int fputc(int ch, FILE *f)
{      while((USART1->SR&0X40)==0);//循环发送,直到发送完毕  USART1->DR = (u8) ch;      return ch;
}
#endif #if EN_USART1_RX   //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名奇妙的错误
char USART_RX_BUF[USART_REC_LEN];     //接收缓冲
uint8_t USART_RxFlag;					//接收完成标志位
u8 Temp_Recv[3];
uint8_t Recv_LED_Flag;					//是否接收到需要的LED信息int count = 0;//计数各内容
int ok_received = 0;//判断是否接收到了okvoid uart_init(u32 bound){//GPIO端口设置GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟//USART1_TX   GPIOA.9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9//USART1_RX	  GPIOA.10初始化GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  //Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//抢占优先级0NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		//子优先级1NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能NVIC_Init(&NVIC_InitStructure);	//初始化NVIC寄存器//USART 初始化设置USART_InitStructure.USART_BaudRate = bound;//波特率USART_InitStructure.USART_WordLength = USART_WordLength_8b;//数据位USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式USART_Init(USART1, &USART_InitStructure); //初始化串口1USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接收中断USART_Cmd(USART1, ENABLE);                    //使能串口1USART_RxFlag = 0;Recv_LED_Flag = 0;memset(Temp_Recv, 0, 3);
}void Serial_SendByte(uint8_t Byte)
{USART_SendData(USART1, Byte);while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}/*** 发送AT指令并检查响应* @param command AT指令字符串* @param expected_response 期望的响应字符串* @param timeout_ms 超时时间(毫秒)* @return 成功返回1,失败返回0*/
int wait_for_ok(char *command,long int timeout_ms,char *expected_response)
{int timeout = 0;USART_RxFlag=1;printf("%s",command);while (!ok_received && timeout < timeout_ms){// 检查是否接收到 "OK"if (count >= 2 && strstr(USART_RX_BUF, expected_response) != NULL){ok_received = 1;}vTaskDelay(1);timeout++;}USART_RxFlag = 0;if (ok_received){// 清空缓冲区memset(USART_RX_BUF, 0, USART_REC_LEN);count = 0;ok_received = 0;return 1; // 成功收到 "OK"}else{return 0; // 超时未收到 "OK"}
}/*** 等待并解析JSON报文,带超时判断* @param timeout_ms 超时时间(毫秒)* @param parsed_id 解析出的ID(引用传递)* @param led_status 解析出的LED状态(引用传递)* @param is_led_command 是否为LED控制指令(引用传递)* @return 解析成功返回1,失败或超时返回0*/
int wait_and_parse_json(long int timeout_ms, int *parsed_id, bool *led_status, bool *is_led_command)
{int timeout = 0;bool json_complete = false;// 重置接收缓冲区memset(USART_RX_BUF, 0, USART_REC_LEN);count = 0;// 等待JSON结束标记 "}}" 或超时while (!json_complete && timeout < timeout_ms){// 检查是否接收到 "}}"if (count >= 2 && strstr(USART_RX_BUF, "}}") != NULL){json_complete = true;}vTaskDelay(1);timeout++;}if (json_complete){// 解析JSON报文if (parse_json_command(USART_RX_BUF, parsed_id, led_status, is_led_command)){// 清空缓冲区memset(USART_RX_BUF, 0, USART_REC_LEN);count = 0;return 1; // 解析成功}else{return 0; // 解析失败}}else{// 清空缓冲区memset(USART_RX_BUF, 0, USART_REC_LEN);count = 0;return 0; // 超时}
}/*** 解析JSON报文中的ID和LED状态(C语言指针版本)* @param json_str 接收到的JSON字符串* @param parsed_id 解析出的ID(指针传递)* @param led_status 解析出的LED状态(指针传递)* @param is_led_command 是否为LED控制指令(指针传递)* @return 解析成功返回1,失败返回0*/
int parse_json_command(char *json_str, int *parsed_id, bool *led_status, bool *is_led_command) {char id_str[10] = {0};char led_str[5] = {0};// 1. 提取ID(格式:"id":"3")char *id_pos = strstr(json_str, "\"id\":\"");if (id_pos == NULL) return 0;id_pos += 6; // 跳过"id":"char *id_end = strchr(id_pos, '"');if (id_end == NULL) return 0;strncpy(id_str, id_pos, id_end - id_pos);*parsed_id = atoi(id_str); // 转为整数// 2. 检查是否包含LED字段(格式:"LED":true/false)char *led_pos = strstr(json_str, "\"LED\":");if (led_pos == NULL) {*is_led_command = false;return 1; // 存在ID但无LED字段}*is_led_command = true;// 3. 提取LED状态(true/false)led_pos += 6; // 跳过"LED":char *led_end = strchr(led_pos, ',');if (led_end == NULL) led_end = strchr(led_pos, '}'); // 处理末尾情况if (led_end == NULL) return 0;strncpy(led_str, led_pos, led_end - led_pos);*led_status = (strcmp(led_str, "true") == 0); // 转为布尔值return 1;
}// 当串口1收到数据, 系统自动调用此中断函数
void USART1_IRQHandler(void)                	//串口1接收中断
{u8 Serial_RxData = 0;static int j=0;if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET){Serial_RxData = USART_ReceiveData(USART1);//判断是否是微信小程序回传信息if(Temp_Recv[0] == 's' && Temp_Recv[1] == 'e' && Temp_Recv[2] == 't'){USART_RxFlag = 1;Recv_LED_Flag = 1;memset(Temp_Recv, 0, 3);memset(USART_RX_BUF, 0, USART_REC_LEN);count = 0;}else if(Temp_Recv[0] == 's' && Temp_Recv[1] == 'e'){j=2;Temp_Recv[j] = Serial_RxData;}else if(Temp_Recv[0] == 's'){j=1;Temp_Recv[j] = Serial_RxData;}else{j=0;Temp_Recv[j] = Serial_RxData;}//抓取内部信息if(USART_RxFlag == 1){USART_RX_BUF[count++] = Serial_RxData;if(count == USART_REC_LEN-1){count = 0;memset(USART_RX_BUF, 0, USART_REC_LEN);}}USART_ClearITPendingBit(USART1, USART_IT_RXNE);}
} #endif	

        最后贴上esp8266的代码文件

#ifndef __ESP8266_H_
#define __ESP8266_H_	 
#include "sys.h" 
#include "usart.h"
#include <stdbool.h> void esp8266_Init();
int esp8266_mqtt_reply(int id);
int esp8266_mqtt_send_int(const char *property_name, int value);
int esp8266_mqtt_send_bool(const char *property_name, bool value);#endif
#include "esp8266.h"
#include "delay.h"
/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "Task.h"// 设备和网络配置
#define product_id "PtAFXPCG49"
#define device_id "DB01"
#define device_key "version=2018-10-31&res=products%2FPtAFXPCG49%2Fdevices%2FDB01&et=1756260513&method=sha1&sign=TcuMb4Vdl%2FQiGc6AkEceJHmt2pI%3D"#define wifi_name "CMCC-yr24"
#define wifi_password "4fy@brba"/*** ESP8266初始化函数* @param bound 串口波特率* @return 初始化成功返回1,失败返回0*/
void esp8266_Init()
{// 定义各命令的超时时间(毫秒)const uint32_t AT_TIMEOUT = 1000;     // 普通AT命令超时const uint32_t WIFI_TIMEOUT = 30000; // WiFi连接超时const uint32_t MQTT_TIMEOUT = 10000; // MQTT连接超时// 用于构建AT命令的缓冲区char cmd_buffer[256];// 步骤1:测试AT通信while(!wait_for_ok("AT\r\n", AT_TIMEOUT, "OK"));vTaskDelay(100);// 步骤2:软重启ESP8266while(!wait_for_ok("AT+RST\r\n", AT_TIMEOUT, "ready"));vTaskDelay(1000); // 等待模块完全重启// 步骤3:设置WiFi模式为STAwhile(!wait_for_ok("AT+CWMODE=1\r\n", AT_TIMEOUT, "OK"));vTaskDelay(100);// 步骤4:启用DHCPwhile(!wait_for_ok("AT+CWDHCP=1,1\r\n", AT_TIMEOUT, "OK"));vTaskDelay(100);// 步骤5:连接WiFisnprintf(cmd_buffer, sizeof(cmd_buffer), "AT+CWJAP=\"%s\",\"%s\"\r\n", wifi_name, wifi_password);while(!wait_for_ok(cmd_buffer, WIFI_TIMEOUT, "OK"));vTaskDelay(100);// 步骤6:配置MQTT用户信息snprintf(cmd_buffer, sizeof(cmd_buffer), "AT+MQTTUSERCFG=0,1,\"%s\",\"%s\",\"%s\",0,0,\"\"\r\n", device_id, product_id, device_key);while(!wait_for_ok(cmd_buffer, AT_TIMEOUT, "OK"));vTaskDelay(100);// 步骤7:连接MQTT服务器while(!wait_for_ok("AT+MQTTCONN=0,\"mqtts.heclouds.com\",1883,1\r\n", MQTT_TIMEOUT, "OK"));vTaskDelay(100);// 步骤8:订阅属性回复主题snprintf(cmd_buffer, sizeof(cmd_buffer), "AT+MQTTSUB=0,\"$sys/%s/%s/thing/property/post/reply\",0\r\n", product_id, device_id);while(!wait_for_ok(cmd_buffer, AT_TIMEOUT, "OK"));vTaskDelay(100);// 步骤9:订阅属性设置主题snprintf(cmd_buffer, sizeof(cmd_buffer), "AT+MQTTSUB=0,\"$sys/%s/%s/thing/property/set\",0\r\n", product_id, device_id);while(!wait_for_ok(cmd_buffer, AT_TIMEOUT, "OK"));vTaskDelay(100);while(!esp8266_mqtt_send_bool("LED", false));vTaskDelay(100);}/*** 发送MQTT回复消息到OneNET平台* @param id 平台下发指令的ID值* @return 发送成功返回1,失败返回0*/
// 当接收到平台下发的指令ID为36时,回复成功
//esp8266_mqtt_reply(36);
int esp8266_mqtt_reply(int id)
{// 定义命令缓冲区char cmd_buffer[256];// 定义消息内容缓冲区char msg_buffer[128];// 构建JSON格式的消息内容(修正转义)snprintf(msg_buffer, sizeof(msg_buffer), "{\\\"id\\\":\\\"%d\\\"\\,\\\"code\\\": 200\\,\\\"msg\\\":\\\"success\\\"}", id);// 构建完整的AT+MQTTPUB命令snprintf(cmd_buffer, sizeof(cmd_buffer), "AT+MQTTPUB=0,\"$sys/%s/%s/thing/property/set_reply\",\"%s\",0,0\r\n", product_id, device_id, msg_buffer);// 发送命令并等待响应return wait_for_ok(cmd_buffer, 5000, "OK");
}/*** 发送单个传感器值到OneNET平台* @param property_name 要上报的属性名称,如"Temp"或"Humi"* @param value 属性值* @return 发送成功返回1,失败返回0*/
// 发送温度值16
//esp8266_mqtt_send_int("Temp", 16);// 发送湿度值66
//esp8266_mqtt_send_int("Humi", 66);
int esp8266_mqtt_send_int(const char *property_name, int value)
{// 定义命令缓冲区char cmd_buffer[256];// 定义消息内容缓冲区char msg_buffer[128];// 构建JSON格式的消息内容(修正转义)snprintf(msg_buffer, sizeof(msg_buffer), "{\\\"id\\\":\\\"123\\\"\\,\\\"params\\\":{\\\"%s\\\":{\\\"value\\\":%d}}}", property_name, value);// 构建完整的AT+MQTTPUB命令snprintf(cmd_buffer, sizeof(cmd_buffer), "AT+MQTTPUB=0,\"$sys/%s/%s/thing/property/post\",\"%s\",0,0\r\n", product_id, device_id, msg_buffer);// 发送命令并等待响应(注意:MQTT响应通常是"OK"而非"success")return wait_for_ok(cmd_buffer, 5000, "OK");
}/*** 发送布尔类型的传感器值到OneNET平台* @param property_name 要上报的属性名称,如"LED"* @param value 属性值(true或false)* @return 发送成功返回1,失败返回0*/
// 发送LED开启状态
//esp8266_mqtt_send_bool("LED", true);// 发送LED关闭状态
//esp8266_mqtt_send_bool("LED", false);
int esp8266_mqtt_send_bool(const char *property_name, bool value)
{// 定义命令缓冲区char cmd_buffer[256];// 定义消息内容缓冲区char msg_buffer[128];// 将布尔值转换为JSON格式的字符串const char *bool_str = value ? "true" : "false";// 构建JSON格式的消息内容(修正转义)snprintf(msg_buffer, sizeof(msg_buffer), "{\\\"id\\\":\\\"123\\\"\\,\\\"params\\\":{\\\"%s\\\":{\\\"value\\\":%s}}}", property_name, bool_str);// 构建完整的AT+MQTTPUB命令snprintf(cmd_buffer, sizeof(cmd_buffer), "AT+MQTTPUB=0,\"$sys/%s/%s/thing/property/post\",\"%s\",0,0\r\n", product_id, device_id, msg_buffer);// 发送命令并等待响应(注意:MQTT响应通常是"OK"而非"success")return wait_for_ok(cmd_buffer, 5000, "OK");
}

        创建不易。希望大家能够点赞、收藏、关注。谢谢大家!!!!!!!!

http://www.lryc.cn/news/596026.html

相关文章:

  • LIMO:仅需817样本激活大模型数学推理能力,挑战“数据规模至上”传统范式
  • 从零构建智能对话助手:LangGraph + ReAct 实现具备记忆功能的 AI 智能体
  • MatterPort3D 数据集 | 简介 | 多途径下载
  • 低成本、高泛化能力的无人机自主飞行!VLM-Nav:基于单目视觉与视觉语言模型的无地图无人机导航
  • 基于模拟的流程为灵巧机器人定制训练数据
  • 动漫短剧系统开发全流程解析:从创意到上线的技术实践
  • CSS中的transform
  • 力扣面试150题--寻找峰值
  • Numpy的应用-2
  • 2025年远程桌面软件深度评测:ToDesk、向日葵、TeamViewer全方位对比分析
  • oracle查询数据结构滤涉及的sql语句
  • 开发者的AI认知指南:用大模型重新理解人工智能(下)
  • 疯狂星期四文案网第15天运营日记
  • PCIe Base Specification解析(三)
  • TDengine时序数据库 详解
  • Kotlin介绍
  • Python机器学习:从零基础到项目实战
  • 时序数据库 TDengine × Ontop:三步构建你的时序知识图谱
  • 如何编译RustDesk(Unbuntu 和Android版本)
  • LeetCode 658.找到K个最接近的元素
  • Linux下的EtherCAT主站环境搭建和通信测试
  • ZooKeeper学习专栏(五):Java客户端开发(原生API)详解
  • 小米视觉算法面试30问全景精解
  • Linux--指令初识
  • RxSwift 核心解析
  • 鸿蒙ArkTS多环境API管理与安全签名方案实践
  • 【React-Three-Fiber实践】放弃Shader!用顶点颜色实现高性能3D可视化
  • 学习做精准、自动化、高效的 GEO优化系统
  • 水电站自动化升级:Modbus TCP与DeviceNet的跨协议协同应用
  • 使用Minio后处理图片回显问题