157 lines
5.3 KiB
C
157 lines
5.3 KiB
C
/* net_wifi.c - WiFi 后端(网络层下的通道实现之一)
|
||
*
|
||
* 组成:esp8266.c (ESP-01S AT驱动/TCP透传) + mqtt_client.c (软件MQTT 3.1.1)。
|
||
* 连接顺序:硬件复位 → 等已存AP自动连接(20s) → 连固定SSID(60s) →
|
||
* Smart Config 智能配网(60s) → 失败返回,由 network.c 切回 4G。
|
||
* 运行阶段:mqttc_process 解析下行报文,30s 心跳保活,
|
||
* 断链(CLOSED/心跳超时) 清 ready,由 network.c 调度重连或切回 4G。
|
||
*/
|
||
|
||
#include "net_wifi.h"
|
||
#include "esp8266.h"
|
||
#include "mqtt_client.h"
|
||
#include "network.h"
|
||
#include "air780e.h" /* bg_delay */
|
||
#include "uart.h"
|
||
#include "log.h"
|
||
#include "stdio.h"
|
||
#include "string.h"
|
||
|
||
volatile uint8_t g_wifi_mqtt_ready = 0;
|
||
volatile uint8_t g_wifi_first_publish_ok = 0;
|
||
|
||
/* WiFi 模块恢复出厂请求标志:由蓝牙 RECOVERY 命令(sys任务)置位,
|
||
* net 任务在 net_wifi_process 里执行——所有 UART4 操作都收口在 net 任务,
|
||
* 避免与 4G 切换/建链并发冲突 */
|
||
volatile uint8_t g_wifi_restore_req = 0;
|
||
|
||
static uint32_t s_ping_tick = 0;
|
||
|
||
/* 上行发布主题暂存 */
|
||
static char s_topic_up[48];
|
||
|
||
/*==== 阻塞建链:成功返回 0 并置 g_wifi_mqtt_ready ====
|
||
* 子顺序由 EEPROM 记忆的"上次成功联网方式"决定:
|
||
* 上次是配网热点 → 先查已存 AP 自动连接(CIPSTATUS, 10s)
|
||
* 然后统一:固定 SSID(60s 倒计时) → SmartConfig(60s 倒计时)
|
||
* 成功后按实际使用的方式更新 EEPROM 记忆(NET_SaveMode) */
|
||
int net_wifi_init(void)
|
||
{
|
||
log_info("> WiFi: init...");
|
||
|
||
g_wifi_mqtt_ready = 0;
|
||
g_wifi_first_publish_ok = 0;
|
||
|
||
esp_gpio_init();
|
||
UART4_StartRx();
|
||
|
||
if (esp_reset() != 0) return -1;
|
||
if (esp_send_cmd("AT", "OK", 2000) != 0) return -1;
|
||
esp_send_cmd("ATE0", "OK", 1000); /* 关回显,失败不致命 */
|
||
if (esp_send_cmd("AT+CWMODE=1", "OK", 2000) != 0) return -1;
|
||
|
||
int joined = 0;
|
||
uint8_t via_smart = 0; /* 1=本次用的是配网热点(已存AP或SmartConfig) */
|
||
|
||
/* 上次是配网热点成功的:模块里存着该热点密码,优先查自动连接 */
|
||
if (NET_GetLastMode() == NET_MODE_WIFI_SMART) {
|
||
if (esp_saved_ap_connected(10) == 0) {
|
||
joined = 1;
|
||
via_smart = 1;
|
||
}
|
||
}
|
||
|
||
/* 连代码里固定的 SSID/PASS */
|
||
if (!joined && esp_join_fixed_ap() == 0) {
|
||
joined = 1;
|
||
via_smart = 0;
|
||
}
|
||
|
||
/* 固定热点连不上:进 SmartConfig 智能配网(ESP-Touch APP 广播密码) */
|
||
if (!joined) {
|
||
if (esp_smartconfig() == 0) {
|
||
joined = 1;
|
||
via_smart = 1;
|
||
} else {
|
||
log_warn("> WiFi: all AP attempts failed");
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
/* TCP 透传到 MQTT 服务器 */
|
||
if (esp_enter_transparent(MqttInfoStr.ServerIP, MqttInfoStr.ServerPort) != 0) return -1;
|
||
|
||
/* 软件 MQTT 建链 + 订阅下行主题 */
|
||
mqttc_init(MqttInfoStr.ClientID, MqttInfoStr.Username, MqttInfoStr.Passward);
|
||
if (mqttc_connect() != 0) return -1;
|
||
|
||
{
|
||
char topic_down[48];
|
||
snprintf(topic_down, sizeof(topic_down), "%s%s", MqttInfoStr.Topic, MqttInfoStr.ClientID);
|
||
if (mqttc_subscribe(topic_down) != 0) return -1;
|
||
snprintf(s_topic_up, sizeof(s_topic_up), "/iot/data/up/%s", MqttInfoStr.ClientID);
|
||
}
|
||
|
||
g_wifi_mqtt_ready = 1;
|
||
s_ping_tick = HAL_GetTick();
|
||
log_info("> WiFi: Ready");
|
||
|
||
/* 更新联网方式记忆(仅变化时写 EEPROM) */
|
||
NET_SaveMode(via_smart ? NET_MODE_WIFI_SMART : NET_MODE_WIFI_FIXED);
|
||
|
||
/* 就绪后上报一次电量,与 4G 就绪行为对齐(同时标记首次发布成功) */
|
||
NET_publish_power(1);
|
||
return 0;
|
||
}
|
||
|
||
/*==== 非阻塞轮询:下行报文解析 + 心跳保活 ====*/
|
||
void net_wifi_process(void)
|
||
{
|
||
/* 恢复出厂请求(蓝牙 RECOVERY):在 net 任务上下文执行,
|
||
* 不与本任务的建链/收发并发 */
|
||
if (g_wifi_restore_req) {
|
||
g_wifi_restore_req = 0;
|
||
log_warn("> WiFi: restore request, AT+RESTORE & reboot");
|
||
g_net_tx_busy = 1;
|
||
g_wifi_mqtt_ready = 0;
|
||
if (esp_factory_restore() != 0) {
|
||
log_warn("> WiFi: RESTORE failed, reboot anyway");
|
||
}
|
||
HAL_Delay(100);
|
||
NVIC_SystemReset();
|
||
}
|
||
|
||
if (!g_wifi_mqtt_ready) return;
|
||
|
||
int r;
|
||
while ((r = mqttc_process()) == 1) { }
|
||
if (r == -1) {
|
||
/* 收到 CLOSED:服务器主动断开 TCP(被平台踢下线),直接复位重连 */
|
||
log_error("> WiFi: connection closed by server (kicked offline by platform), system reset");
|
||
HAL_Delay(100);
|
||
NVIC_SystemReset();
|
||
}
|
||
|
||
/* 15s 一次 PINGREQ;上一帧 PINGREQ 未收到 PINGRESP 判断链 */
|
||
if (HAL_GetTick() - s_ping_tick >= 15000) {
|
||
if (g_mqttc_ping_outstanding) {
|
||
/* 心跳超时:服务器无响应(假在线/网络异常),直接复位重连 */
|
||
log_error("> WiFi: ping timeout (server no response), system reset");
|
||
HAL_Delay(100);
|
||
NVIC_SystemReset();
|
||
}
|
||
s_ping_tick = HAL_GetTick();
|
||
mqttc_ping();
|
||
}
|
||
}
|
||
|
||
/*==== 上行发布 ====*/
|
||
void net_wifi_publish_up(const char *json)
|
||
{
|
||
if (!g_wifi_mqtt_ready) return;
|
||
if (mqttc_publish(s_topic_up, json) == 0 && !g_wifi_first_publish_ok) {
|
||
g_wifi_first_publish_ok = 1;
|
||
log_info("> WiFi: first publish ok");
|
||
}
|
||
}
|