ESP8266学习
一,连接Wifi
1.Esp8266连接手机热点
AT
AT+RST
AT+CWMODE=1
AT+CWJAP="ESP8266","123456789"
手机查看连接信息
2.Esp8266连接手机热点进入透传模式
AT
AT+RST
AT+CWMODE=1
AT+CWJAP="ESP8266","123456789"
AT+CIPMUX=0
AT+CIPSTART="TCP","192.168.10.7",8080
AT+CIPMODE=1 //透传模式
AT+CIPSEND
AT+RESTORE
需要先发送 “+++” 来退出透传模式,之后才能进行 AT 指令的交互。
不加回车换行
AT+SAVETRANSLINK=1,"192.168.10.7",8080,"TCP",100 保存,上电后自动配置
AT+SAVETRANSLINK=1,"服务器IP",端口,"TCP" // 保存透传配置(1 表示使能上电自动连接和自动进入透传模式。即模块在上电之后,会按照后续设定的参数自动连接目标服务器,并自动进入透传模式;如果设置为 0,则不会自动进行连接和进入透传,需要手动发送相关指令来操作。)100 表示模块上电后自动连接服务器时的超时时间
解释:
- AT:测试模块是否正常响应,正常会返回 "OK"
- AT+RST:重启模块,恢复初始状态
- AT+CWMODE=1:STA(站点)模式,模块作为客户端连接外部 WiFi(如路由器)AT+CWMODE=2:AP(接入点)模式,模块自身创建 WiFi 热点供其他设备连接AT+CWMODE=3:混合模式,同时具备 STA 和 AP 功能(既能连外部 WiFi,又能自建热点)
- AT+CWJAP="ESP8266","123456789":连接名为 "ESP8266"、密码 "123456789" 的 WiFi
- AT+CIPSTART="TCP","192.168.10.7",8080:建立 TCP 连接到 IP 为 192.168.10.7、端口 8080 的服务器
- AT+CIPMUX=0:设置为单连接模式(同一时间只保持一个连接)
- AT+CIPMODE=1:开启透传模式(串口数据直接通过 TCP 发送,无需额外指令)
- AT+CIPSEND:进入透传发送状态(此时可直接发送数据)
- AT+RESTORE:恢复模块出厂设置(清除所有配置)
二,两个ESP8266通信
1.一个配置服务器
- AT+RST:用于复位 ESP8266 模块。
- AT+CWMODE=2:设置模块的工作模式为 AP(接入点)模式,此时模块可作为热点,让其他设备连接。
- AT+CWSAP="ESP8266","12345678",5,3:配置 AP 模式下的参数,设置热点名称为 “ESP8266”,密码为 “12345678”,信道为 5,加密方式为 WPA PSK。
- AT+CIPMUX=1:将模块设置为多连接模式,支持同时与多个客户端建立连接。
- AT+CIPSERVER=1,8080:建立服务器,端口为 8080,这样模块可监听来自客户端的连接请求。
- AT+CIFSR:用于查询模块的 IP 地址等网络相关信息。
AT+CIPSEND=<连接ID>,<数据长度>
2.另一个单片机作为客户端
- AT
- AT+RST
- AT+CWMODE=1
- AT+CWJAP="ESP8266","123456789"
- AT+CIPMUX=0
- AT+CIPSTART="TCP","192.168.10.7",8080
- AT+CIPMODE=1 //透传模式
- AT+CIPSEND
- AT+RESTORE
需要先发送 “+++” 来退出透传模式,之后才能进行 AT 指令的交互。
这样两个ESP8266就可以相互通信
程序:
服务器
一个程序是创建服务器,检测信息,是否连接,和获取的数据信息
这个程序,功能不完善,能简单获取设备号,和显示客户端发来的消息
#include "stm32f10x.h"
#include "OLED.h"
#include "Delay.h"
#include "usart.h"
#include "string.h"
#include "EXTI_KEY.h"extern const unsigned char BMP1[];
//extern const unsigned char BMP2[];#define BUFFER_SIZE 100 //缓冲区大小
char receiveBuffer[BUFFER_SIZE]; // 缓冲区
//uint16_t dht11[BUFFER_SIZE]; //
uint8_t receiveIndex = 0,copyreceive=0; // 接收索引
uint16_t receivedChar=0;char server_ok=0,show=0;
char i=0,kehu=0,clear=0;void ESP8266_SendAT(char *p,char*q)
{USART_SendString(USART1,p);while((receiveBuffer[copyreceive-3]!=*q)&&(receiveBuffer[copyreceive-2]!=*(q+1)));memset(receiveBuffer,32,BUFFER_SIZE);
}void ESP8266_Init()
{ESP8266_SendAT("AT\r\n", "OK"); // 测试模块ESP8266_SendAT("AT+CWMODE=2\r\n", "OK"); // 设为AP模式(手机/设备可连)ESP8266_SendAT("AT+CWSAP=\"ESP8266\",\"12345678\",5,3\r\n", "OK"); // AP名:ESP8266,密码:12345678ESP8266_SendAT("AT+CIPMUX=1\r\n", "OK"); // 多连接模式ESP8266_SendAT("AT+CIPSERVER=1,8080\r\n", "OK"); // 启动服务器,端口8080
}int main()
{char *customer;static uint8_t last_state = 0xFF; //记录上次状态// 硬件初始化EXTI_PE_Configuration(); // 中断按键配置OLED_Init(); // OLED初始化USART1_Config(); // 串口1配置(连ESP8266)ESP8266_Init(); // 初始化ESP8266服务器OLED_Clear();OLED_ShowString(1, 1, "Server Ready"); // 显示服务器就绪while(1){//模式1 显示客户端连接状态if(show == 1){OLED_ShowString(1,1,"Wait Connect");customer=receiveBuffer+2;if(strncmp(customer,"CONNECT",7)==0){uint8_t current_customer = *(customer-2) - '0';if(last_state!=current_customer){OLED_Clear();OLED_ShowString(1, 1, "Connected");switch(current_customer) {case 0:OLED_ShowString(2,1,"one customer");break;case 1:OLED_ShowString(2,1,"two customer");break;case 2:OLED_ShowString(2,1,"three customer");break;case 3:OLED_ShowString(2,1,"four customer");break;default:OLED_ShowString(2,1,"unknown");break;}OLED_ShowString(3,1,"status: on");last_state=current_customer;}show = 0;}}//显示客户端发送的数据if(show == 2){OLED_Clear();OLED_ShowString(1,1,"Wait Data");customer=receiveBuffer;if(strncmp(customer,"+IPD,",5)==0){OLED_ShowString(2,1,"Data:");OLED_ShowString(2,6,receiveBuffer+9); //+IPD,0,4:}show=0;} }
}void USART1_IRQHandler(void)
{if (USART_GetITStatus(USART1, USART_IT_RXNE)!= RESET) {receivedChar = USART_ReceiveData(USART1);if (receivedChar!= '\n' && receiveIndex < BUFFER_SIZE - 1) {receiveBuffer[receiveIndex++] = receivedChar;} else {receiveBuffer[receiveIndex] = '\0';copyreceive=receiveIndex;receiveIndex = 0; }show =kehu;}
}
void EXTI0_IRQHandler(void)
{ static int mode=0;if(EXTI_GetITStatus(EXTI_Line0) != RESET) {mode = (mode >= 2) ? 1 : mode + 1; // 模式1/2切换// 发1字节数据到服务器连接(ID=0)USART_SendString(USART1, "AT+CIPSEND=0,1\r\n");Delay_ms(100);USART_SendData(USART1, mode); // 发模式值(1/2)Delay_ms(100);kehu = mode; // 标记当前模式} EXTI_ClearITPendingBit(EXTI_Line0);
}
客户端
连接服务器,检测服务器发来的消息,为’1‘的话,发送数据
#include "stm32f10x.h"
#include "OLED.h"
#include "Delay.h"
#include "usart.h"
#include "string.h"
#include "dht11.h"extern const unsigned char BMP1[];
//extern const unsigned char BMP2[];
u8 temp;
u8 humi;
u8 tempp;
u8 humip;
#define BUFFER_SIZE 100
char receiveBuffer[BUFFER_SIZE];
uint8_t receiveIndex = 0,copyreceive=0; uint16_t instruction=0;char i=0,ready=0,show=0;
void ESP8266_AT(char *p,char*q)
{USART_SendString(USART1,p);while((receiveBuffer[copyreceive-3]!=*q)&&(receiveBuffer[copyreceive-2]!=*(q+1)));memset(receiveBuffer,32,BUFFER_SIZE);
}
void ESP8266_Init(void)
{USART_SendString(USART1, "+++"); // 发送退出指令Delay_ms(500);USART_SendString(USART1, "+++");Delay_ms(500);USART_SendString(USART1, "+++");OLED_ShowString(1,1,"ESP_Init");Delay_ms(500);ESP8266_AT("AT\r\n","OK");ESP8266_AT("AT+CWMODE=1\r\n","OK");ESP8266_AT("AT+CWJAP=\"ESP8266\",\"12345678\"\r\n","OK");ESP8266_AT("AT+CIPMUX=0\r\n","OK");ESP8266_AT("AT+CIPMODE=1\r\n","OK");ESP8266_AT("AT+CIPSTART=\"TCP\",\"192.168.4.1\",8080\r\n","OK");ESP8266_AT("AT+CIPSEND\r\n","OK");OLED_ShowString(4,1,"ESP_Init ok");Delay_s(1);
}int main()
{char tx[10];OLED_Init();USART1_Config();DHT11_Init();ESP8266_Init();ready=1;OLED_Clear();OLED_ShowString(1,1,"wait server");OLED_ShowString(2,1,"temp:");OLED_ShowString(3,1,"humi:");while(1){DHT11_Read_Data(&temp,&tempp,&humi,&humip);OLED_ShowNum(2,6,temp,2);OLED_ShowNum(3,6,humi,2);OLED_ShowString(2,8,".");OLED_ShowString(3,8,".");OLED_ShowNum(2,9,tempp,1);OLED_ShowNum(3,9,humip,1);if(show==1){OLED_ShowNum(4,1,instruction,2);show=0;}if(instruction==49){tx[0]='T';tx[1]=(temp/10)+'0';tx[2]=(temp%10)+'0';tx[3]='.';tx[4]=(tempp%10)+'0';tx[5]='\0';USART_SendString(USART1,tx);Delay_ms(500);tx[0] = 'H'; tx[1] = (humi / 10) + '0'; tx[2] = (humi % 10) + '0'; tx[3] = '.'; tx[4] = (humip % 10) + '0'; tx[5] = '\0'; USART_SendString(USART1, tx);Delay_ms(500); // 保持与温度发送相同的间隔}}}void USART1_IRQHandler(void)
{if (USART_GetITStatus(USART1, USART_IT_RXNE)!= RESET) {char receivedChar = USART_ReceiveData(USART1);if(ready==0){if (receivedChar!= '\n' && receiveIndex < BUFFER_SIZE - 1) {receiveBuffer[receiveIndex++] = receivedChar;} else {receiveBuffer[receiveIndex] = '\0'; copyreceive=receiveIndex;receiveIndex = 0; }}if(ready==1){instruction=receivedChar;show=1;}}
}
三:MQTT连接
1.通信
无线通信 通信协议
WIFI MQTT
NBIOT coap
GPRS HTTP
Lora
MQTT要保证实时连接
2.传输层协议
TCP和UDP的对比
对比维度 | TCP | UDP |
连接方式 | 面向连接(三次握手) | 无连接 |
传输可靠性 | 可靠(不丢、有序) | 不可靠(可能丢、乱序) |
速度 | 较慢(多步骤确认) | 快(无额外步骤) |
资源开销 | 高 | 低 |
适用场景 | 需可靠(下载、聊天) | 需快速(直播、游戏) |
3.涉及的C 语言知识
Strlen函数和Sizeof函数:
相同点:字符数组的长度
不同点:是否包含结束标志’\0’
Strlen 不包含结束标志‘\0‘
Sizeof 包含结束标志’\0’
Memcpy
void *memcpy(void *str1,const void *str2,size_t n)
从存储区str2复制n个字节到存储区str1中
将一个内存数据复制到另一块中,即用于复制源空间的数据到目的空间
Memset
void *memset(void *str, int c, size_t n)
将已开辟内存空间 str 的首 n 个字节的值设置为指定的值 c
常用于初始化某个内存空间
Sprintf 头文件 stdio.h
Sprint函数数据打印到数组容器中
4.onenet平台
围绕设备→平台(上报事件) ” 和 “平台→设备(下发指令)
设备数据上报平台
设备事件上报请求($sys/{pid}/{device-name}/thing/event/post 、发布权限)
- 谁用:设备(比如传感器、智能终端 )
- 干啥用:设备主动把自身的 “事件” 发给平台。
举个例子:- 温湿度传感器检测到 “温度超限”,就通过向这个主题 发布(Publish) 消息,把 {"event":"temp_over","data":{"temp":35}}(意思是 “温度超限事件,当前温度 35℃” )这类数据上报给平台,让平台知道 “设备那边出状况了”。
- 简单说就是 设备主动 “喊” 平台:我这有情况,你看看。
设备事件上报响应($sys/{pid}/{device-name}/thing/event/post/reply 、订阅权限)
- 谁用:还是设备(因为要接收平台回复 )
- 干啥用:设备订阅这个主题,等平台回应。
接着上面的例子:- 平台收到传感器 “温度超限” 事件后,可能回复 {"code":200,"msg":"已收到,将触发降温策略"}(意思是 “收到事件,会处理” )。
- 设备因为订阅了这个主题,就能收到平台的 响应(Reply),知道 “平台接收到事件,下一步要干啥” 。
- 简单说就是 设备等平台 “回话”,确认消息有没有处理、下一步咋做。
设备接收平台下发命令
1. 设备属性设置请求($sys/{pid}/{device-name}/thing/property/set 、订阅权限 )
核心作用:平台→设备 “发指令” 的通道,让平台能远程要求设备修改自身属性(比如开关 LED、调整传感器采集频率 )。
- 谁用:设备端(如单片机 + ESP8266 )需要 订阅(Subscribe) 这个主题,才能收到平台的指令。
- 场景举例:
平台想控制设备 “打开 LED”,就会往 $sys/{pid}/{device-name}/thing/property/set 主题发一条消息,内容可能是:
- 设备因为订阅了这个主题,就能收到这条消息,知道 “平台要我改 LED 状态为开” 。
- 通俗理解:平台通过这个主题 “远程喊话设备”:“你把某个属性改成 XX ,赶紧执行!”
2. 设备属性设置响应($sys/{pid}/{device-name}/thing/property/set_reply 、发布权限 )
核心作用:设备→平台 “回反馈” 的通道,让设备能告诉平台 “指令执行结果咋样了”(成功 / 失败、具体状态 )。
谁用:设备端执行完平台指令后,需要 发布(Publish) 消息到这个主题,回复平台。
场景举例:
设备收到 “打开 LED” 指令后,成功点亮了 LED ,就往 $sys/{pid}/{device-name}/thing/property/set_reply 发消息:
- 平台收到后,就知道 “设备收到指令了,而且执行成功,LED 确实开了” 。
- 通俗理解:设备通过这个主题 “回喊平台”:“你让我改的属性,我改完了!结果是 XX ,你看看对不对~”
设备数据上报平台
设备事件上报请求($sys/{pid}/{device-name}/thing/event/post 、发布权限)
- 谁用:设备(比如传感器、智能终端 )
- 干啥用:设备主动把自身的 “事件” 发给平台。
举个例子:- 温湿度传感器检测到 “温度超限”,就通过向这个主题 发布(Publish) 消息,把 {"event":"temp_over","data":{"temp":35}}(意思是 “温度超限事件,当前温度 35℃” )这类数据上报给平台,让平台知道 “设备那边出状况了”。
- 简单说就是 设备主动 “喊” 平台:我这有情况,你看看。
设备事件上报响应($sys/{pid}/{device-name}/thing/event/post/reply 、订阅权限)
- 谁用:还是设备(因为要接收平台回复 )
- 干啥用:设备订阅这个主题,等平台回应。
接着上面的例子:- 平台收到传感器 “温度超限” 事件后,可能回复 {"code":200,"msg":"已收到,将触发降温策略"}(意思是 “收到事件,会处理” )。
- 设备因为订阅了这个主题,就能收到平台的 响应(Reply),知道 “平台接收到事件,下一步要干啥” 。
- 简单说就是 设备等平台 “回话”,确认消息有没有处理、下一步咋做。
设备接收平台下发命令
1. 设备属性设置请求($sys/{pid}/{device-name}/thing/property/set 、订阅权限 )
核心作用:平台→设备 “发指令” 的通道,让平台能远程要求设备修改自身属性(比如开关 LED、调整传感器采集频率 )。
- 谁用:设备端(如单片机 + ESP8266 )需要 订阅(Subscribe) 这个主题,才能收到平台的指令。
- 场景举例:
平台想控制设备 “打开 LED”,就会往 $sys/{pid}/{device-name}/thing/property/set 主题发一条消息,内容可能是:
- 设备因为订阅了这个主题,就能收到这条消息,知道 “平台要我改 LED 状态为开” 。
- 通俗理解:平台通过这个主题 “远程喊话设备”:“你把某个属性改成 XX ,赶紧执行!”
2. 设备属性设置响应($sys/{pid}/{device-name}/thing/property/set_reply 、发布权限 )
核心作用:设备→平台 “回反馈” 的通道,让设备能告诉平台 “指令执行结果咋样了”(成功 / 失败、具体状态 )。
谁用:设备端执行完平台指令后,需要 发布(Publish) 消息到这个主题,回复平台。
场景举例:
设备收到 “打开 LED” 指令后,成功点亮了 LED ,就往 $sys/{pid}/{device-name}/thing/property/set_reply 发消息:
- 平台收到后,就知道 “设备收到指令了,而且执行成功,LED 确实开了” 。
- 通俗理解:设备通过这个主题 “回喊平台”:“你让我改的属性,我改完了!结果是 XX ,你看看对不对~”