首先推荐中国移动的代码,我觉得中国移动的代码更为合理:(但是有一些其他的模块在里面)
OneNET开发板代码、资料--2020-09-27--标准板、Mini板bug修复 - 开发板专区 - OneNET设备云论坛 (10086.cn)
以及这位b站up做的视频:(wifi模块在p9节)
【挽救小白第一季】STM32+8266+小程序智能家居毕设实战_哔哩哔哩_bilibili
推荐跟着这位up一起c+v。但是没有讲解原理,只追求做出来的可以看看。
---------------------------------------------------------------------------------------------------------------------------------
esp8266是一款wifi无线通讯模块,该模块能够通过wifi来与其他设备通信。我们要做的就是通过串口来发送指令、接收指令,其他的都是由模块自身来处理的。
我所用的是这块新大陆的wifi模块,就是由普通的esp8266集成的,用法没什么区别。串口与stm32单片机的串口对接就行,rx-tx交叉接线。由板子3.3v供电。
这里选择j6端。
启动和下载选着启动,这个下载是在固件库烧录的时候用的。
当然如果你选择其他的wifi模块也是一样的,连接串口电源一样能用,没有什么区别。
其实这个东西很简单,就是一个串口接收发的事。
-------------------------------------------------------------------------------------------------------------------------
那么这个东西该如何使用呢?
我们通过AT指令来操作这个模块。通过串口发送字符串来发送AT操作指令,这个模块会解析指令来实现操作,并且反馈一些信息。(MQTT协议)
下面这个连接是AT指令集:(左边栏目选择wifi指令集,基础指令啥得也都能用)
推荐自己去看看,这些指令我是讲不完的,学会读文档是一个很重要的技能!
Wi-Fi AT 命令集 - ESP32 - — ESP-AT 用户指南 latest 文档 (espressif.cohttps://docs.espressif.com/projects/esp-at/zh_CN/latest/esp32/AT_Command_Set/Wi-Fi_AT_Commands.html
我也来讲解一下一些指令,方便各位理解:
1.AT+CWMODE
AT+CWMODE=<mode>[,<auto_connect>]
-
<mode>:模式
-
0: 无 Wi-Fi 模式,并且关闭 Wi-Fi RF
-
1: Station 模式 \\该模式是连接wifi的模式,作为客机
-
2: SoftAP 模式 \\该模式是将esp8266成为热点的模式,选择2
-
3: SoftAP+Station 模式 \\这个我也没有用过估计是两个都有
-
我们一般选择1模式连接别的热点或wifi进行通信。当你要设置这个模式的时候就得一字符串的形式发给模块,如下
"AT+CIPMODE=1\r\n" !!!注意必须要有\r\n,这是规定,否则指令识别不了
wifi模块自己会读取设别设置模式,收到并且发送的指令正确后会返回“OK”,否则会返回“ERROR”。
2.AT+CWJAP
AT+CWJAP=[<ssid>],[<pwd>][,<bssid>][,<pci_en>][,<reconn_interval>][,<listen_interval>][,<scan_mode>][,<jap_timeout>][,<pmf>]
这个指令是用来连接wifi的,看起来一堆,其实主要的只是前面的ssid和pwd,就是你wifi的名字和密码,如下
"AT+CWJAP=\"wifiname\",\"password\"\r\n"
!!!注意必须要有\r\n,这是规定,否则指令识别不了
在wifiname中输入你的wifi名字,password中输入密码,在输入上一条指令“AT+CIPMODE=1”后你就能连上你的热点或者wifi了。当然这个函数后面还有许多参数,我也没用过,感兴趣的可以去学学。AT指令有很多,建议自己去看一看。
然后下面这些指令就是要有MQTT协议的wifi模块才能用了!如果报错就需要你自己去烧录一下固件库了。当然你不下也可以连接部分服务器,只不过有MQTT协议的你可能就连接不了了。普通的服务器至于要用TCP/IP指令,还有更多指令要自己去看文档!
// 单连接 (AT+CIPMUX=0): AT+CIPSTART=<"type">,<"remote host">,<remote port>[,<keep_alive>][,<"local IP">] // 多连接 (AT+CIPMUX=1): AT+CIPSTART=<link ID>,<"type">,<"remote host">,<remote port>[,<keep_alive>][,<"local IP">]
4.AT+MQTTUSERCFG
AT+MQTTUSERCFG=<LinkID>,<scheme>,<"client_id">,<"username">,<"password">,<cert_key_ID>,<CA_ID>,<"path">
-
<LinkID>:当前仅支持 link ID 0。
-
<scheme>:
-
1: MQTT over TCP;
-
2: MQTT over TLS(不校验证书);
-
3: MQTT over TLS(校验 server 证书);
-
4: MQTT over TLS(提供 client 证书);
-
5: MQTT over TLS(校验 server 证书并且提供 client 证书);
-
6: MQTT over WebSocket(基于 TCP);
-
7: MQTT over WebSocket Secure(基于 TLS,不校验证书);
-
8: MQTT over WebSocket Secure(基于 TLS,校验 server 证书);
-
9: MQTT over WebSocket Secure(基于 TLS,提供 client 证书);
-
10: MQTT over WebSocket Secure(基于 TLS,校验 server 证书并且提供 client 证书)。
-
-
<client_id>:MQTT 客户端 ID,最大长度:256 字节。
-
<username>:用户名,用于登陆 MQTT broker,最大长度:64 字节。
-
<password>:密码,用于登陆 MQTT broker,最大长度:64 字节。
-
<cert_key_ID>:证书 ID,目前 ESP-AT 仅支持一套 cert 证书,参数为 0。
-
<CA_ID>:CA ID,目前 ESP-AT 仅支持一套 CA 证书,参数为 0。
-
<path>:资源路径,最大长度:32 字节
link id填0;scheme填1;client_id随便填,但不能不填;username和password随便填。我用的是公共服务器,没有用户名和密码的要求,如果是专业的服务器就有可能要填正确的用户名和密码。后面三个参数就填0,0和空就行了。如下
="AT+MQTTUSERCFG=0,1,\"sbmc\",\"user\",\"pw\",0,0,\"\"\r\n"
!!!注意必须要有\r\n,这是规定,否则指令识别不了
3.AT+MQTTCONN
AT+MQTTCONN=<LinkID>,<"host">,<port>,<reconnect>
-
<LinkID>:当前仅支持 link ID 0。
-
<host>:MQTT broker 域名,最大长度:128 字节。
-
<port>:MQTT broker 端口,最大端口:65535。
-
<reconnect>:
-
0: MQTT 不自动重连。如果 MQTT 建立连接后又断开,则无法再次使用本命令重新建立连接,您需要先发送 AT+MQTTCLEAN=0 命令清理信息,重新配置参数,再建立新的连接。
-
1: MQTT 自动重连,会消耗较多的内存资源。
-
linkid填0;host填你连接服务器的ip地址;port填连接的端口号。reconnect选择不自动重连。我选择的是使用emqx创建的服务器,不会创建服务器的话我后面可能会写一篇博客来帮助大家创建服务器。
4.AT+MQTTSUB
AT+MQTTSUB=<LinkID>,<"topic">,<qos>
-
<LinkID>:当前仅支持 link ID 0。
-
<topic>:订阅的 topic。
-
<qos>:订阅的 QoS。
linkid为0;topic为订阅的话题,这个你可以把他看成是一个频道,两台设备只有在同一频道内才能接收到互相发的消息。qos这个东西就是确认是否收到消息,1级为我发消息后我要确认你收到了,要等到你发送反馈信息。而0就是我发给你了就是发给你了,不管你收没收到,发了只后我就不管了。我们填0就行了。
"AT+MQTTSUB=0,\"123\",1\r\n"
!!!注意必须要有\r\n,这是规定,否则指令识别不了
5.AT+MQTTPUB
AT+MQTTPUB=<LinkID>,<"topic">,<"data">,<qos>,<retain>
-
<LinkID>:当前仅支持 link ID 0。
-
<topic>:MQTT topic,最大长度:128 字节。
-
<data>:MQTT 字符串消息。
-
<qos>:发布消息的 QoS,参数可选 0、1、或 2,默认值:0。
-
<retain>:发布 retain。
topic为订阅的话题;data为要发送的数据;qos和上面一样。retain填0都行,我并不期待他的反馈。
"AT+MQTTPUB=0,\"123\",\"ON_OK\",1,0\r\n"
!!!注意必须要有\r\n,这是规定,否则指令识别不了
最后就是串口初始化了,我就不讲了。源码搞上!
OLED并没有用上,如果有删掉就行了
main.c
#include "stm32f10x.h" // Device header
#include "usart.h"
#include "usart3.h"
#include "esp8622.h"
#include "Delay.h"
#include "OLED.h"
u8 *instruction = "";
int main(void){
OLED_Init();
usart_init();
usart3_init();
sendcmd("ATE0","",1); //关闭回显
connect_Wifi();
Delay_ms(3000);
connect_Cloud();
while(1){
if(ack_dat("led_on")){
sendcmd("AT+MQTTPUB=0,\"123\",\"ON_OK\",1,0\r\n","",1);//
}
if(ack_dat("led_down")){
sendcmd("AT+MQTTPUB=0,\"123\",\"DOWN_OK\",1,0\r\n","",1);
}
Delay_ms(200);
}
}
usart.c
#include "usart.h"
#if EN_USART1_RX //如果使能了接收
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
u16 USART_RX_STA=0; //接收状态标记
void usart_init(void){
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//tx
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;
NVIC_Init(&NVIC_InitStructure);
USART_InitStructure.USART_BaudRate=115200;
USART_InitStructure.USART_WordLength=USART_WordLength_8b;
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode=USART_Mode_Tx|USART_Mode_Rx;
USART_InitStructure.USART_StopBits=USART_StopBits_1;
USART_InitStructure.USART_Parity=USART_Parity_No;
USART_Init(USART1,&USART_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE,ENABLE);
USART_Cmd(USART1,ENABLE);
}
void usart_send(uint8_t Byte){
USART_SendData(USART1, Byte);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == 0);
}
void USART1_IRQHandler(void){
u8 res;
if(USART_GetFlagStatus(USART1, USART_IT_RXNE)!=0){
res = USART_ReceiveData(USART1);
if((USART_RX_STA&0x8000) == 0){
if(USART_RX_STA&0x4000){
if(res!=0x0a)USART_RX_STA=0;
else USART_RX_STA|=0x8000;
}
else{
if(res==0x0d)USART_RX_STA|=0x4000;
else{
USART_RX_BUF[USART_RX_STA&0x3fff]=res;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;
}
}
}
}
}
#endif
//重定义fputc函数
int fputc(int ch, FILE *f)
{
usart_send(ch);
return ch;
}
usart.h
#ifndef __UASRT_H__
#define __UASRT_H__
#include <stdio.h>
#include "stm32f10x.h"
#define USART_REC_LEN 200 //定义最大接收字节数 200
#define EN_USART1_RX 1
extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u16 USART_RX_STA;
void usart_init(void);
void usart_send(uint8_t Byte);
#endif
usart3.c
#include "usart3.h"
#include "stdarg.h"
#include "string.h"
#include "OLED.h"
u8 USART3_RX_BUF[USART3_MAX_RECV_LEN]; //接收缓冲,最大USART3_MAX_RECV_LEN个字节.
u8 USART3_TX_BUF[USART3_MAX_SEND_LEN]; //发送缓冲,最大USART3_MAX_SEND_LEN字节
vu16 USART3_RX_STA=0;
void usart3_init(void){
NVIC_InitTypeDef NVIC_InitStructure;
USART_InitTypeDef usartst;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能USART3,GPIOA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);//注意usart3再apb1
//USART3_TX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PB.10
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB.10
//USART1_RX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;//PA11
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB.11
USART_DeInit(USART3); //复位串口3
usartst.USART_BaudRate = 115200;
usartst.USART_WordLength = USART_WordLength_8b;
usartst.USART_StopBits = USART_StopBits_1;
usartst.USART_Parity = USART_Parity_No;
usartst.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
usartst.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
USART_Init(USART3,&usartst);
USART_Cmd(USART3,ENABLE);
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启串口接受中断
NVIC_InitStructure.NVIC_IRQChannel=USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;
NVIC_Init(&NVIC_InitStructure);
}
void u3_printf(char* fmt,...)
{
u16 i,j;
va_list ap;
va_start(ap,fmt);
vsprintf((char*)USART3_TX_BUF,fmt,ap);
va_end(ap);
i=strlen((const char*)USART3_TX_BUF); //此次发送数据的长度
for(j=0;j<i;j++) //循环发送数据
{
while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==RESET); //循环发送,直到发送完毕
USART_SendData(USART3,USART3_TX_BUF[j]);
}
}
void USART3_IRQHandler(void){
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) //接收到数据
{
USART_ClearITPendingBit(USART3, USART_IT_RXNE);
USART3_RX_BUF[USART3_RX_STA] = USART_ReceiveData(USART3); //读取接收到的数据
USART3_RX_STA=USART3_RX_STA+1;
if(USART3_RX_STA>USART3_MAX_RECV_LEN)//缓存区溢出
USART3_RX_STA = 0x0000;
}
}
usart3.h
#ifndef __USART3_H__
#define __USART3_H__
#include "stm32f10x.h"
#include <stdio.h>
#define USART3_MAX_RECV_LEN 1024 //最大接收缓存字节数
#define USART3_MAX_SEND_LEN 600 //最大发送缓存字节数;
extern u8 USART3_RX_BUF[USART3_MAX_RECV_LEN]; //接收缓冲,最大USART3_MAX_RECV_LEN字节
extern u8 USART3_TX_BUF[USART3_MAX_SEND_LEN]; //发送缓冲,最大USART3_MAX_SEND_LEN字节
extern vu16 USART3_RX_STA; //接收数据状态
void usart3_init(void);
#endif
esp8266.c
#include "esp8622.h"
//当你已经连上服务器地址后,再次下载需要重新启动esp8266模块,否则连接到服务器的指令会报错
//推荐视频,用的是中国移动的代码
//https://www.bilibili.com/video/BV1ae411W7yD/?spm_id_from=333.999.0.0
//MQTT指令集
//https://docs.espressif.com/projects/esp-at/zh_CN/latest/esp32/AT_Command_Set/MQTT_AT_Commands.html#cmd-mqttsub
//esp固件库烧录
//https://www.bilibili.com/read/cv18483671
//https://www.bilibili.com/video/BV1Mx4y1P72o/?spm_id_from=333.337.search-card.all.click
/* 发送指令
AT+MQTTPUB:发布 MQTT 消息(字符串)?
设置命令?
功能:
通过 topic 发布 MQTT 字符串 消息。如果您发布消息的数据量相对较多,已经超过了单条 AT 指令的长度阈值 256 字节,请使用 AT+MQTTPUBRAW 命令。
命令:
AT+MQTTPUB=<LinkID>,<"topic">,<"data">,<qos>,<retain>
响应:
OK
参数?
<LinkID>:当前仅支持 link ID 0。
<topic>:MQTT topic,最大长度:128 字节。
<data>:MQTT 字符串消息。
<qos>:发布消息的 QoS,参数可选 0、1、或 2,默认值:0。
<retain>:发布 retain。
说明?
每条 AT 命令的总长度不能超过 256 字节。
本命令不能发送数据 \0,若需要发送该数据,请使用 AT+MQTTPUBRAW 命令。
示例?
AT+CWMODE=1
AT+CWJAP="ssid","password"
AT+MQTTUSERCFG=0,1,"ESP32","espressif","1234567890",0,0,""
AT+MQTTCONN=0,"192.168.10.234",1883,0
AT+MQTTPUB=0,"topic","\"{\"timestamp\":\"20201121085253\"}\"",0,0 // 发送此命令时,请注意特殊字符是否需要转义。
*/
//从串口3响应接收,如果接收到了则会返回接收到的数据。
u8 AT_Respose(u8 *string)
{
printf("\r\n接收到模块响应数据为:\n%s",USART3_RX_BUF);
if(strstr((const char*)USART3_RX_BUF,(const char*)string) != 0)
{
printf("\r\n接收到期待的响应 %s",string);
USART3_RX_STA = 0;
memset(USART3_RX_BUF,0x00,USART3_MAX_RECV_LEN);
return 1;
}
else
{
printf("\r\n未接收到期待的响应 %s",string);
USART3_RX_STA = 0;
memset(USART3_RX_BUF,0x00,USART3_MAX_RECV_LEN);
return 0;
}
}
//发送数据,通过串口3发送AT指令,esp8266会自行根据指令来传输数据
u8 sendcmd(u8 *cmd,u8 *ack,u8 times)//cmd:传输的指令 ack:期待收到的响应 times:失败后再次发送的次数
{
u8 i;
for(i=0;i<times;i++)
{
u3_printf("%s\r\n",cmd);//串口三输出
printf("\r\n已发送指令:%s",cmd);//串口一输出,提示程序的进程
Delay_ms(1000);//等待响应
if(AT_Respose(ack))
return 1;
printf("\r\n发送指令响应错误,1.5s后重新发送指令:%s",cmd);
Delay_ms(1500);
}
return 0;
}
u8 sendcmd1(u8 *cmd,u8 *ack,u8 times)//发送数据特殊版,一般不用
{
u8 i;
for(i=0;i<times;i++)
{
u3_printf("%s\r\n",cmd);
printf("\r\n已发送指令:%s",cmd);
Delay_ms(7000);
if(AT_Respose(ack))
return 1;
printf("\r\n发送指令响应错误,1.5s后重新发送指令:%s",cmd);
Delay_ms(1500);
}
return 0;
}
//连接wifi AT+XXX 的格式是固定的
u8 connect_Wifi(void)
{
u8 *instruction="AT";
if(sendcmd(instruction,"OK",3))//检测wifi模块是否正常
{
Delay_ms(1000);
instruction="AT+CIPMODE=1";
if(sendcmd(instruction,"OK",3))//设置为sta模式
{
Delay_ms(1000);
instruction="AT+CWJAP=\"Lakzhu\",\"1588662051\"";
if(sendcmd1(instruction,"OK",5))//连接wifi热点
{
return 1;
}
}
}
return 0;
}
//连接云平台服务器
u8 connect_Cloud(void)
{
u8 *instruction = "";
instruction="AT+MQTTUSERCFG=0,1,\"sbmc\",\"user\",\"pw\",0,0,\"\""; //发送连接后的自己的id,用户名,密码 id不能为空
if(sendcmd(instruction,"OK",3)){
instruction="AT+MQTTCONN=0,\"192.168.109.71\",1883,0"; //连接服务器的地址、通道
if(sendcmd(instruction,"OK",3)){
instruction="AT+MQTTSUB=0,\"123\",1\r\n"; //订阅话题
if(sendcmd(instruction,"OK",3)){
return 1;
}
}
}
return 0;
}
int ack_dat(u8* string){
if(strstr((const char*)USART3_RX_BUF,(const char*)string) != 0)
{
printf("\r\n接收到期待的响应 %s",string);
USART3_RX_STA = 0;
memset(USART3_RX_BUF,0x00,USART3_MAX_RECV_LEN);
return 1;
}
return 0;
}
esp8266.h
#ifndef __ESP8622_H__
#define __ESP8622_H__
#include "Delay.h"
#include "usart.h"
#include "usart3.h"
#include "stm32f10x.h"
#include "string.h"
//WiFi连接
//云平台连接
//数据上传
//命令上传
u8 sendcmd(u8 *cmd,u8 *ack,u8 times);
u8 connect_Wifi(void);
u8 connect_Cloud(void);
int ack_dat(u8* string);
#endif
Delay.c
#include "Delay.h"
/**
* @brief 微秒级延时
* @param xus 延时时长,范围:0~233015
* @retval 无
*/
void Delay_us(uint32_t xus)
{
SysTick->LOAD = 72 * xus; //设置定时器重装值
SysTick->VAL = 0x00; //清空当前计数值
SysTick->CTRL = 0x00000005; //设置时钟源为HCLK,启动定时器
while(!(SysTick->CTRL & 0x00010000)); //等待计数到0
SysTick->CTRL = 0x00000004; //关闭定时器
}
/**
* @brief 毫秒级延时
* @param xms 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_ms(uint32_t xms)
{
while(xms--)
{
Delay_us(1000);
}
}
/**
* @brief 秒级延时
* @param xs 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_s(uint32_t xs)
{
while(xs--)
{
Delay_ms(1000);
}
}
Delay.h文章来源:https://www.toymoban.com/news/detail-718642.html
#ifndef __DELAY_H
#define __DELAY_H
#include "stm32f10x.h"
void Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
void Delay_s(uint32_t s);
#endif
分享就到这里。感谢观看。文章来源地址https://www.toymoban.com/news/detail-718642.html
到了这里,关于esp8266模块--MQTT协议连接服务器实现数据接收和发送+源码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!