本次博客知识来自于韦东山老师的7天物联网课程。
一、预期功能
运用ESP8266模块,使单片机与手机微信的小程序进行通信,实现手机控制LED的亮灭。并且,单片机可以远程控制微信小程序中模拟的门铃开启和关闭。
二、硬件设置
本次实验采用STM32F103C8T6,用到了以下单片机的以下功能。
GPIO_PA0,用作外部中断0,并且连接一个按键,使用按键向手机发送数据。
GPIO_PA1,连接LED灯,手机APP控制LED的亮灭。
USART2,使用串口2与ESP8266模块连接,并启用串口中断,当接收到ESP8266的信息(手机的信息)时,发送串口中断。
因为要开启调试模式,所以在SYS中要把Debug设置为Serial Wire。
三、发送程序编写
所有函数记得声明
1、定义静态变量
static uint8_t rx_data = 0; //接收的字符
static uint8_t rx_len = 0; //接收的长度
static uint8_t usart_rx_buf[200] = {0};//保存接收的数据
2、调用HAL库接收函数,等待接收数据,这样可以在程序一开始就启用接受中断。每次接受一个字节大小的数据。在usart.c中编写。
void USART_StartRx(void)
{
HAL_UART_Receive_IT(&huart2, (uint8_t*)&rx_data, 1);
}
3、USART中断回调函数,保存接收的数据并启动下一次接收。每接收到一个字节的数据,发送一次中断,调用一次中断回调函数,把接受的一个字节的数据存储到usart_rx_buf[]中。
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
usart_rx_buf[rx_len%200]= rx_data;
rx_len++;
HAL_UART_Receive_IT(&huart2, (uint8_t*)&rx_data, 1);
}
4、获取接收的数据。可以调用这个函数获取接受的数据。
uint8_t USART2_Receive(uint8_t *rx_data)
{
memcpy(rx_data, (uint8_t*)usart_rx_buf, rx_len);
return rx_len;
}
5、UASRT2发送数据。这里调用的时HEL库的函数。
void USART2_Transmit(uint8_t *pdata, uint16_t len, uint16_t tt)
{
HAL_UART_Transmit(&huart2, pdata, len, tt);
}
6、清除接收的数据,运用memset把usart_rx_buf[]清空。
void USART2_ClearBuf(void)
{
rx_len = 0;
memset((uint8_t*)usart_rx_buf, 0, 200);
}
7、向ESP8266发送数据,要向每个命令加入换行符才能执行。
uint8_t ESP8266_SendCommand(char *cmd, char *reply, uint16_t timeout)
{
//1.保存传入的指令
char buf[256] = {0};
strcat(buf, cmd); //strcpy
//2.处理AT指令(添加换行)
if (strstr(buf, "\r\n") == 0)
{
strcat(buf, "\r\n");
}
//3.清理前面接收的buf
USART2_ClearBuf();
//4.发送数据
USART2_Transmit((uint8_t *)buf, strlen(buf), 500);
//5.接收数据
memset(buf, 0, 256); //buf清空
while(timeout != 0) //超时控制
{
if (USART2_Receive((uint8_t *)buf)) //接收数据
{
//检查结果
if (strstr(buf, reply))
{
printf("%s Send ok!\r\n", cmd);//发送成功
return 0;
}
else
{
timeout--;
HAL_Delay(1);
}
}
}
printf("%s Send error!\r\n", cmd);//发送失败
return 1;
}
8、获取ESP8266的IP,因为返回的结果不一样,所以这里要重新写一个接受IP的函数。
uint8_t ESP8266_GetIP(void)
{
uint16_t timeout = 500;
//1.准备发送的指令 AT+CIFSR
char buf[256] = {0};
strcat(buf, "AT+CIFSR\r\n");
//2.清理前面接收的buf
USART2_ClearBuf();
//4.发送数据
USART2_Transmit((uint8_t *)buf, strlen(buf), 500);
//5.接收数据
memset(buf, 0, 256); //buf清空
while(timeout != 0) //超时控制
{
if (USART2_Receive((uint8_t *)buf)) //接收数据
{
//检查结果
if (strstr(buf, "OK"))
{
printf("%s", buf); //打印IP
return 0;
}
else
{
timeout--;
HAL_Delay(1);
}
}
}
printf("Get IP Failed! \r\n"); //获取失败
return 1;
}
9、ESP8266发送UDP数据,这里使用ESP8266的UDP传输。因为发送的数据是变量,所以要重新写一个函数。
uint8_t ESP8266_Send_UDP(char *data)
{
//1.准备发送的指令 AT+CIPSEND=len
char buf[256] = {0};
uint8_t len = strlen(data);
sprintf(buf, "AT+CIPSEND=%d\r\n", len); //把格式化的数据写入字符串
if (ESP8266_SendCommand(buf, "OK", 500) == 0) //发送指令
{
ESP8266_SendCommand(data, "SEND OK", 1000); //发送数据
return 0;
}
return 1;
}
四、按键标志位程序编写
//在gpio.c中
static uint8_t key_flag = 0;
uint8_t Key_GetFlag(void)
{
if(key_flag)
{
key_flag = 0;
return 1;
}
else
return 0;
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == KEY_Pin)
{
key_flag = 1;
}
}
五、主函数编写
1、定义全局变量
uint8_t rx_data[200] = {0}; // 保存接收数据
uint8_t tx_data[200] = "AT\r\n"; // 保存发送数据
2、启动ESP8266模块
USART_StartRx(); //启动开始接收数据
ESP8266_SendCommand("AT+RST", "OK", 500);
HAL_Delay(800);
ESP8266_SendCommand("AT+CWMODE=1", "OK", 500);
ESP8266_SendCommand("AT+CWJAP=\"wife名称\",\"wife密码\"", "OK", 5000);
ESP8266_SendCommand("AT+CIPSTART=\"UDP\",\"192.168.50.230\",9999,9999,2", "OK", 500);
ESP8266_GetIP();
3、实现按键按下操作手机微信小程序。实现手机微信小程序控制LED灯的亮灭。
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
//按键按下
if (Key_GetFlag())
{
ESP8266_Send_UDP("{\"data\":\"doorbell\",\"status\":\"1\"}");
}
//收到UDP数据
if (USART2_Receive(rx_data))
{
//printf("%s\r\n", rx_data);
if (strstr((char *)rx_data, "\"dev\":\"led\",\"status\":\"0\""))
{
printf("led off\r\n");
printf("%s\r\n",rx_data);
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
USART2_ClearBuf();
//memset(rx_data, 0, 200);
}
else if (strstr((char *)rx_data, "\"dev\":\"led\",\"status\":\"1\""))
{
printf("led on\r\n");
printf("%s\r\n",rx_data);
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
USART2_ClearBuf();
//memset(rx_data, 0, 200);
}
}
}