1.首先,你得搭建一个本地服务器,我用的是基于mqtt的,这个搜索一下就可以了,先进入bin目录下输入cmd,注意在bin目录下输入emqx start 可以进行开启,再bin目录下输入emqx stop 可以停止,在任意目录输入 emqx_ctl status可以查询是否开启,同时在任意目录下输入ipconfig可以查询ip地址,注意是最下面的IPv4 地址。
2.后面用mqttx或者mqttfx搭建服务器
点击新建连接
端口写1883,服务器地址复制自己电脑查找到的,但这里要注意的是,如果你想用别人电脑连接不同wife,再通过wife模块操作你的板子,这需要搭建一个云服务器,你也可以直接用官网提供的免费的,把服务器地址改了就行。它这个新建默认端口就是官网提供的那个,名称随便输入一个就行。The Top 1 Free Public MQTT Broker | EMQ (emqx.com)
3.首先esp8266的At固件库是必须有的,一般这个会自带,没有得通过工具去官网下载,我下的是安信可的AT固件汇总 | 安信可科技
后面代码我用的是基于Mqtt固件库的,意思就是说把Mqtt固件库下到板子中去,这样子写的话代码会简单很多,也容易理解很多,当然如果你不想下Mqtt固件库,我推荐你可以看b站画卿师兄的【挽救小白第一季】STM32+8266+小程序智能家居毕设实战这个视频,我觉得很不错的。
4.准备工作差不多做完了,现在可以讲代码了
我用的是stm32zet6板子,然后我将esp8266发送和接受用串口2来进行,我这里不准备用全局变量传数据了,所以中断放esp8266.c里面去了,usart2.h我就不放了,就是对函数的声明,也没定义变量。
usart2.c
#include "stm32f10x.h"
#include "usart2.h"
/*
************************************************************
* 函数名称: Usart2_Init
*
* 函数功能: 串口2初始化
*
* 入口参数: baud:设定的波特率
*
* 返回参数: 无
*
* 说明: TX-PA2 RX-PA3
************************************************************
*/
void Usart2_Init(unsigned int baud)
{
GPIO_InitTypeDef gpioInitStruct;
USART_InitTypeDef usartInitStruct;
NVIC_InitTypeDef nvicInitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
//PA2 TXD
gpioInitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
gpioInitStruct.GPIO_Pin = GPIO_Pin_2;
gpioInitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpioInitStruct);
//PA3 RXD
gpioInitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
gpioInitStruct.GPIO_Pin = GPIO_Pin_3;
gpioInitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpioInitStruct);
usartInitStruct.USART_BaudRate = baud;
usartInitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控
usartInitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //接收和发送
usartInitStruct.USART_Parity = USART_Parity_No; //无校验
usartInitStruct.USART_StopBits = USART_StopBits_1; //1位停止位
usartInitStruct.USART_WordLength = USART_WordLength_8b; //8位数据位
USART_Init(USART2, &usartInitStruct);
USART_Cmd(USART2, ENABLE); //使能串口
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //使能接收中断
nvicInitStruct.NVIC_IRQChannel = USART2_IRQn;
nvicInitStruct.NVIC_IRQChannelCmd = ENABLE;
nvicInitStruct.NVIC_IRQChannelPreemptionPriority = 0;
nvicInitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&nvicInitStruct);
}
/*
************************************************************
* 函数名称: Usart_SendString
*
* 函数功能: 串口数据发送
*
* 入口参数: USARTx:串口组
* str:要发送的数据
* len:数据长度
*
* 返回参数: 无
*
* 说明:
************************************************************
*/
void Usart_SendString(USART_TypeDef *USARTx, unsigned char *str, unsigned short len)
{
unsigned short count = 0;
for(; count < len; count++)
{
USART_SendData(USARTx, *str++); //发送数据
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET); //等待发送完成
}
}
esp8266.c
这个我先把AT+Mqtt指令的作用列举一下吧
AT\r\n 返回ok表示有AT固件
AT+RST\r\n 复位
AT+CWMODE=1\r\n 设置为Station模式
AT+CWJAP=\"1234\",\"abc\"\r\n 连接wifi 1234是wifi名字 abc是密码
AT+MQTTUSERCFG=0,1,\"12\",\"34\",\"56\",0,0,\"\"\r\n 这个说是连接服务器id(12),密码(34),名字(56),但是id必须有一个东西,随便写啥都行,不能空着,其他我没设置,直接空都行。
AT+MQTTCONN=0,\"123\",1883,0\r\n 连接服务器地址和端口 123是地址 1883是端口
AT+MQTTSUB=0,\"hello\",0\r\n 连接服务器话题 hello是话题,不连接话题串口2不能接受到数据
AT+MQTTPUB=0,\"hello\",\"123\",0,0\r\n 向对应话题发送数据,123是发送过去的数据
我这里的USER_GetJsonValue(char *cJson, char *Tag)是对发来的数据进行处理,但是这个发送格式必须是{ "msg":"123456"},中间也不能有空格,它会将:后面的数据提取出来,但我觉得其实你自己可以将收到数据的串口2数组打印出来,比如发个123,后面自己看如何处理这个数据其实更好。delay.c与usart1.c可以去这里找
//单片机头文件
#include "stm32f10x.h"
//网络设备驱动
#include "esp8266.h"
//硬件驱动
#include "delay.h"//这个可以我用的正点原子的delay,要用的话去我上篇那lora会告诉你如何弄
#include "usart2.h"
#include "usart1.h"//这个我也用的正点原子的,也可以去看我上篇文章,这是用于printf函数的,可以打印到电脑的串口
//C库
#include <string.h>
#include <stdio.h>
#define ESP8266_WIFI_INFO "AT+CWJAP=\"TP-LINK_FDDB\",\"fategrandorder\"\r\n"
#define ESP8266_ONENET_INFO "AT+MQTTCONN=0,\"172.16.40.64\",1883,0\r\n"
#define MQTT_CLIENT_ID "clientidc801"//
#define MQTT_USERNAME "admin"//名字
#define MQTT_PASSWORD "public"//密码
unsigned char esp8266_buf[128];
unsigned short esp8266_cnt = 0, esp8266_cntPre = 0;
//==========================================================
// 函数名称: ESP8266_Clear
//
// 函数功能: 清空缓存
//
// 入口参数: 无
//
// 返回参数: 无
//
// 说明:
//==========================================================
void ESP8266_Clear(void)
{
memset(esp8266_buf, 0, sizeof(esp8266_buf));
esp8266_cnt = 0;
}
//==========================================================
// 函数名称: ESP8266_WaitRecive
//
// 函数功能: 等待接收完成
//
// 入口参数: 无
//
// 返回参数: REV_OK-接收完成 REV_WAIT-接收超时未完成
//
// 说明: 循环调用检测是否接收完成
//==========================================================
_Bool ESP8266_WaitRecive(void)
{
if(esp8266_cnt == 0) //如果接收计数为0 则说明没有处于接收数据中,所以直接跳出,结束函数
return REV_WAIT;
if(esp8266_cnt == esp8266_cntPre) //如果上一次的值和这次相同,则说明接收完毕
{
esp8266_cnt = 0; //清0接收计数
return REV_OK; //返回接收完成标志
}
esp8266_cntPre = esp8266_cnt; //置为相同
return REV_WAIT; //返回接收未完成标志
}
//==========================================================
// 函数名称: ESP8266_SendCmd
//
// 函数功能: 发送命令
//
// 入口参数: cmd:命令
// res:需要检查的返回指令
//
// 返回参数: 0-成功 1-失败
//
// 说明:
//==========================================================
_Bool ESP8266_SendCmd(char *cmd, char *res)
{
unsigned char timeOut = 200;
Usart_SendString(USART2, (unsigned char *)cmd, strlen((const char *)cmd));
while(timeOut--)
{
if(ESP8266_WaitRecive() == REV_OK) //如果收到数据
{
if(strstr((const char *)esp8266_buf, res) != NULL) //如果检索到关键词
{
printf("%s\r\n",esp8266_buf);
ESP8266_Clear(); //清空缓存
return 0;
}
}
delay_ms(10);
}
return 1;
}
//==========================================================
// 函数名称: ESP8266_SendData
//
// 函数功能: 发送数据
//
// 入口参数: data:数据
// len:长度
//
// 返回参数: 无
//
// 说明:
//==========================================================
void ESP8266_SendData(char *data)
{
char cmdBuf[100];
sprintf(cmdBuf,"AT+MQTTPUB=0,\"hello\",\"%s\",0,0\r\n",data);
Usart_SendString(USART2, (unsigned char *)cmdBuf, strlen((const char *)cmdBuf));
}
//==========================================================
// 函数名称: ESP8266_GetIPD
//
// 函数功能: 获取平台返回的数据
//
// 入口参数: 等待的时间(乘以10ms)
//
// 返回参数: 平台返回的原始数据
//
// 说明: 不同网络设备返回的格式不同,需要去调试
// 如ESP8266的返回格式为 "+IPD,x:yyy" x代表数据长度,yyy是数据内容
//==========================================================
unsigned char *ESP8266_GetIPD(unsigned short timeOut)
{
char *ptrIPD = NULL;
do
{
if(ESP8266_WaitRecive() == REV_OK) //如果接收完成
{
ptrIPD = strstr((char *)esp8266_buf, "IPD,"); //搜索“IPD”头
if(ptrIPD == NULL) //如果没找到,可能是IPD头的延迟,还是需要等待一会,但不会超过设定的时间
{
//UsartPrintf(USART_DEBUG, "\"IPD\" not found\r\n");
}
else
{
ptrIPD = strchr(ptrIPD, ':'); //找到':'
if(ptrIPD != NULL)
{
ptrIPD++;
return (unsigned char *)(ptrIPD);
}
else
return NULL;
}
}
delay_ms(5); //延时等待
} while(timeOut--);
return NULL; //超时还未找到,返回空指针
}
//==========================================================
// 函数名称: ESP8266_Init
//
// 函数功能: 初始化ESP8266
//
// 入口参数: 无
//
// 返回参数: 无
//
// 说明:
//==========================================================
void ESP8266_Init(void)
{
char str123[100]={0};
GPIO_InitTypeDef GPIO_Initure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//ESP8266复位引脚,这个其实也没啥必要,因为后面我通过AT给他复位了
GPIO_Initure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Initure.GPIO_Pin = GPIO_Pin_14; //GPIOC14-复位
GPIO_Initure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_Initure);
GPIO_WriteBit(GPIOC, GPIO_Pin_14, Bit_RESET);
delay_ms(250);
GPIO_WriteBit(GPIOC, GPIO_Pin_14, Bit_SET);
delay_ms(500);
ESP8266_Clear();
printf("0. AT\r\n");
while(ESP8266_SendCmd("AT\r\n", "OK"))
delay_ms(500);
printf("1.RST\r\n");
ESP8266_SendCmd("AT+RST\r\n","");
delay_ms(500);
printf("2. CWMODE\r\n");
while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"))
delay_ms(500);
printf("3. CWJAP\r\n");
while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "GOT IP"))
delay_ms(500);
printf("4. nsend USERCFG\r\n");
sprintf(str123, "AT+MQTTUSERCFG=0,1,\"%s\",\"%s\",\"%s\",0,0,\"\"\r\n",MQTT_CLIENT_ID, MQTT_USERNAME, MQTT_PASSWORD);
while(ESP8266_SendCmd(str123, "OK"))
delay_ms(500);
printf("5. nsend MQTTCONN\r\n");
while(ESP8266_SendCmd(ESP8266_ONENET_INFO, "OK"))
delay_ms(500);
printf("5. nsend topic\r\n");
while(ESP8266_SendCmd("AT+MQTTSUB=0,\"hello\",0\r\n", "OK"))
delay_ms(500);
printf("6. ESP8266 Init OK\r\n");
}
/*******************************************************************
*函数:char *USER_GetJsonValue(char *cJson, char *Tag)
*功能:json为字符串序列,将json格式中的目标对象Tag对应的值字符串提取出来
*输入:
char *cJson json字符串
char *Tag 要操作的对象标签
*输出:返回数值的字符串形式的启始地址
*特殊说明:用户可以在此基础上改造和扩展该函数,这里只是个简单的DEMO
*******************************************************************/
char temp[100];
uint8_t USER_GetJsonValue(char *cJson, char *Tag)
{
char *target = NULL;
int8_t i=0;
//int flagX = 0;
memset(temp, 0x00, 128);
sprintf(temp,"\"%s\":",Tag);
target=strstr((const char *)cJson, (const char *)temp);
if(target == NULL)
{
//printf("空字符!\r\n");
return 0;
}
i=strlen((const char *)temp);
target=target+i;
memset(temp, 0x00, 128);
if(*target == '"')// 如果是字符型
{
i = 0;
target++;
while(1){
if(*target != '\\' && *(target + 1) == '"'){
temp[i] = *target;
break;
}
temp[i] = *target;
target++;
i++;
if(i >= 99)
break;
}
}
else {
for(i=0; i<10; i++, target++)//数值超过10个位为非法,由于2^32=4294967296
{
if((*target<='9')&&(*target>='0'))
{
temp[i]=*target;
}
else
{
break;
}
}
}
temp[i+1] = '\0';
//printf("数值=%s\r\n",temp);
return 1;
}
//==========================================================
// 函数名称: USART2_IRQHandler
//
// 函数功能: 串口2收发中断
//
// 入口参数: 无
//
// 返回参数: 无
//
// 说明:
//==========================================================
void USART2_IRQHandler(void)
{
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中断
{
if(esp8266_cnt >= sizeof(esp8266_buf)) esp8266_cnt = 0; //防止串口被刷爆
esp8266_buf[esp8266_cnt++] = USART2->DR;
USART_ClearFlag(USART2, USART_FLAG_RXNE);
}
}
esp8266.h
#ifndef _ESP8266_H_
#define _ESP8266_H_
#define REV_OK 0 //接收完成标志
#define REV_WAIT 1 //接收未完成标志
void ESP8266_Init(void);
void ESP8266_Clear(void);
void ESP8266_SendData(char *data);
unsigned char *ESP8266_GetIPD(unsigned short timeOut);
_Bool ESP8266_WaitRecive(void);
uint8_t USER_GetJsonValue(char *cJson, char *Tag);
#endif
main.c
最后说一下main.c,我的串口中断优先级配置分组直接写到主函数里去了。
#include "stm32f10x.h" // Device header
#include <stdio.h>
#include "Delay.h"
#include "string.h"
#include "stdio.h"
#include "Delay.h"//这个函数看我上篇文章
#include "usart1.h"
#include "usart2.h"
#include "esp8266.h"
extern u8 USART_RX_BUF[USART_REC_LEN];
extern unsigned char esp8266_buf[128];
extern unsigned short esp8266_cnt;
extern char temp[100];
int main(){
char msg[100]="20hello";
int time_three=3;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init();
uart_init(115200);
Usart2_Init(115200);
ESP8266_Init();
while(1){
while(time_three--)
{
if(ESP8266_WaitRecive() == REV_OK) //如果收到数据
{
if(USER_GetJsonValue((char*)esp8266_buf,"msg")) //读取出msg后面:后的数据
printf("Send:%s",temp);//显示发送的数据
ESP8266_Clear(); //清空缓存
}
delay_ms(5);
}
time_three=3;
delay_ms(1000);
}
}