实现目标
1、掌握云平台执行器的创建
2、熟悉STM32 与ESP8266模块之间的通信
3、具体实现目标:(1)创建5个执行器:蜂鸣器,LED1,LED2,ED3,LED4;(2)执行器能对应控制开发板的蜂鸣器和LED。
一、项目创建
项目创建详细步骤参考:https://blog.csdn.net/luojuan198780/article/details/138766783
项目整体:创建5个执行器
1.1 执行器之蜂鸣器设置
1.2 执行器之LED设置。4个LED配置除了名称其它都一样
二、开发板连接图
开发板P7四个跳键帽接法如下图:
说明:单片机的USART1用于调试信息打印;USART2用于与ESP8266模块通信。
三、STM32CubeMX 配置
1.GPIO配置
BEEP:初始电平为低电平,推挽输出;LED1-LED4:初始电平为高电平,推挽输出。
2.串口USART1配置
USART1:异步模式,波特率115200,功能:用于打印调试信息
3.串口USART2配置
USART2:异步模式,波特率115200,开启DMA,开启中断。功能:用于与ESP8266通信
四、程序设计
说明:整个工程的代码量大,下面只列出了整个工程中最主要的一些函数。
1、串口USART1重定向
#include <stdio.h>
int fputc(int ch,FILE *f)
{
HAL_UART_Transmit (&huart1 ,(uint8_t *)&ch,1,HAL_MAX_DELAY );
return ch;
}
2、串口USART2收发实现
uint8_t UART2_RX_BUF[255];
//开启DMA空闲接收中断,将数据存放在数组UART2_RX_BUF中
HAL_UARTEx_ReceiveToIdle_DMA(&huart2,UART2_RX_BUF,sizeof(UART2_RX_BUF));
//中断回调函数:实现接收云平台发来的数据
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if(huart == &huart2) //
{
HAL_UARTEx_ReceiveToIdle_DMA(&huart2,UART2_RX_BUF,sizeof(UART2_RX_BUF));
__HAL_DMA_DISABLE_IT(&hdma_usart2_rx,DMA_IT_HT);
}
}
//功能:发送AT指令字符串
void SendAtCmd(uint8_t *string, uint8_t len)
{
HAL_UART_Transmit(&huart2,string, len,1000);
HAL_UART_Transmit(&huart2,(uint8_t *)"\r\n", 2,20);
}
//清空AT串口接收缓存
void ClrAtRxBuf(void)
{
memset(AT_RX_BUF, 0x00, MAX_AT_RX_LEN);//清空缓存
AT_RX_COUNT = 0;
F_AT_RX_FINISH = 0;
}
3、STM32发送AT指令主要函数
//ESP8266设置为station模式 -----1
int8_t ESP8266_SetStation(void)
{
ClrAtRxBuf();//清空缓存
SendAtCmd((uint8_t *)AT_CWMODE,strlen(AT_CWMODE));
printf("AT+CWMODE_CUR=1\r\n");
HAL_Delay(100);
if(strstr((const char *)AT_RX_BUF, (const char *)"OK") == NULL)
{
printf("AT+CWMODE_CUR=1 error\r\n");
return -1;
}
else
{
printf("AT+CWMODE_CUR=1 ok\r\n");
}
return 0;
}
//设置ESP8266要连接的热点名称和密码 -----2
int8_t ESP8266_SetAP(char *wifi, char *pwd)
{
uint8_t AtCwjap[MAX_AT_TX_LEN];
memset(AtCwjap, 0x00, MAX_AT_TX_LEN);//清空缓存
ClrAtRxBuf();//清空缓存
sprintf((char *)AtCwjap,"AT+CWJAP_CUR=\"%s\",\"%s\"",wifi, pwd);
printf("AT+CWJAP_CUR=newland-edu1,12345678 \r\n");
SendAtCmd((uint8_t *)AtCwjap,strlen((const char *)AtCwjap));
HAL_Delay(5500);
if(strstr((const char *)AT_RX_BUF, (const char *)"OK") == NULL)
{
return -1;
}
return 0;
}
//ESP8266建立TCP连接 -----3
int8_t ESP8266_IpStart(char *IpAddr, uint16_t port)
{
uint8_t IpStart[MAX_AT_TX_LEN];
memset(IpStart, 0x00, MAX_AT_TX_LEN);//清空缓存
ClrAtRxBuf();//清空缓存
sprintf((char *)IpStart,"AT+CIPSTART=\"TCP\",\"%s\",%d",IpAddr, port);
printf("%s\r\n",IpStart);//
SendAtCmd((uint8_t *)IpStart,strlen((const char *)IpStart));
HAL_Delay(1500);
if(strstr((const char *)AT_RX_BUF, (const char *)"OK") == NULL)
{
return -1;
}
return 0;
}
//ESP8266建立TCP连接 -----4
int8_t ESP8266_IpSend(char *IpBuf, uint8_t len)
{
uint8_t TryGo = 0;
int8_t error = 0;
uint8_t IpSend[MAX_AT_TX_LEN];
memset(IpSend, 0x00, MAX_AT_TX_LEN);//清空缓存
ClrAtRxBuf();//清空缓存
sprintf((char *)IpSend,"AT+CIPSEND=%d",len);
printf("%s\r\n",IpSend);/
SendAtCmd((uint8_t *)IpSend,strlen((const char *)IpSend));
HAL_Delay(3);
if(strstr((const char *)AT_RX_BUF, (const char *)"OK") == NULL)
{
return -1;
}
ClrAtRxBuf();//清空缓存
SendStrLen((uint8_t *)IpBuf, len);
printf("%s\r\n",IpBuf);//
for(TryGo = 0; TryGo<60; TryGo++)//最多等待时间 100*60=6000ms
{
if(strstr((const char *)AT_RX_BUF, (const char *)"SEND OK") == NULL)
{
error = -2;
}
else
{
error = 0;
break;
}
HAL_Delay(100);
}
return error;
}
//连接到服务器 -----5
int8_t ConnectToServer(char *DeviceID, char *SecretKey)
{
uint8_t TryGo = 0;
int8_t error = 0;
uint8_t TxetBuf[MAX_AT_TX_LEN];
memset(TxetBuf,0x00,MAX_AT_TX_LEN);//清空缓存
for(TryGo = 0; TryGo<3; TryGo++)
{
if(ESP8266_SetStation() == 0)//设置WiFi通讯模块工作模式
{
error = 0;
break;
}
else
{
error = -1;
}
}
if(error < 0)
{
return error;
}
for(TryGo = 0; TryGo<3; TryGo++)
{
if(ESP8266_SetAP((char *)WIFI_AP, (char *)WIFI_PWD) == 0)//设置热点名称和密码
{
error = 0;
break;
}
else
{
error = -2;
}
}
if(error < 0)
{
return error;
}
for(TryGo = 0; TryGo<3; TryGo++)
{
if(ESP8266_IpStart((char *)SERVER_IP,SERVER_PORT) == 0)//连接服务器IP地址,端口:120.77.58.34,8600
{
error = 0;
break;
}
else
{
error = -3;
}
}
if(error < 0)
{
return error;
}
sprintf((char *)TxetBuf,"{\"t\":1,\"device\":\"%s\",\"key\":\"%s\",\"ver\":\"v0.0.0.0\"}",DeviceID,SecretKey);
//printf("%s\r\n",TxetBuf);
if(ESP8266_IpSend((char *)TxetBuf, strlen((char *)TxetBuf)) < 0)
{//发送失败
error=-4;
}
else
{//发送成功
for(TryGo = 0; TryGo<50; TryGo++)//最多等待时间50*10=500ms
{
if(strstr((const char *)AT_RX_BUF, (const char *)"\"status\":0") == NULL)//检查响应状态是否为握手成功
{
error = -5;
}
else
{
error = 0;
break;
}
HAL_Delay(10);
}
}
return error;
}
//发送传感数据到服务器 -----6
int8_t ESP8266_SendSensor(uint8_t sensor, char *TimeStr)
{
uint8_t TryGo = 0;
int8_t error = 0;
uint8_t TxetBuf[MAX_AT_TX_LEN];
memset(TxetBuf,0x00,MAX_AT_TX_LEN);//清空缓存
sprintf((char *)TxetBuf,"{\"t\":3,\"datatype\":2,\"datas\":{\"alarm\":{\"%s\":%d},\"temp\":{\"%s\":%d}},\"msgid\":001}",TimeStr,sensor,TimeStr,sensor+1);
//printf("%s\r\n",TxetBuf);
if(ESP8266_IpSend((char *)TxetBuf, strlen((char *)TxetBuf)) < 0)
{//发送失败
error=-1;
}
else
{//发送成功
for(TryGo = 0; TryGo<50; TryGo++)//最多等待时间50*10=500ms
{
if(strstr((const char *)AT_RX_BUF, (const char *)"\"status\":0") == NULL)//检查响应状态是否为"上报成功"
{
error = -1;
}
else
{
error = 0;
break;
}
HAL_Delay(10);
}
}
return error;
}
4、解析云平台数据函数
//解析服务器数据-----7
uint8_t ESP8266_GetIpData(uint8_t *AtRxBuf, char *GetIpData)
{
char *Point = NULL;
uint8_t len = 0;
Point = strstr((const char *)AtRxBuf, (const char *)"+IPD,");
if(Point != NULL)//检查模块收到TCP/IP数据包?
{
len=(Point[5]-'0')*10+Point[6]-'0';//这里IP数据长度不会超过100,故可以按“个位”和“十位”计算收到的IP数据长度
Point = strstr((const char *)AtRxBuf, (const char *)":")+1;
memcpy(GetIpData, Point, len);
GetIpData[len]='\0';
printf("收到IP数据:%s\r\n",GetIpData);///
printf("收到IP数据长度=%d\r\n",len);///
}
return (len);
}
//对服务器下发的命令进行判断处理-----8
void USER_DataAnalysisProcess(char *RxBuf)
{
char *cmdid = NULL;
uint8_t TxetBuf[128];
if(strstr((const char *)RxBuf, (const char *)PING_REQ) != NULL)//心跳请求?
{
if(ESP8266_IpSend((char *)PING_RSP, strlen((const char *)PING_RSP)) < 0)//响应心跳
{//发送失败
printf("发送心跳包失败!\r\n");
}
else
{
printf("心跳包!\r\n");
}
}
else if(strstr((const char *)RxBuf, (const char *)"\"t\":5") != NULL)//命令请求?
{
if(strstr((const char *)RxBuf, (const char *)"\"apitag\":\"defense\"") != NULL)//布防/撤防请求
{
memset(TxetBuf,0x00,128);//清空缓存
if((strstr((const char *)RxBuf, (const char *)"\"data\":1") != NULL))//布防
{
printf("布防!\r\n");
;//...
;//...
;//...
cmdid = USER_GetJsonValue((char *)RxBuf, (char *)"cmdid");
sprintf((char *)TxetBuf,"{\"t\":6,\"cmdid\":%s,\"status\":0,\"data\":1}",cmdid);
//printf("%s\r\n",TxetBuf);
if(ESP8266_IpSend((char *)TxetBuf, strlen((char *)TxetBuf)) < 0)
{//发送失败
printf("发送响应失败!\r\n");
}
}
else if((strstr((const char *)RxBuf, (const char *)"\"data\":0") != NULL))//撤防
{
printf("撤防!\r\n");
;//...
;//...
;//...
cmdid = USER_GetJsonValue((char *)RxBuf, (char *)"cmdid");
sprintf((char *)TxetBuf,"{\"t\":6,\"cmdid\":%s,\"status\":0,\"data\":0}",cmdid);
//printf("%s\r\n",TxetBuf);
if(ESP8266_IpSend((char *)TxetBuf, strlen((char *)TxetBuf)) < 0)
{//发送失败
printf("发送响应失败!\r\n");
}
}
}
else if(strstr((const char *)RxBuf, (const char *)"\"apitag\":\"ctrl\"") != NULL)//开锁/关锁请求
{
memset(TxetBuf,0x00,128);//清空缓存
if((strstr((const char *)RxBuf, (const char *)"\"data\":1") != NULL))//开锁
{
printf("开锁!\r\n");
;//...
;//...
;//...
cmdid = USER_GetJsonValue((char *)RxBuf, (char *)"cmdid");
sprintf((char *)TxetBuf,"{\"t\":6,\"cmdid\":%s,\"status\":0,\"data\":1}",cmdid);
//printf("%s\r\n",TxetBuf);
if(ESP8266_IpSend((char *)TxetBuf, strlen((char *)TxetBuf)) < 0)
{//发送失败
printf("发送响应失败!\r\n");
}
lock=1;
}
else if((strstr((const char *)RxBuf, (const char *)"\"data\":0") != NULL))//关锁
{
printf("关锁!\r\n");
;//...
;//...
;//...
cmdid = USER_GetJsonValue((char *)RxBuf, (char *)"cmdid");
sprintf((char *)TxetBuf,"{\"t\":6,\"cmdid\":%s,\"status\":0,\"data\":0}",cmdid);
//printf("%s\r\n",TxetBuf);
if(ESP8266_IpSend((char *)TxetBuf, strlen((char *)TxetBuf)) < 0)
{//发送失败
printf("发送响应失败!\r\n");
}
}
}
}
}
五、工程修改
由于代码量大,作为上课学生,只需要按照下面教程在我下发的工程基础上进行修改程序即可。
1、在CloudReference.h 修改wifi名称和密码
2、对云平台下发命令进行处理
在stm32App.c 文件void USER_DataAnalysisProcess(char *RxBuf)函数中对下发命令做相应处理
方法:上面代码段是对云平台LED1下发的指令进行相应的处理:当云平台点击"开",则它会下发数据{"apitag":"LED1","cmdid":"96c80b17-a9d9-4169-9a22-a7ad07fd8e2f","data":1,"t":5} ,单片机收到后作打开开发板LED1操作;当云平台点击"关",则它会下发数据{"apitag":"LED1","cmdid":"3b698d9a-34fc-4448-b707-fec5de1da636","data":0,"t":5},单片机收到后作熄灭开发板LED1操作。如果对其它3个LED灯的命令解析,我们可以复制一份“else if”代码块,接在这代码块后。