Files
stm32-iot-sensor-node/mylib/esp8266.c
T
2026-10-19 15:47:18 +08:00

333 lines
11 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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);
}