目录
需求
完成WiFi模块的配置,使其能连接服务器并最终能和服务器相互发送消息。
一、wifi模块简述
本项目开发版上没有封装好的WIFI模块,所以借助外部wifi模块。
ESP8266:乐鑫公司推出的一款无线SOC。可以直接使用ESP_IDF、Arduino直接编程
ESP_12F:安信可公司在ESP8266芯片的基础上,封装出的一个模块
模块原理图:
模块引脚配置:
通信接口配置:
二、配置流程
1.配置通信串口
配置串口3(本次使用的WiFi模块串口接的是串口3)
默认配置: 波特率115200 8位数据位 0位校验位 1位停止位
配置PB10(TX) PB11(RX)
TX:复用推挽 RX:浮空输入
配置PE6(ESP模块的使能引脚)
高电平使能
代码编写:
bash
/*****************************************************************
*函 数 名 称:USART3_Config
*函 数 功 能:初始化串口3
*函 数 形 参:无
*函 数 返 回:无
*******************************************************************/
void USART3_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct={0};
USART_InitTypeDef USART_InitStruct={0};
NVIC_InitTypeDef NVIC_InitStruct={0};
//1,开时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);
//2,配置IO口
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_11;
GPIO_Init(GPIOB,&GPIO_InitStruct);
//3,配置串口
USART_InitStruct.USART_BaudRate=115200;
USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
USART_InitStruct.USART_Parity=USART_Parity_No;
USART_InitStruct.USART_StopBits=USART_StopBits_1;
USART_InitStruct.USART_WordLength=USART_WordLength_8b;
USART_Init(USART3,&USART_InitStruct);
//4,使能串口
USART_Cmd(USART3,ENABLE);
//5,开中断
USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);
USART_ITConfig(USART3,USART_IT_IDLE,ENABLE);
//6,配置中断
NVIC_InitStruct.NVIC_IRQChannel=USART3_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=0;//抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority=0;//响应优先级
NVIC_Init(&NVIC_InitStruct);
}
/*****************************************************************
*函 数 名 称:WIFI_Config
*函 数 功 能:初始化WIFI
*函 数 形 参:无
*函 数 返 回:无
*******************************************************************/
void WIFI_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct={0};
//配置通信串口
USART3_Config();
//配置使能引脚
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
//2,配置IO口
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOE,&GPIO_InitStruct);
GPIO_SetBits(GPIOE,GPIO_Pin_6);
Delay_nms(500);//延时让WIFI输出固件信息
}
2.配置引脚与中断接受
在中断中进行信息的接受
bash
/*****************************************************************
*函 数 名 称:USART3_IRQHandler
*函 数 功 能:初始化串口3
*函 数 形 参:无
*函 数 返 回:无
*******************************************************************/
void USART3_IRQHandler(void)
{
uint8_t data=0;
if(USART_GetITStatus(USART3,USART_IT_RXNE) == SET)
{
data = USART3->DR;
rxdata.rxbuff[rxdata.rxcnt++] = data;
rxdata.rxcnt %= MAX;//确保不越界
USART1->DR = data;//通过串口1将数据发给电脑串口助手
}
if(USART_GetITStatus(USART3,USART_IT_IDLE) == SET)
{
rxdata.rxflag = 1;
data = USART3->SR;
data = USART3->DR;
}
}
3.简述AT指令
AT 命令(AT Commands)最早是由发明拨号调制解调器(MODEM)的贺氏公司(Hayes)为了控制 MODEM 而发明的控制协议。后来随着网络带宽的升级,速度很低的拨号 MODEM 基本退出一般使用市场,但是 AT 命令保留下来。
在嵌入式开发中,经常是使用AT命令去控制各种通讯模块,比如ESP8266 WIFI模块、4G模块、GPRS模块等等。一般就是主芯片通过硬件接口(比如串口、SPI)发送AT命令给通讯模块,模块接收到数据之后回应响应的数据。
AT指令的分类:
AT指令的使用:
要注意:基本所有AT指令,结尾必须换行(\r\n),除"+++"以外
ESP12F模块的工作模式:STA(连接热点) AP(释放热点) STA+AP
STA模式:模组作为节点去连接热点,然后就可连接某个服务器。
AP模式:模组作为热点,释放网络,可以在模组上创建服务器,其他设备连接他。
该模块的指令:
AT :测试固件的
AT+RST :重启ESP8266
ATE0 :关闭回显
ATE1 :打开回显
AT+CWMODE_DEF(_DEF有些固件支持,有些不支持)=x
x=1为设置工作模式 STA模式(可以连接其他设备热点),2为AP模式,3为组合模式。
AT+CWJAP_DEF="WIFI名","WIFI密码" 连接WIFI。
AT+CWSAP :配置 ESP8266 SoftAP 参数(配置释放的热点) 。
AT+CIPAP :设置 ESP8266 SoftAP 的 IP 地址。
多连接情况下 (AT+CIPMUX=1),才能开启 TCP 服务器。
AT+CIPSERVER :建⽴ TCP 服务器
AT+CIPSTART="TCP","IP",端口号 以TCP的形式连接服务器
AT+CIPMODE=1 : 开启透传 (向wifi发送的所有消息(除+++外)都认为不是指令)
AT+CIPSEND : 启动发送功能
+++ 没有回车 : 退出透传
AT+CIPCLOSE : 退出服务器连接
以上的指令都是比较常用的,所以应当熟记。
连接服务器需要那些步骤:
1.连接网络
2.设置位连接热点模式:STA
3.连接热点:名字和密码
4.连接服务器 ip 和 端口
5.收发数据
AT是AT指令还是收发的数据
透传模式(透明传输,所有消息都认为是普通收发的消息)
4.程序编写
给串口发送AT指令,相当于发送字符串。
本项目相当于WIFI模块经过串口三发给串口一,经过串口一发送给串口助手。
发送单个字符的代码:
bash
/*****************************************************************
*函 数 名 称:USART3_SendData
*函 数 功 能:串口3发送单个字节
*函 数 形 参:data :发送的内容
*函 数 返 回:无
*******************************************************************/
void USART3_SendData(uint8_t data)
{
while(USART_GetFlagStatus(USART3,USART_FLAG_TXE) == RESET);
USART3->DR = data;
while(USART_GetFlagStatus(USART3,USART_FLAG_TC) == RESET);
}
发送字符串:
bash
/*****************************************************************
*函 数 名 称:USART3_SendStr
*函 数 功 能:串口3发送字符串
*函 数 形 参:Str:发送的内容 len:长度
*函 数 返 回:无
*******************************************************************/
void USART3_SendStrLen(uint8_t *Str,uint16_t len)
{
uint16_t i=0;
for(i=0;i<len;i++)
{
USART3_SendData(Str[i]);
}
}
定义一个WIFI的结构体进行信息的收发。
再写一个指令发送的函数.
bash
/*****************************************************************
*函 数 名 称:WIFI_SendCmd
*函 数 功 能:发送指令,并且判断是否发送成功
*函 数 形 参:CMD:发送的指令内容
ACK:期待回复的数据
TIME:等待时间 单位ms
***函 数 返 回:成功返回0 失败返回1**
*******************************************************************/
uint8_t WIFI_SendCmd(uint8_t* CMD,uint8_t* ACK,uint16_t TIME)
{
memset(&rxdata,0,sizeof(rxdata));//清数据
USART3_SendStr(CMD);
while(TIME--)//超时检测机制
{
if(rxdata.rxflag == 1) //接受完成之后再查找
{
rxdata.rxflag = 0;
if(strstr((char *)rxdata.rxbuff,(char *)ACK) != NULL)
{
memset(&rxdata,0,sizeof(rxdata));//清数据
return 0;//查到正确返回值
}
}
Delay_nms(1);
}
memset(&rxdata,0,sizeof(rxdata));//清数据
return 1;//规定时间没查到
}
其中strstr函数为查询目标字符串种是否有所需字符串,若有则返回所需字符串的地址,没有则返回0。
strstr(目标字符串,所需字符串)
链接服务器的函数:
bash
/*****************************************************************
*函 数 名 称:WIFI_ConnectSever
*函 数 功 能:WIFI链接服务器
*函 数 形 参:无
*函 数 返 回:成功返回0 失败返回1
*******************************************************************/
uint8_t WIFI_ConnectSever(char *severIP,uint16_t Port)
{
char buff[100]={0};
if(WIFI_SendCmd((u8*)"AT\r\n",(u8*)"OK",100) == 0)
{
printf("WIFI 正常工作\r\n");
if(WIFI_SendCmd((u8*)"AT+CWMODE=1\r\n",(u8*)"OK",100) == 0)
{
printf("WIFI 模式设置完成\r\n");
if(WIFI_SendCmd((u8*)"AT+CWJAP=\"cmyddf\",\"88888888\"\r\n",(u8*)"OK",30000) == 0)
{
printf("WIFI 热点链接完成\r\n");
sprintf(buff,"AT+CIPSTART=\"TCP\",\"%s\",%d\r\n",severIP,Port);
if(WIFI_SendCmd((u8*)buff,(u8*)"OK",3000) == 0)
{
printf("WIFI 服务器链接完成\r\n");
WIFI_SendCmd((u8*)"AT+CIPMODE=1\r\n",(u8*)"OK",3000);
WIFI_SendCmd((u8*)"AT+CIPSEND\r\n",(u8*)"OK",3000);
printf("请发送数据\r\n");
return 0;
}
return 1;
}
return 1;
}
return 1;
}
return 1;
}
三、需求实现代码
main.c
bash
//WIFI任务
TaskHandle_t Ali_TaskHandle;
void Ali_Task(void *p)
{
uint16_t cnt=0;
MyPrintf("Ali_Task begin\r\n"); //传感器开始
WIFI_ConnectSever(Sever_IP, SeverPort);//链接wifi
MQTT_Connect(ClientID, UserName,PassWord,3000);//发布报文
Delay_nms(1000);
MQTT_Subscribe(Subscribe_Topic);//订阅时钟同步主题
Delay_nms(1000);
MQTT_Publish(Publish_NTPTopic,(u8*)"{\"deviceSendTime\":\"123\"}");
while(1)
{ cnt++;
WIFI_Anylze();
vTaskDelay(10);
if(cnt>=500)
{
cnt=0;
MQTT_Publish(Publish_Topic,(u8 *)Payload);
}
}
}
wifi.c
bash
#include "wifi.h"
#include "delay.h"
#include "string.h"
#include "stdio.h"
#include "led.h"
#include "stdlib.h"
#include "rtc.h"
WIFI_DATA rxdata={0};
//串口3 PB10 - TX PB11 - RX
/*****************************************************************
*函 数 名 称:USART3_Config
*函 数 功 能:初始化串口3
*函 数 形 参:无
*函 数 返 回:无
*作 者:CYM
*修 改 日 期:xx/xx/xx
*******************************************************************/
void USART3_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct={0};
USART_InitTypeDef USART_InitStruct={0};
NVIC_InitTypeDef NVIC_InitStruct={0};
//1,开时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);
//2,配置IO口
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_11;
GPIO_Init(GPIOB,&GPIO_InitStruct);
//3,配置串口
USART_InitStruct.USART_BaudRate=115200;
USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
USART_InitStruct.USART_Parity=USART_Parity_No;
USART_InitStruct.USART_StopBits=USART_StopBits_1;
USART_InitStruct.USART_WordLength=USART_WordLength_8b;
USART_Init(USART3,&USART_InitStruct);
//4,使能串口
USART_Cmd(USART3,ENABLE);
//5,开中断
USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);
USART_ITConfig(USART3,USART_IT_IDLE,ENABLE);
//6,配置中断
NVIC_InitStruct.NVIC_IRQChannel=USART3_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=0;//抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority=0;//响应优先级
NVIC_Init(&NVIC_InitStruct);
}
/*****************************************************************
*函 数 名 称:WIFI_Config
*函 数 功 能:初始化WIFI
*函 数 形 参:无
*函 数 返 回:无
*作 者:CYM
*修 改 日 期:xx/xx/xx
*******************************************************************/
void WIFI_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct={0};
//配置通信串口
USART3_Config();
//配置使能引脚
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
//2,配置IO口
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOE,&GPIO_InitStruct);
GPIO_SetBits(GPIOE,GPIO_Pin_6);
Delay_nms(500);//延时让WIFI输出固件信息
}
/*****************************************************************
*函 数 名 称:USART3_IRQHandler
*函 数 功 能:初始化串口4
*函 数 形 参:无
*函 数 返 回:无
*作 者:CYM
*修 改 日 期:xx/xx/xx 01 02 03
*******************************************************************/
void USART3_IRQHandler(void)
{
uint8_t data=0;
if(USART_GetITStatus(USART3,USART_IT_RXNE) == SET)
{
data = USART3->DR;
rxdata.rxbuff[rxdata.rxcnt++] = data;
rxdata.rxcnt %= MAX;//确保不越界
USART1->DR = data;//通过串口1将数据发给电脑串口助手
}
if(USART_GetITStatus(USART3,USART_IT_IDLE) == SET)
{
rxdata.rxflag = 1;
data = USART3->SR;
data = USART3->DR;
}
}
/*****************************************************************
*函 数 名 称:USART3_SendData
*函 数 功 能:串口3发送单个字节
*函 数 形 参:data :发送的内容
*函 数 返 回:无
*作 者:CYM
*修 改 日 期:xx/xx/xx
*******************************************************************/
void USART3_SendData(uint8_t data)
{
while(USART_GetFlagStatus(USART3,USART_FLAG_TXE) == RESET);
USART3->DR = data;
while(USART_GetFlagStatus(USART3,USART_FLAG_TC) == RESET);
}
/*****************************************************************
*函 数 名 称:USART3_SendStr
*函 数 功 能:串口3发送字符串
*函 数 形 参:Str:发送的内容
len:长度
*函 数 返 回:无
*作 者:CYM
*修 改 日 期:xx/xx/xx
*******************************************************************/
void USART3_SendStrLen(uint8_t *Str,uint16_t len)
{
uint16_t i=0;
for(i=0;i<len;i++)
{
USART3_SendData(Str[i]);
}
}
void USART3_SendStr(uint8_t *Str)
{
while(*Str != '\0')
{
USART3_SendData(*Str);
Str++;
}
}
/*****************************************************************
*函 数 名 称:WIFI_SendCmd
*函 数 功 能:发送指令,并且判断是否发送成功
*函 数 形 参:CMD:发送的指令内容
ACK:期待回复的数据
TIME:等待时间 单位ms
*函 数 返 回:成功返回0 失败返回1
*作 者:CYM
*修 改 日 期:xx/xx/xx
*******************************************************************/
uint8_t WIFI_SendCmd(uint8_t* CMD,uint8_t* ACK,uint16_t TIME)
{
memset(&rxdata,0,sizeof(rxdata));//清数据
USART3_SendStr(CMD);
while(TIME--)//超时检测机制
{
if(rxdata.rxflag == 1) //接受完成之后再查找
{
rxdata.rxflag = 0;
if(strstr((char *)rxdata.rxbuff,(char *)ACK) != NULL)
{
memset(&rxdata,0,sizeof(rxdata));//清数据
return 0;//查到正确返回值
}
}
Delay_nms(1);
}
memset(&rxdata,0,sizeof(rxdata));//清数据
return 1;//规定时间没查到
}
/*****************************************************************
*函 数 名 称:WIFI_ConnectSever
*函 数 功 能:WIFI链接服务器
*函 数 形 参:无
*函 数 返 回:成功返回0 失败返回1
*作 者:CYM
*修 改 日 期:xx/xx/xx
*******************************************************************/
uint8_t WIFI_ConnectSever(char *severIP,uint16_t Port)
{
char buff[100]={0};
if(WIFI_SendCmd((u8*)"AT\r\n",(u8*)"OK",100) == 0)
{
printf("WIFI 正常工作\r\n");
if(WIFI_SendCmd((u8*)"AT+CWMODE=1\r\n",(u8*)"OK",100) == 0)
{
printf("WIFI 模式设置完成\r\n");
if(WIFI_SendCmd((u8*)"AT+CWJAP=\"cmyddf\",\"88888888\"\r\n",(u8*)"OK",30000) == 0)
{
printf("WIFI 热点链接完成\r\n");
sprintf(buff,"AT+CIPSTART=\"TCP\",\"%s\",%d\r\n",severIP,Port);
if(WIFI_SendCmd((u8*)buff,(u8*)"OK",3000) == 0)
{
printf("WIFI 服务器链接完成\r\n");
WIFI_SendCmd((u8*)"AT+CIPMODE=1\r\n",(u8*)"OK",3000);
WIFI_SendCmd((u8*)"AT+CIPSEND\r\n",(u8*)"OK",3000);
printf("请发送数据\r\n");
return 0;
}
return 1;
}
return 1;
}
return 1;
}
return 1;
}
/*****************************************************************
*函 数 名 称:WIFI_Anylze
*函 数 功 能:WIFI接受解析
*函 数 形 参:无
*函 数 返 回:无
*作 者:CYM
*修 改 日 期:xx/xx/xx
*******************************************************************/
void WIFI_Anylze(void)
{
uint32_t GetTime=0;
uint32_t nowtime=0;
char *p=NULL;
char buff[12]={0};
uint8_t i = 0;
if(rxdata.rxflag == 1)
{
if(rxdata.rxbuff[0] == '1')
Led_ON(1);
if(rxdata.rxbuff[0] == '0')
Led_OFF(1);
if(strstr((const char *)rxdata.rxbuff+10,"serverSendTime") != NULL )
{
printf("收到时间同步消息\r\n");
p = strstr((const char *)rxdata.rxbuff+10,"serverSendTime");
p += 17;
for(i=0;i<10;i++)
{
buff[i] = p[i];
}
GetTime=atoi(buff);
printf("提取出的时间结果:%d\r\n",GetTime);
//将获取到的时间,写入到RTC的计数器里即可。
//RTC_Configuration(GetTime);
nowtime=GetTime+8*60*60;
RTC_Configuration(nowtime);
}
if(strstr((const char *)rxdata.rxbuff+10,"\"powerstate\":1") != NULL )
{
Led_ON(1);
}
if(strstr((const char *)rxdata.rxbuff+10,"\"powerstate\":0") != NULL )
{
Led_OFF(1);
}
memset(&rxdata,0,sizeof(rxdata));
}
}
wifi.h
bash
#ifndef __WIFI_H
#define __WIFI_H
#include "stm32f10x.h"
#define MAX 1024
typedef struct{
uint8_t rxbuff[MAX];//保存接受数据
uint16_t rxcnt;//记录接受数据个数
uint8_t rxflag;//接受完成标志
}WIFI_DATA;
extern WIFI_DATA rxdata;
uint8_t WIFI_ConnectSever(char *severIP,uint16_t Port);
void USART3_SendStrLen(uint8_t * Str,uint16_t len);
void WIFI_Config(void);
void WIFI_Anylze(void);
#endif