shuichan_old/V1.2/STM32F103_App1/HardWare/WIFI/esp8266.c

385 lines
13 KiB
C
Raw Normal View History

/* esp8266.c - ESP-01S (AT固件) 驱动
*
* 4WIFI和4G V3.4 WIFI无回弹HARDWARE/WIFI/wifi.c
* HAL USART2 UART4(PC10/PC11) PA5
*
* 4G U2_CopyBuffWiFi UART4 IDLE
* stm32f1xx_it.c UART4_IRQHandler network.c
* StopRx/StartRx
*/
#include "esp8266.h"
#include "air780e.h" /* U2_CopyBuff / bg_delay */
#include "uart.h"
#include "log.h"
#include "system.h"
#include "stdio.h"
#include "string.h"
#include <stdarg.h>
/*==== 接收缓冲操作 ====*/
void esp_rx_clear(void)
{
U2_CopyIndex = 0;
U2_CopyBuff[0] = 0;
U2_CopyFlag = 0;
}
/* 二进制安全的子串查找strstr 遇 0x00 会失明) */
static uint8_t *esp_memfind(uint8_t *hay, uint16_t hay_len, const char *needle)
{
uint16_t nlen = strlen(needle);
if (hay_len < nlen) return NULL;
for (uint16_t i = 0; i <= hay_len - nlen; i++) {
if (memcmp(hay + i, needle, nlen) == 0) return hay + i;
}
return NULL;
}
/* 等待接收缓冲中出现关键字,期间切片喂狗/跑后台任务。
* "ERROR" 0= */
static int esp_wait(const char *expect, int timeout_ms)
{
int t = timeout_ms / 100;
while (t-- > 0) {
bg_delay(100);
if (U2_CopyFlag) {
if (esp_memfind(U2_CopyBuff, U2_CopyIndex, expect)) return 0;
if (expect[0] != 'E' && esp_memfind(U2_CopyBuff, U2_CopyIndex, "ERROR")) return -1;
}
}
return -1;
}
/* 带倒计时日志的长等待:每秒打印一次剩余秒数,便于观察进度。
* "ERROR"/"FAIL" 0= */
static int esp_wait_countdown(const char *expect, int timeout_s)
{
int remain = timeout_s * 10; /* 100ms 计数 */
int sec = timeout_s;
while (remain-- > 0) {
bg_delay(100);
if (U2_CopyFlag) {
if (esp_memfind(U2_CopyBuff, U2_CopyIndex, expect)) {
log_info("> WiFi: wait done (%ds left)", sec);
return 0;
}
if (esp_memfind(U2_CopyBuff, U2_CopyIndex, "ERROR") ||
esp_memfind(U2_CopyBuff, U2_CopyIndex, "FAIL")) {
return -1;
}
}
if (remain % 10 == 0 && sec > 0) {
log_info("> WiFi: countdown %ds", sec--);
}
}
return -1;
}
/*==== 透传/AT 数据发送UART4末尾自动加\r\n ====*/
static void ESP_printf(const char *fmt, ...)
{
static uint32_t last_tx_tick = 0;
char buf[256];
va_list ap;
/* 任意两条 AT 指令之间至少间隔 30ms与 4G 侧一致 */
while (HAL_GetTick() - last_tx_tick < 30) {
HAL_Delay(1);
}
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
HAL_UART_Transmit(&huart4, (uint8_t *)buf, strlen(buf), 500);
HAL_UART_Transmit(&huart4, (uint8_t *)"\r\n", 2, 500);
last_tx_tick = HAL_GetTick();
}
/* 透传模式原始数据发送MQTT 二进制报文,不加\r\n */
void esp_send(const uint8_t *data, uint16_t len)
{
HAL_UART_Transmit(&huart4, (uint8_t *)data, len, 1000);
}
/*==== GPIO ====*/
void esp_gpio_init(void)
{
GPIO_InitTypeDef g = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
ESP_RST_HIGH(); /* 默认拉高,模块正常运行 */
g.Pin = ESP_RST_PIN;
g.Mode = GPIO_MODE_OUTPUT_PP;
g.Pull = GPIO_NOPULL;
g.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(ESP_RST_PORT, &g);
ESP_RST_HIGH();
}
/*==== 硬件复位 + 波特率同步 ====
* 500ms 115200 /RESTORE
* AT+UART_DEF WIFI_BAUD_TARGET
* 使 */
int esp_reset(void)
{
log_info("> WiFi: ESP reset...");
/* 关闭中断/DMA防止模块复位期间乱码触发中断风暴同 4G cat_reset 思路) */
HAL_NVIC_DisableIRQ(UART4_IRQn);
HAL_NVIC_DisableIRQ(DMA2_Channel3_IRQn);
HAL_UART_DMAStop(&huart4);
DMA2_Channel3->CCR &= ~DMA_CCR_EN;
while (DMA2_Channel3->CCR & DMA_CCR_EN);
DMA2->IFCR = DMA_IFCR_CGIF3;
__HAL_UART_CLEAR_IDLEFLAG(&huart4);
__HAL_UART_CLEAR_OREFLAG(&huart4);
__HAL_UART_CLEAR_PEFLAG(&huart4);
__HAL_UART_CLEAR_FEFLAG(&huart4);
__HAL_UART_CLEAR_NEFLAG(&huart4);
esp_rx_clear();
ESP_RST_LOW();
bg_delay(500);
ESP_RST_HIGH();
bg_delay(1500); /* 等模块完成启动boot log 输出完毕) */
/* 复位完成后重新启动 DMA+IDLE 接收 */
esp_rx_clear();
UART4_SetBaudRate(WIFI_BAUD_DEFAULT);
HAL_NVIC_EnableIRQ(DMA2_Channel3_IRQn);
HAL_NVIC_EnableIRQ(UART4_IRQn);
/* 先按 115200 探测 */
if (esp_send_cmd("AT", "OK", 1500) == 0) {
/* 通了固定为目标波特率AT+UART_DEF 掉电保存) */
if (esp_send_cmd("AT+UART_DEF=921600,8,1,0,0", "OK", 3000) == 0) {
UART4_SetBaudRate(WIFI_BAUD_TARGET);
bg_delay(200);
if (esp_send_cmd("AT", "OK", 1500) == 0) {
log_info("> WiFi: baud -> 921600");
return 0;
}
/* 目标波特率通信失败:回滚为 115200维持可用 */
log_warn("> WiFi: 921600 unusable, rollback to 115200");
UART4_SetBaudRate(WIFI_BAUD_DEFAULT);
bg_delay(100);
esp_send_cmd("AT+UART_DEF=115200,8,1,0,0", "OK", 2000);
return 0;
}
/* 固件不支持 UART_DEF 等:维持 115200 也能用 */
log_warn("> WiFi: UART_DEF failed, stay 115200");
return 0;
}
/* 115200 不通:模块可能已是目标波特率 */
UART4_SetBaudRate(WIFI_BAUD_TARGET);
if (esp_send_cmd("AT", "OK", 1500) == 0) {
log_info("> WiFi: baud already 921600");
return 0;
}
log_warn("> WiFi: no AT reply at any baud");
UART4_SetBaudRate(WIFI_BAUD_DEFAULT);
return -1;
}
/*==== 通用 AT 指令:发送→清缓冲→等关键字 ====*/
/* 接收缓冲十六进制转储诊断用strstr 遇 0x00 会失明,
* */
static void esp_dump_rx(const char *tag)
{
char hex[3 * 40 + 1];
int n = 0;
uint16_t len = U2_CopyIndex < 40 ? U2_CopyIndex : 40;
for (uint16_t i = 0; i < len; i++) {
n += snprintf(hex + n, sizeof(hex) - n, "%02X ", U2_CopyBuff[i]);
}
if (len == 0) n = snprintf(hex, sizeof(hex), "(empty)");
log_warn("> WiFi rx[%s] idx=%u: %s", tag, U2_CopyIndex, hex);
}
/*==== 通用 AT 指令:发送→清缓冲→等关键字 ====
* CWJAP
* "WIFI GOT IP" OK esp_wait
* "假成功" AT+CIPMODE=1 OK
* DMA+IDLE
* Rx_Buf IDLE U2_CopyBuff */
int esp_send_cmd(const char *cmd, const char *expect, int timeout_ms)
{
ESP_printf("%s", cmd);
esp_rx_clear();
if (esp_wait(expect, timeout_ms) != 0) {
log_warn("> WiFi AT t/o: %s", cmd);
esp_dump_rx(cmd);
return -1;
}
return 0;
}
/*==== 等待模块自动连接已保存的热点SmartConfig/历史CWJAP保存的AP返回 0=拿到IP ====*/
int esp_wait_got_ip(int timeout_s)
{
log_info("> WiFi: wait saved AP auto-connect (%ds)", timeout_s);
if (esp_wait_countdown("WIFI GOT IP", timeout_s) != 0) {
log_warn("> WiFi: no saved AP auto-connect");
return -1;
}
log_info("> WiFi: saved AP joined");
return 0;
}
/*==== 连接固定 WiFi代码里保存的 SSID/PASS60s 超时 ====*/
int esp_join_fixed_ap(void)
{
char cmd[96];
log_info("> WiFi: join fixed AP '%s' (%ds)", WIFI_SSID, WIFI_JOIN_TIMEOUT_S);
/* 保持 CWAUTOCONN=1默认连接成功的AP会被模块保存
* esp_wait_got_ip */
snprintf(cmd, sizeof(cmd), "AT+CWJAP=\"%s\",\"%s\"", WIFI_SSID, WIFI_PASS);
ESP_printf("%s", cmd);
esp_rx_clear();
if (esp_wait_countdown("WIFI GOT IP", WIFI_JOIN_TIMEOUT_S) != 0) {
log_warn("> WiFi: fixed AP join timeout");
return -1;
}
log_info("> WiFi: fixed AP joined");
return 0;
}
/*==== Smart Config (ESP-Touch) 智能配网60s 倒计时 ====
* APP(ESP-Touch) 广 WiFi AP
* AT+CWJAP */
int esp_smartconfig(void)
{
log_info("> WiFi: SmartConfig start (%ds), use ESP-Touch APP", WIFI_SMART_TIMEOUT_S);
if (esp_send_cmd("AT+CWSTARTSMART", "OK", 5000) != 0) return -1;
esp_rx_clear();
if (esp_wait_countdown("connected", WIFI_SMART_TIMEOUT_S) != 0) {
log_warn("> WiFi: SmartConfig timeout");
esp_send_cmd("AT+CWSTOPSMART", "OK", 2000);
return -1;
}
log_info("> WiFi: SmartConfig got AP");
esp_send_cmd("AT+CWSTOPSMART", "OK", 2000);
/* 配网成功后模块自动连接热点,等拿到 IP */
if (esp_wait("WIFI GOT IP", 10000) != 0) {
log_warn("> WiFi: SmartConfig no IP");
return -1;
}
log_info("> WiFi: SmartConfig joined AP");
return 0;
}
/*==== 退出透传模式 ====
* "+++" 1s guard time \r\n
* TCP esp_reset */
int esp_exit_transparent(void)
{
bg_delay(1100);
esp_send((uint8_t *)"+++", 3);
bg_delay(1100);
if (esp_send_cmd("AT", "OK", 2000) != 0) {
log_warn("> WiFi: exit transparent no AT reply");
return -1;
}
esp_send_cmd("AT+CIPCLOSE", "OK", 2000);
log_info("> WiFi: exit transparent OK");
return 0;
}
/*==== 恢复出厂设置AT+RESTORE模块忘掉已保存的热点用于测试 SmartConfig====
* 1RESTORE 115200
* UART4 115200
* 2WiFi TCP AT TCP
* "+++" 退 */
int esp_factory_restore(void)
{
log_info("> WiFi: AT+RESTORE (forget saved AP)");
esp_exit_transparent(); /* 透传中先退回命令态;非透传时调用无害 */
esp_rx_clear();
ESP_printf("AT+RESTORE");
bg_delay(2500); /* 等模块执行 RESTORE 并完成重启 */
UART4_SetBaudRate(WIFI_BAUD_DEFAULT);
if (esp_send_cmd("AT", "OK", 2000) != 0) {
log_warn("> WiFi: RESTORE no reply after baud reset");
return -1;
}
log_info("> WiFi: RESTORE done, saved AP forgotten");
return 0;
}
/*==== 连接 TCP 服务器并进入透传模式 ====*/
int esp_enter_transparent(const char *ip, uint16_t port)
{
char cmd[96];
log_info("> WiFi: enter transparent %s:%u", ip, (unsigned)port);
/* 整体可重试一次CIPSEND 收到 ERROR 多为 CIPMODE 未生效或连接状态残留 */
for (int attempt = 0; attempt < 2; attempt++) {
if (attempt > 0) {
log_warn("> WiFi: transparent retry");
bg_delay(1000);
}
/* 先清掉可能残留的旧 TCP 连接(上一轮失败没关的连接会让
* CIPMODE/CIPMUX "link is builded" ERROR
* CIPCLOSE ERROR */
esp_send_cmd("AT+CIPCLOSE", "OK", 1500);
/* CIPMUX=0 必须在 CIPMODE=1 之前:透传模式只在单连接下可设置 */
if (esp_send_cmd("AT+CIPMUX=0", "OK", 2000) != 0) continue;
if (esp_send_cmd("AT+CIPMODE=1", "OK", 2000) != 0) continue;
snprintf(cmd, sizeof(cmd), "AT+CIPSTART=\"TCP\",\"%s\",%u", ip, (unsigned)port);
ESP_printf("%s", cmd);
esp_rx_clear();
if (esp_wait("CONNECT", WIFI_TCP_TIMEOUT_MS) != 0) {
log_warn("> WiFi: TCP connect failed");
esp_dump_rx("CIPSTART");
continue;
}
log_info("> WiFi: TCP connected, rx: %.100s", U2_CopyBuff);
/* 等 CIPSTART 的 OK 收尾(先匹配到 CONNECT 时 OK 可能还没回来),
* busy CIPSEND */
esp_wait("OK", 2000);
bg_delay(300);
/* 进透传:等 ">" */
ESP_printf("AT+CIPSEND");
esp_rx_clear();
if (esp_wait(">", 10000) == 0) {
esp_rx_clear();
log_info("> WiFi: transparent mode OK");
return 0;
}
log_warn("> WiFi: CIPSEND no '>'");
esp_dump_rx("CIPSEND");
/* 发 AT 探测:有 OK=仍在命令态;无应答=固件已自动进透传 */
ESP_printf("AT");
esp_rx_clear();
if (esp_wait("OK", 2000) != 0) {
log_warn("> WiFi: no AT reply, assume already transparent");
esp_rx_clear();
return 0;
}
/* 仍在命令态:查 CIPMODE 实际值做诊断 */
if (esp_send_cmd("AT+CIPMODE?", "+CIPMODE:1", 2000) == 0) {
log_warn("> WiFi: CIPMODE still 1, link may be closed by server");
} else {
log_warn("> WiFi: CIPMODE reverted or never set");
}
}
log_warn("> WiFi: enter transparent failed");
return -1;
}