可参考STC15实战的WiFi通信:http://t.csdn.cn/Aw0Uc
STM32通过串口与ESP-12S模块通信,控制模块连接路由器,然后连接服务器,将实战板的SHT30获取到的环境温湿度值上传到服务器,服务器可以下发指令,控制实战板的蜂鸣器开启与关闭,控制继电器吸合断开
指令 | 响应 | 说明 |
---|---|---|
AT | OK | 测试AT启动 |
ATE0 | OK | 关闭回显功能 |
AT+CWMODE_CUR=1 | OK | 设置模块为Station模式 |
AT+CWAUTOCONN=1 | OK | 设置上电自动连接AP |
AT+CWSTARTSMART=2 | OK | 开启SmartConfig(智能配网),类型:AirKiss |
AT+CWSTOPSMART | OK | 停止SmartConfig |
AT+CIFSR | OK | 查询本地IP地址 |
AT+CIPSTART=“TCP”,“192.168.10.6”,8888 | OK或者ERROR | 建立TCP连接 |
AT+CIPMODE=1 | OK | 设置传输模式为透传模式 |
AT+CIPSEND | > | 发送数据 |
+++ | 退出透传模式,回到AT指令模式 |
上面指令除了最后的”+++“不用在发送时添加回车换行,其他指令都要在末尾添加回车换行,如调用函数发送AT指令:
SendAT("AT\r\n");
GPIO配置
实验用到了数码管、触摸按键、LED灯、继电器,NPN输出这几个外设,配置对应的引脚,PF11是WiFi模块的使能引脚,PF12是WiFi模块的重启引脚,SCL和SDA是IIC通信引脚,与SHT30通信用的
蜂鸣器用定时器1产生PWM信号控制
串口2波特率配置为115200,因为WiFi模块默认波特率为115200
使能DMA发送和DMA接收,用DMA搬运WiFi模块与单片机之间的通信数据,增加CPU效率
另外开启串口1,将WiFi模块与单片机通信的数据通过串口1打印到串口助手上,方便查看通信过程与调试
代码量太大,记录一些重要的步骤
UART2.c
串口2发送数组和字符串的函数都是调用串口DMA发送,当调用SendArray函数时,DMA会自动将指针p_Arr指向的数组里的内容搬运到串口2发送,搬运的数组长度为LEN;SendString函数同理
/*
* @name SendArray
* @brief 发送数组
* @param p_Arr:数据首地址,LEN:数组长度
* @retval None
*/
static void SendArray(uint8_t* p_Arr,uint16_t LEN)
{
HAL_UART_Transmit_DMA(&huart2,p_Arr,LEN);
}
/*
* @name SendString
* @brief 发送字符串
* @param p_Str:字符串首地址
* @retval None
*/
static void SendString(uint8_t* p_Str)
{
HAL_UART_Transmit_DMA(&huart2,p_Str,strlen((const char*)p_Str));
}
System.c
主函数中,先调用SHT30相关函数获取温湿度值,然后显示在数码管上,配网函数通过触摸按键3外部中断长按2s来触发,TC连接状态位TCP_Connect_Status初始化为FALSE,表示没连接上TCP服务器,进入判断里,TCP重连定时器TCP_Reconnect_Timer初始化为10s,默认一上电就连接一次服务器,如果服务器连接成功,就调用ESP_12S.Transfer_SHT30()函数来发送温湿度值;如果服务器连接失败,则函数里判断有没有获取到IP地址,如果没有IP地址,则自动调用配网操作
/*
* @name Run
* @brief 系统运行
* @param None
* @retval None
*/
static void Run()
{
float Temp_float = 0;
uint16_t Temp_uint = 0;
//调用SHT30周期检测函数
SHT30.Measure_Period_Mode();
//判断温度正负
if(SHT30.fTemperature < 0)
{
Temp_float = 0 - SHT30.fTemperature;
Display.Disp_Other(Disp_NUM_4,0x40,Disp_DP_OFF);
}
else
{
Temp_float = SHT30.fTemperature;
Display.Disp_Other(Disp_NUM_4,0x00,Disp_DP_OFF);
}
//显示温度
Temp_uint = (uint16_t)(Temp_float*10); //将温度值乘以10,显示小数点后一位
Display.Disp_Hex(Disp_NUM_3,Temp_uint/100,Disp_DP_OFF); //显示十位
Display.Disp_Hex(Disp_NUM_2,Temp_uint%100/10,Disp_DP_ON); //显示个位,开启小数点
Display.Disp_Hex(Disp_NUM_1,Temp_uint%10,Disp_DP_OFF); //显示小数点后一位
//显示湿度
Display.Disp_Hex(Disp_NUM_6,SHT30.ucHumidity/10,Disp_DP_OFF);
Display.Disp_Hex(Disp_NUM_5,SHT30.ucHumidity%10,Disp_DP_OFF);
//配网
ESP_12S.SmartConfig();
//连接TCP服务器
if(ESP_12S.TCP_Connect_Status == FALSE)
{
if(ESP_12S.TCP_Reconnect_Timer >= TIMER_10s)
{
//连接TCP服务器
ESP_12S.TCP_Connect_Server();
ESP_12S.TCP_Reconnect_Timer = 0;
}
}
//传输SHT30的数据
ESP_12S.Transfer_SHT30();
//延时
HAL_Delay(500);
}
ESP-12S.c
智能配网函数SmartConfig,如果模块已经连上了服务器就会开启透传模式,再次配网的话就要先发送“+++”退出透传模式,然后就是发送智能配网的AT指令,并检测模块是否回应AP连接成功应答,再判断是配网成功还是失败,失败就不清除标志位,下个循环继续进入配网模式,因为没有连接AP后续连接TCP服务器就没有意义
/*
* @name SmartConfig
* @brief 智能配网
* @param AT_command:AT指令,Respond_Str:模块回应
* @retval None
*/
static void SmartConfig()
{
if(ESP_12S.SmartConfig_Flag == TRUE)
{
//关闭指示灯
LED.LED_Fun(LED2,LED_OFF);
LED.LED_Fun(LED3,LED_OFF);
//退出透传模式
*(UART2.pucSend_Buffer+0) = '+';
*(UART2.pucSend_Buffer+1) = '+';
*(UART2.pucSend_Buffer+2) = '+';
UART2.SendArray(UART2.pucSend_Buffer,3);
HAL_Delay(1000);
//发送配网AT指令
SendAT((uint8_t*)"AT\r\n",(uint8_t*)"OK"); //测试AT
SendAT((uint8_t*)"ATE0\r\n",(uint8_t*)"OK"); //关闭回显
SendAT((uint8_t*)"AT+CWMODE_CUR=1\r\n",(uint8_t*)"OK"); //设置模块为station模式
SendAT((uint8_t*)"AT+CWAUTOCONN=1\r\n",(uint8_t*)"OK"); //设置上电自动连接AP
SendAT((uint8_t*)"AT+CWSTARTSMART=2\r\n",(uint8_t*)"OK"); //开启SmartConfig,Airkiss类型
printf("Start SmartConfig:\r\n");
//等待配网,3分钟超时退出
Timer6.usDelay_Timer = 0;
while(Timer6.usDelay_Timer < TIMER_3min)
{
//DMA重新接收设置
ESP_12S.DMA_Receive_Set();
//LED2指示灯快闪
HAL_Delay(100);
LED.LED_Fun(LED2,LED_Flip);
//打印接收缓存的数据
printf("%s",UART2.pucRec_Buffer);
//判断接收缓存中是否有正确应答
if(strstr((const char*)UART2.pucRec_Buffer, "connected") != NULL) //成功连接到AP
{
SendAT((uint8_t*)"AT+CWSTOPSMART\r\n",(uint8_t*)"OK"); //停止SmartConfig
break;
}
}
//输出信息
//如果定时器小于3分钟的,说明是配网成功退出的循环
if(Timer6.usDelay_Timer < TIMER_3min)
{
printf("\r\n\r\nSmartConfig success!\r\n"); //打印配网成功信息
ESP_12S.TCP_Connect_Status = FALSE; //TCP连接标志位清零
ESP_12S.TCP_Reconnect_Timer = TIMER_10s; //立即重连服务器
ESP_12S.SmartConfig_Flag = FALSE; //清除配网标志位
}
//否则定时器大于3分钟,说明没配网成功,超时退出
else
{
printf("\r\n\r\nSmartConfig Fail!\r\n"); //打印配网失败信息
ESP_12S.TCP_Connect_Status = FALSE; //TCP连接标志位清零
ESP_12S.TCP_Reconnect_Timer = 0;
//配网失败则不清除标志位,继续配网,因为没配网后续操作没意义
}
//关闭LED灯
LED.LED_Fun(LED2,LED_OFF);
}
}
ESP-12S.c
DMA接收设置,准备接收WiFi模块返回的应答时,先关闭DMA接收,把接收缓存里的内容清除,再开启DMA接收,DMA接收完数据则进入空闲中断
/*
* @name DMA_Receive_Set
* @brief DMA接收设置
* @param None
* @retval None
*/
static void DMA_Receive_Set()
{
//先关闭串口2的DMA接收
HAL_UART_DMAStop(&huart2);
//清除接收缓存
Public.Memory_Clr(UART2.pucRec_Buffer,strlen((const char*)UART2.pucRec_Buffer));
//再开启串口2的DMA接收
HAL_UARTEx_ReceiveToIdle_DMA(&huart2,UART2.pucRec_Buffer,PUCREC_BUFFER_LEN);
}
CallBack.c
DMA搬运的数据有两种类型,一种是WiFi模块返回的应答,另一种是模块连接上服务器后,服务器下发指令到模块,模块再将指令传到穿串口,所以WiFi模块的应答可以直接从接收缓存里找,如果是服务器下发指令控制继电器和蜂鸣器,则在空闲中断回调函数中调用接收服务器信息函数,执行服务器指令,控制继电器或者蜂鸣器
/*
* @name HAL_UARTEx_RxEventCallback
* @brief 串口接收完成空闲中断回调函数
* @param huart:串口指针,Size:接收的数据大小
* @retval None
*/
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
//接收完成串口数据后,触发空闲中断,在这里处理接收数据
if(huart->Instance == huart2.Instance)
{
ESP_12S.Receive_Information();
}
}
/*
* @name Receive_Information
* @brief 接收服务器返回数据
* @param None
* @retval None
*/
static void Receive_Information()
{
if(ESP_12S.TCP_Connect_Status == TRUE)
{
//如果不是配网才发送
if(ESP_12S.SmartConfig_Flag == FALSE)
{
printf("Received information from the TCP server\r\n");
}
printf("%s\r\n",UART2.pucRec_Buffer);
//切换继电器状态
if(strstr((const char*)UART2.pucRec_Buffer,"Relay Flip") != NULL)
{
Relay.Relay_Flip();
}
//切换蜂鸣器状态
if(strstr((const char*)UART2.pucRec_Buffer,"Buzzer Flip") != NULL)
{
Buzzer.Buzzer_Flip();
}
//DMA重新接收设置,接收TCP服务器的新指令
ESP_12S.DMA_Receive_Set();
}
TCP服务器连接成功
服务器接收到SHT30采集的温湿度