333 lines
11 KiB
C
333 lines
11 KiB
C
#include <stdio.h>
|
||
#include <string.h>
|
||
#include "systick.h"
|
||
#include "queue.h"
|
||
|
||
#include "esp8266.h"
|
||
|
||
/* USART2管脚定义
|
||
TX PA2
|
||
RX PA3
|
||
*/
|
||
// 创建USART2的接收队列。
|
||
static struct queue usart2_rx_queue;
|
||
|
||
// 重置 Wifi 模块的传输模式为工作站模式,成功返回1,失败返回0.
|
||
int esp8266_reset_module(void)
|
||
{
|
||
char buf[100];
|
||
|
||
// 重置Wifi模块
|
||
esp8266_send_cmd("AT+RST");
|
||
delay_ms(1000);
|
||
delay_ms(1000);
|
||
esp8266_clear_read_buf();
|
||
// 设置Wifi的工作模式为 工作站模式
|
||
esp8266_send_cmd("AT+CWMODE=1");
|
||
if (esp8266_read_response(buf, sizeof(buf), 1000) == 0)
|
||
return 0;
|
||
if (strstr(buf, "OK"))
|
||
return 1;
|
||
return 0;
|
||
}
|
||
|
||
// ESP8266初始化
|
||
void esp8266_init(void)
|
||
{
|
||
GPIO_InitTypeDef GPIO_InitStruct;
|
||
USART_InitTypeDef USART_InitStruct;
|
||
NVIC_InitTypeDef NVIC_InitStruct;
|
||
|
||
// 初始化接收队列
|
||
queue_init(&usart2_rx_queue);
|
||
|
||
// 使能PA2和 PA3的时钟
|
||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
|
||
// 使能 USART2外设时钟
|
||
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
|
||
|
||
// 设置PA2 为发送管脚
|
||
// 复用推挽输出
|
||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
|
||
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
|
||
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
|
||
GPIO_Init(GPIOA, &GPIO_InitStruct);
|
||
// 设置PA3 为接受管脚
|
||
// 悬空输入
|
||
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
|
||
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
|
||
GPIO_Init(GPIOA, &GPIO_InitStruct);
|
||
|
||
// 初始化 USART1控制器
|
||
USART_InitStruct.USART_BaudRate = 115200; // 设置波特率
|
||
USART_InitStruct.USART_WordLength = USART_WordLength_8b; // 字长8bit
|
||
USART_InitStruct.USART_StopBits = USART_StopBits_1; // 停止位1bit
|
||
USART_InitStruct.USART_Parity = USART_Parity_No; // 校验位(无)
|
||
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 收发
|
||
USART_InitStruct.USART_HardwareFlowControl =
|
||
USART_HardwareFlowControl_None; // 硬件流控制(无)
|
||
USART_Init(USART2, &USART_InitStruct);
|
||
// 使能串口2
|
||
USART_Cmd(USART2, ENABLE);
|
||
|
||
// 初始化接收中断
|
||
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
|
||
// 清理中断标志位
|
||
USART_ClearFlag(USART2, USART_FLAG_TC);
|
||
|
||
NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn; // 中断通道
|
||
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
|
||
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
|
||
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
|
||
NVIC_Init(&NVIC_InitStruct);
|
||
|
||
// 开启接收寄存器非空中断
|
||
// USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
|
||
if (0 == esp8266_reset_module())
|
||
printf("reset Wifi module Error!\r\n");
|
||
}
|
||
|
||
// 编写冲断处理函数
|
||
void USART2_IRQHandler(void)
|
||
{
|
||
uint8_t ch;
|
||
// 如果是接收寄存器非空中断,则读取数据
|
||
if (USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
|
||
{
|
||
ch = USART_ReceiveData(USART2);
|
||
// 将接收到的字符放入接收队列,如果队列已满会丢掉此字符
|
||
queue_enqueue(&usart2_rx_queue, ch);
|
||
}
|
||
}
|
||
|
||
// 用于串口发送一个字节的数据
|
||
void usart2_put_char(uint8_t ch)
|
||
{
|
||
USART_SendData(USART2, ch); // 发送数据
|
||
// 等待发送数据完毕
|
||
while (USART_GetFlagStatus(USART2, USART_FLAG_TC) != SET)
|
||
;
|
||
}
|
||
|
||
// ESP8266通过USART2发送字符串数据
|
||
void esp8266_send_string(const char *buf)
|
||
{
|
||
const char *pchar;
|
||
for (pchar = buf; *pchar; ++pchar)
|
||
usart2_put_char((uint8_t)*pchar);
|
||
}
|
||
|
||
// ESP8266通过USART2发送AT指令
|
||
void esp8266_send_cmd(const char *cmd)
|
||
{
|
||
esp8266_send_string(cmd);
|
||
esp8266_send_string("\r\n");
|
||
}
|
||
|
||
// 从USART2 读取相应字符串放入 buf,总等待时长timeout_ms毫秒ms,
|
||
// 直到timeout_ms耗尽为0才返回
|
||
int esp8266_read_response(char *buf, int buf_size, int timeout_ms)
|
||
{
|
||
int i = 0;
|
||
int ch = '\0';
|
||
// 等待接收数据,最大等待 timeout_ms 微妙
|
||
while (--timeout_ms)
|
||
{
|
||
if (queue_is_empty(&usart2_rx_queue))
|
||
delay_ms(1);
|
||
else
|
||
break;
|
||
}
|
||
if (timeout_ms == 0) // 超时返回
|
||
return 0;
|
||
// 接收数据
|
||
while (i < buf_size - 1 && timeout_ms > 0)
|
||
{
|
||
if (queue_is_empty(&usart2_rx_queue))
|
||
{
|
||
delay_ms(1); // 继续等待,直至timeout_ms为零
|
||
timeout_ms--;
|
||
}
|
||
else
|
||
{
|
||
queue_dequeue(&usart2_rx_queue, &ch);
|
||
buf[i] = ch;
|
||
i++;
|
||
}
|
||
}
|
||
buf[i] = '\0'; // 添加尾零后返回数据
|
||
// debug print for response
|
||
printf("esp8266_resp: %s\r\n", buf);
|
||
return i;
|
||
|
||
// for (i = 0; i < buf_size -1; i++) {
|
||
// queue_dequeue(&usart2_rx_queue, &ch);
|
||
// buf[i] = ch;
|
||
// if (queue_is_empty(&usart2_rx_queue))
|
||
// delay_ms(1);
|
||
// if (queue_is_empty(&usart2_rx_queue)) {
|
||
// i++;
|
||
// buf[i] = '\0';
|
||
// return i;
|
||
// }
|
||
// }
|
||
// return i;
|
||
}
|
||
|
||
// ESP8266 连接 Wifi,成功返回1,失败返回0
|
||
int esp8266_connect_wifi(const char *ssid, const char *passwd)
|
||
{
|
||
char buf[100];
|
||
// 拼接命令字符串
|
||
sprintf(buf, "AT+CWJAP=\"%s\",\"%s\"", ssid, passwd);
|
||
esp8266_clear_read_buf(); // 清空读缓冲。
|
||
esp8266_send_cmd(buf);
|
||
// WiFi 连接可能需要较长时间,延长超时到 15s
|
||
if (0 == esp8266_read_response(buf, sizeof(buf), 15000))
|
||
return 0;
|
||
if (strstr(buf, "OK") != NULL)
|
||
return 1;
|
||
// print returned response for debugging
|
||
printf("esp8266_connect_wifi failed resp: %s\r\n", buf);
|
||
return 0;
|
||
}
|
||
|
||
// ESP8266 作为客户端远程 TCP 连接。成功返回1,失败返回0
|
||
int esp8266_tcp_connect(const char *ip, uint16_t port)
|
||
{
|
||
char buf[100];
|
||
// 拼接命令字符串
|
||
sprintf(buf, "AT+CIPSTART=\"TCP\",\"%s\",%d", ip, port);
|
||
esp8266_clear_read_buf(); // 清空读缓冲。
|
||
esp8266_send_cmd(buf);
|
||
if (0 == esp8266_read_response(buf, sizeof(buf), 8000))
|
||
return 0;
|
||
// some firmwares may return "OK" or "ALREADY" or "ERROR"
|
||
if (strstr(buf, "OK") != NULL || strstr(buf, "ALREADY") != NULL)
|
||
return 1;
|
||
printf("esp8266_tcp_connect resp: %s\r\n", buf);
|
||
return 0;
|
||
}
|
||
|
||
// 关闭tcp 连接
|
||
void esp8266_tcp_close(void)
|
||
{
|
||
esp8266_send_cmd("AT+CIPCLOSE");
|
||
delay_ms(1000);
|
||
esp8266_clear_read_buf(); // 清空读缓冲。
|
||
}
|
||
|
||
// ESP8266 TCP 协议发送数据,返回写入的字节数,0表示失败
|
||
int esp8266_write(const char *data, uint16_t len)
|
||
{
|
||
char buf[100];
|
||
// 拼接命令字符串
|
||
sprintf(buf, "AT+CIPSEND=%d", len);
|
||
esp8266_clear_read_buf(); // 清空读缓冲。
|
||
esp8266_send_cmd(buf);
|
||
// wait for prompt '>' (and optionally OK). Allow longer timeout for some modules.
|
||
if (0 == esp8266_read_response(buf, sizeof(buf), 8000))
|
||
return 0;
|
||
// If there's no '>' in the response, but OK is present, try to read again shortly
|
||
if (strstr(buf, ">") == NULL) {
|
||
if (strstr(buf, "OK") != NULL) {
|
||
// try a short additional read to get the '>' prompt
|
||
if (0 == esp8266_read_response(buf, sizeof(buf), 2000) || strstr(buf, ">") == NULL) {
|
||
printf("esp8266_write: no '>' prompt after CIPSEND, resp: %s\r\n", buf);
|
||
return 0;
|
||
}
|
||
} else {
|
||
printf("esp8266_write: unexpected resp to CIPSEND: %s\r\n", buf);
|
||
return 0;
|
||
}
|
||
}
|
||
// send payload
|
||
esp8266_send_string(data);
|
||
// wait for SEND OK (some firmwares reply with SEND OK)
|
||
if (0 == esp8266_read_response(buf, sizeof(buf), 5000)) {
|
||
// no response, still consider write as attempted
|
||
return len;
|
||
}
|
||
if (strstr(buf, "SEND OK") != NULL || strstr(buf, "OK") != NULL) {
|
||
return len;
|
||
}
|
||
// unknown response, but we've attempted send
|
||
printf("esp8266_write: post-send resp: %s\r\n", buf);
|
||
return len;
|
||
}
|
||
|
||
// ESP8266 读取数据,返回读取的字节数
|
||
uint16_t esp8266_read(char *data, uint16_t buf_size)
|
||
{
|
||
// accumulate reads and search for +IPD, length:data pattern
|
||
char tmp[128];
|
||
char acc[512];
|
||
int acc_len = 0;
|
||
int total_timeout = 5000; // total wait up to 5s
|
||
int step = 200; // per-read timeout
|
||
|
||
acc[0] = '\0';
|
||
while (total_timeout > 0 && acc_len < (int)sizeof(acc) - 1)
|
||
{
|
||
int r = esp8266_read_response(tmp, sizeof(tmp), step);
|
||
if (r > 0)
|
||
{
|
||
// append
|
||
int copy = r < (int)sizeof(acc) - 1 - acc_len ? r : (int)sizeof(acc) - 1 - acc_len;
|
||
memcpy(acc + acc_len, tmp, copy);
|
||
acc_len += copy;
|
||
acc[acc_len] = '\0';
|
||
// debug
|
||
printf("esp8266_acc: %s\r\n", acc);
|
||
// search for +IPD,
|
||
char *pfind = strstr(acc, "+IPD,");
|
||
if (pfind)
|
||
{
|
||
pfind += 5; // move past +IPD,
|
||
// parse number until ':'
|
||
int len = 0;
|
||
while (*pfind && *pfind != ':' && len < 100000)
|
||
{
|
||
if (*pfind >= '0' && *pfind <= '9')
|
||
len = len * 10 + (*pfind - '0');
|
||
pfind++;
|
||
}
|
||
if (*pfind == ':')
|
||
{
|
||
pfind++; // data start
|
||
int copylen = len < (int)buf_size - 1 ? len : (int)buf_size - 1;
|
||
// ensure we have enough bytes in acc; if not, try to read more briefly
|
||
int available = (int)(acc + acc_len - pfind);
|
||
if (available < copylen)
|
||
{
|
||
// try one more short read
|
||
int r2 = esp8266_read_response(tmp, sizeof(tmp), 500);
|
||
if (r2 > 0)
|
||
{
|
||
int c = r2 < (int)sizeof(acc) - 1 - acc_len ? r2 : (int)sizeof(acc) - 1 - acc_len;
|
||
memcpy(acc + acc_len, tmp, c);
|
||
acc_len += c;
|
||
acc[acc_len] = '\0';
|
||
}
|
||
}
|
||
// copy payload
|
||
memcpy(data, pfind, copylen);
|
||
data[copylen] = '\0';
|
||
return (uint16_t)copylen;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
total_timeout -= step;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
// ESP8266 清空读缓冲区
|
||
void esp8266_clear_read_buf(void)
|
||
{
|
||
queue_clear(&usart2_rx_queue);
|
||
}
|