Răsfoiți Sursa

AP热点配网,成功

yidong 1 lună în urmă
părinte
comite
a5bc09935f

+ 1 - 1
Core/Src/main.c

@@ -271,7 +271,7 @@ int main(void)
 	PackDeviceNum();
 
 #ifdef MINI7_V2_DEBUG   
-	sysinf.log_level =  VAN_LOG_ERROR|VAN_LOG_INFO|VAN_LOG_TEMP|VAN_LOG_WIFI;
+//	sysinf.log_level =  VAN_LOG_ERROR|VAN_LOG_INFO|VAN_LOG_TEMP|VAN_LOG_WIFI;
 #endif	
   while (1)
   {

+ 1 - 1
Core/Src/tim.c

@@ -219,7 +219,7 @@ void MX_TIM4_Init(void)
 
   /* USER CODE END TIM4_Init 1 */
   htim4.Instance = TIM4;
-  htim4.Init.Prescaler = 0;
+  htim4.Init.Prescaler = 127;
   htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
   htim4.Init.Period = 65535;
   htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

+ 636 - 0
DriverInterface/ESP32_WROOM_32D.c

@@ -12,6 +12,7 @@ extern uint8_t Devicenum;
 extern uint8_t g_wifi_rx_buf[WIFI_RX_BUF_SIZE];
 extern uint32_t g_wifi_rx_last;
 extern uint32_t g_wifi_rx_head;
+extern uint32_t g_wifi_rx_last_tick;   /* 最近一次收到ESP数据的时间戳(10ms单位) */
 uint8_t wifi_tx_buf[WIFI_TX_BUF_SIZE];
 uint16_t wifi_tx_len;
 
@@ -741,10 +742,645 @@ void WIFI_send_deal(void)
 
 }
 
+/* ========================================================================
+ * 热点配网(SoftAP WiFi配网)状态机
+ *  触发 : 按键长按30s -> WIFI_provision_request() (usr_motor.c)
+ *  流程 : 复位ESP32 -> AP模式(AT+CWMODE=2) -> 建热点
+ *         OneChip_Mini7 / test1234 (192.168.4.1/255.255.255.0)
+ *         -> AT+CIPAP? 查询热点IP -> AT+CWDHCP? 查询DHCP状态
+ *         -> AT+CIPMUX=1 -> AT+CIPSERVER=1,8002 启动TCP服务
+ *         -> 手机连接热点,经TCP 8002下发:
+ *            {"ssid":"Onechip_602","password":"onchip1003"}
+ *         -> 解析JSON, ssid->sysinf.wifiname, password->sysinf.password
+ *         -> cfg_save_cfg_param() 保存到Flash
+ *         -> 复位ESP32进入正常STA模式(按新配置自动连接)
+ *  说明 : 热点地址192.168.4.1/255.255.255.0为ESP32 SoftAP默认值,无需配置
+ * ===================================================================== */
+#define WIFI_PROV_AP_SSID        "OneChip_Mini7"   /* 热点名称 */
+#define WIFI_PROV_AP_PWD         "test1234"        /* 热点密码 */
+#define WIFI_PROV_AP_CHANNEL     6                 /* 信道 */
+#define WIFI_PROV_AP_ECN         3                 /* 加密:3=WPA2_PSK */
+#define WIFI_PROV_TCP_PORT       8002              /* TCP服务端口 */
+
+#define WIFI_PROV_CMD_TIMEOUT    500               /* 单条AT指令超时:5s(10ms) */
+#define WIFI_PROV_RST_TIMEOUT    1000              /* AT+RST等待ready:10s */
+#define WIFI_PROV_LISTEN_TIMEOUT 30000             /* 等待配网数据总超时:300s */
+#define WIFI_PROV_MAX_RETRY      3                 /* 指令失败重试次数 */
+#define WIFI_PROV_RX_IDLE_TIMEOUT 100               /* 无有效IPD残留数据清空时限:1s(10ms) */
+
+typedef enum
+{
+    WIFI_PROV_IDLE = 0,   /* 空闲 */
+    WIFI_PROV_START,      /* 启动初始化 */
+    WIFI_PROV_RST,        /* 复位ESP32,重启进入AP主机模式 */
+    WIFI_PROV_WAIT_READY, /* 等待模块重启完成 */
+    WIFI_PROV_MODE,       /* AT+CWMODE=2 设置AP模式 */
+    WIFI_PROV_SAP,        /* AT+CWSAP 建立热点 */
+    WIFI_PROV_IP_Q,       /* AT+CIPAP? 查询SoftAP接口IP(默认192.168.4.1) */
+    WIFI_PROV_DHCP_Q,     /* AT+CWDHCP? 查询DHCP使能状态 */
+    WIFI_PROV_MUX,        /* AT+CIPMUX=1 打开多连接 */
+    WIFI_PROV_SERVER,     /* AT+CIPSERVER=1,8002 启动TCP服务 */
+    WIFI_PROV_LISTEN,     /* 等待手机下发配网JSON */
+    WIFI_PROV_ACK_SEND,   /* AT+CIPSEND 向手机应答 */
+    WIFI_PROV_ACK_DATA,   /* 等待'>'并发送应答数据 */
+    WIFI_PROV_SAVE,       /* 保存配置到Flash */
+    WIFI_PROV_DONE,       /* 配网完成,复位进入正常模式 */
+} wifi_prov_state_t;
+
+static volatile uint8_t wifi_prov_request = 0;  /* 配网请求标志(可被ISR置位) */
+static uint8_t  wifi_prov_active  = 0;         /* 配网进行中 */
+static uint8_t  wifi_prov_state   = WIFI_PROV_IDLE;
+static uint8_t  wifi_prov_retry   = 0;
+static uint8_t  wifi_prov_wait    = 0;         /* 1=正在等待应答 */
+static uint8_t  wifi_prov_json_ok = 0;         /* 解析到的配置是否有效 */
+static uint8_t  wifi_prov_linkid  = 0;         /* TCP链路ID */
+static uint32_t wifi_prov_tick    = 0;         /* 起始时间戳(10ms单位) */
+static uint32_t wifi_prov_timeout = 0;         /* 等待超时(10ms单位) */
+static char     wifi_prov_ipd[WIFI_RX_BUF_SIZE];/* +IPD载荷累积缓存(JSON) */
+static uint16_t wifi_prov_ipd_len = 0;
+static char     wifi_prov_ack[WIFI_RX_BUF_SIZE];/* 应答JSON */
+
+static void wifi_prov_enter(void);
+static void wifi_prov_exit(void);
+static void wifi_prov_abort(void);
+
+/* 配网进行中? */
+uint8_t WIFI_provision_active(void)
+{
+    return wifi_prov_active;
+}
+
+/* 请求进入热点配网模式(按键长按30s调用,允许在中断中调用) */
+uint8_t WIFI_provision_request(void)
+{
+    if (wifi_prov_active != 0)
+        return 0;   /* 已在配网中 */
+    wifi_prov_request = 1;
+    return 1;
+}
+
+/* 配网状态机内部发送AT指令 */
+static void wifi_prov_send_cmd(const char *cmd, uint32_t timeout10ms)
+{
+    char buff[128] = {0};
+
+    strcpy(buff, cmd);
+    strcat(buff, "\r\n");
+    Van_Device_Printf(VAN_LOG_WIFI, "\nP--> %s", buff);
+    WriteComm2(buff, strlen(buff));
+
+    wifi_prov_wait    = 1;
+    wifi_prov_tick    = sysinf.conter10ms;
+    wifi_prov_timeout = timeout10ms;
+}
+
+/* 从ESP接收环中提取一个完整的+IPD报文载荷并追加到累积缓存
+ * 返回: 1=取到完整报文; 0=暂无完整报文(继续等待);
+ *      -1=帧头/载荷异常(不打印、不清空、不消费,继续等待直到总超时退出) */
+static int wifi_prov_capture_ipd(void)
+{
+    uint32_t head  = g_wifi_rx_head;
+    uint32_t last  = g_wifi_rx_last;
+    uint32_t avail = (last + WIFI_RX_BUF_SIZE - head) % WIFI_RX_BUF_SIZE;
+    int pos = -1;
+    uint32_t i;
+
+    if (avail == 0)
+        return 0;
+
+    /* 查找"+IPD,"头 */
+    for (i = 0; (i + 5) <= avail; i++)
+    {
+        uint32_t p = (head + i) % WIFI_RX_BUF_SIZE;
+        if (g_wifi_rx_buf[p] == '+' &&
+            g_wifi_rx_buf[(p + 1) % WIFI_RX_BUF_SIZE] == 'I' &&
+            g_wifi_rx_buf[(p + 2) % WIFI_RX_BUF_SIZE] == 'P' &&
+            g_wifi_rx_buf[(p + 3) % WIFI_RX_BUF_SIZE] == 'D' &&
+            g_wifi_rx_buf[(p + 4) % WIFI_RX_BUF_SIZE] == ',')
+        {
+            pos = (int)i;
+            break;
+        }
+    }
+    if (pos < 0)
+        return 0;   /* 缓冲内无+IPD报文 */
+
+    i = (uint32_t)(pos + 5);
+    if (i >= avail)
+        return 0;   /* 帧头不完整,继续等待 */
+
+    /* 解析头: +IPD,<linkID>,<len>: */
+    if (g_wifi_rx_buf[(head + i) % WIFI_RX_BUF_SIZE] < '0' ||
+        g_wifi_rx_buf[(head + i) % WIFI_RX_BUF_SIZE] > '9')
+        return -1;   /* linkid非数字,帧损坏 */
+    {
+        uint32_t linkid = 0;
+        while (i < avail &&
+               g_wifi_rx_buf[(head + i) % WIFI_RX_BUF_SIZE] >= '0' &&
+               g_wifi_rx_buf[(head + i) % WIFI_RX_BUF_SIZE] <= '9')
+        {
+            linkid = linkid * 10 + (uint32_t)(g_wifi_rx_buf[(head + i) % WIFI_RX_BUF_SIZE] - '0');
+            i++;
+        }
+        if (i >= avail)
+            return 0;   /* linkid未完,继续等待 */
+        if (g_wifi_rx_buf[(head + i) % WIFI_RX_BUF_SIZE] != ',')
+            return -1;   /* 缺少逗号分隔,帧损坏 */
+        wifi_prov_linkid = (uint8_t)linkid;
+        i++;
+        if (i >= avail)
+            return 0;
+        if (g_wifi_rx_buf[(head + i) % WIFI_RX_BUF_SIZE] < '0' ||
+            g_wifi_rx_buf[(head + i) % WIFI_RX_BUF_SIZE] > '9')
+            return -1;   /* 长度非数字,帧损坏 */
+        {
+            uint32_t pktlen = 0;
+            while (i < avail &&
+                   g_wifi_rx_buf[(head + i) % WIFI_RX_BUF_SIZE] >= '0' &&
+                   g_wifi_rx_buf[(head + i) % WIFI_RX_BUF_SIZE] <= '9')
+            {
+                pktlen = pktlen * 10 + (uint32_t)(g_wifi_rx_buf[(head + i) % WIFI_RX_BUF_SIZE] - '0');
+                i++;
+            }
+            if (i >= avail)
+                return 0;   /* len未完,继续等待 */
+            if (g_wifi_rx_buf[(head + i) % WIFI_RX_BUF_SIZE] != ':')
+                return -1;   /* 缺少冒号分隔,帧损坏 */
+            i++;    /* 跳过 ':' */
+            if (pktlen == 0)
+                return -1;   /* 空载荷异常 */
+            if (avail < i + pktlen)
+                return 0;   /* 载荷尚未收完,继续等待 */
+
+            if ((uint16_t)(wifi_prov_ipd_len + pktlen) < sizeof(wifi_prov_ipd))
+            {
+                uint32_t k;
+                for (k = 0; k < pktlen; k++)
+                {
+                    char c = (char)g_wifi_rx_buf[(head + i + k) % WIFI_RX_BUF_SIZE];
+                    if (c == '\r' || c == '\n')  /* 去掉行结束符 */
+                        continue;
+                    wifi_prov_ipd[wifi_prov_ipd_len++] = c;
+                }
+                wifi_prov_ipd[wifi_prov_ipd_len] = '\0';
+            }
+            else
+            {
+                /* 载荷超限无法累积: 丢弃该帧载荷,重新接收 */
+                wifi_prov_ipd_len = 0;
+                wifi_prov_ipd[0]  = '\0';
+            }
+            /* 消费该报文 */
+            g_wifi_rx_head = (head + i + pktlen) % WIFI_RX_BUF_SIZE;
+            return 1;
+        }
+    }
+}
+
+/* 解析配网JSON: {"ssid":"xxx","password":"xxx"}
+ * 返回: 0=成功; -1=失败 */
+static int wifi_prov_parse_json(const char *json, char *ssid, uint16_t ssidCap,
+                                char *pwd, uint16_t pwdCap)
+{
+    cJSON *root = NULL;
+    cJSON *it   = NULL;
+    int rlt     = -1;
+
+    if (json == NULL || json[0] != '{' || ssid == NULL || pwd == NULL)
+        return -1;
+    ssid[0] = '\0';
+    pwd[0]  = '\0';
+
+    root = cJSON_Parse(json);
+    if (root == NULL)
+        return -1;
+
+    it = cJSON_GetObjectItem(root, "ssid");
+    if (it == NULL || !cJSON_IsString(it) || it->valuestring == NULL ||
+        it->valuestring[0] == '\0')
+        goto out;   /* 必须包含非空ssid */
+    if (strlen(it->valuestring) >= (size_t)ssidCap)
+        goto out;   /* ssid超长 */
+    strcpy(ssid, it->valuestring);
+
+    it = cJSON_GetObjectItem(root, "password");
+    if (it != NULL && cJSON_IsString(it) && it->valuestring != NULL)
+    {
+        if (strlen(it->valuestring) >= (size_t)pwdCap)
+            goto out;   /* 密码超长 */
+        strcpy(pwd, it->valuestring);   /* 允许开放网络密码为空 */
+    }
+    rlt = 0;
+
+out:
+    cJSON_Delete(root);
+    return rlt;
+}
+
+/* 生成应答JSON */
+static void wifi_prov_build_ack(uint8_t accepted, const char *reason)
+{
+    if (accepted)
+    {
+        my_sprintf(wifi_prov_ack, sizeof(wifi_prov_ack),
+                   "{\"v\":1,\"status\":\"accepted\",\"sn\":\"%s\"}\r\n",
+                   sysinf.SN);
+    }
+    else
+    {
+        if (reason == NULL)
+            reason = "invalid";
+        my_sprintf(wifi_prov_ack, sizeof(wifi_prov_ack),
+                   "{\"v\":1,\"status\":\"rejected\",\"reason\":\"%s\"}\r\n",
+                   reason);
+    }
+}
+
+/* 进入热点配网模式 */
+static void wifi_prov_enter(void)
+{
+    wifi_prov_active  = 1;
+    wifi_prov_state   = WIFI_PROV_START;
+    wifi_prov_retry   = 0;
+    wifi_prov_wait    = 0;
+    wifi_prov_linkid  = 0;
+    wifi_prov_json_ok = 0;
+    wifi_prov_ipd_len = 0;
+    wifi_prov_ipd[0]  = '\0';
+
+    memset(wifi_tx_buf, 0, sizeof(wifi_tx_buf));
+    wifi_tx_len    = 0;
+    wifi_ack_delay = 0;
+    wifi_state     = WIFI_POWEROFF;   /* 暂停正常wifi流程 */
+    wifi_receive_clear();
+
+    Van_Device_Printf(VAN_LOG_WIFI,
+                      "\r\n[Provision] enter, AP=%s pwd=%s port=%d",
+                      WIFI_PROV_AP_SSID, WIFI_PROV_AP_PWD, WIFI_PROV_TCP_PORT);
+    beep_set(BEEP_SCANOK);   /* 提示进入配网 */
+}
+
+/* 退出热点配网,回到正常STA流程 */
+static void wifi_prov_exit(void)
+{
+    wifi_prov_active  = 0;
+    wifi_prov_request = 0;
+    wifi_prov_state   = WIFI_PROV_IDLE;
+    wifi_prov_wait    = 0;
+    wifi_prov_ipd_len = 0;
+    wifi_prov_ipd[0]  = '\0';
+
+    memset(wifi_tx_buf, 0, sizeof(wifi_tx_buf));
+    wifi_tx_len    = 0;
+    wifi_ack_delay = 0;
+    wifi_receive_clear();
+
+    /* 交给正常流程:WIFI_POWEROFF会先AT+RST复位模块,再按新配置重连 */
+    WIFI_set_ssid();
+    Van_Device_Printf(VAN_LOG_WIFI, "\r\n[Provision] exit to normal STA");
+}
+
+/* 配网失败,恢复到正常STA模式 */
+static void wifi_prov_abort(void)
+{
+    Van_Device_Printf(VAN_LOG_WIFI, "\r\n[Provision] abort, exit to normal STA");
+    wifi_prov_exit();
+}
+
+/* 检查AT指令应答;收到OK后切到okState;出错则重试,超过次数退出配网
+ * 返回: 1=收到OK并已切状态; 0=仍在等待; -1=失败(已重发或已中止) */
+static int wifi_prov_check_cmd_resp(uint8_t okState)
+{
+    if (NULL != find_str_loop(g_wifi_rx_buf, WIFI_RX_BUF_SIZE, &g_wifi_rx_head,
+                              g_wifi_rx_last, "OK\r\n"))
+    {
+        wifi_receive_clear();
+        wifi_prov_wait  = 0;
+        wifi_prov_retry = 0;
+        wifi_prov_state = okState;
+        return 1;
+    }
+    if (NULL != find_str_loop(g_wifi_rx_buf, WIFI_RX_BUF_SIZE, &g_wifi_rx_head,
+                              g_wifi_rx_last, "ERROR") ||
+        (uint32_t)(sysinf.conter10ms - wifi_prov_tick) >= wifi_prov_timeout)
+    {
+        wifi_receive_clear();
+        wifi_prov_wait = 0;
+        if (++wifi_prov_retry < WIFI_PROV_MAX_RETRY)
+            return -1;   /* 由状态机重发指令 */
+        wifi_prov_abort();
+        return -1;
+    }
+    return 0;
+}
+
+/* 查询类AT指令处理: 发送AT+CIPAP?/AT+CWDHCP?后等待回复,收到OK(或超时)后
+ * 将完整应答打印到调试日志并切到okState状态
+ * 返回: 0=仍在等待; 1=已收到回复并切状态 */
+static uint8_t wifi_prov_query_done(uint8_t okState)
+{
+    char tmp[WIFI_RX_BUF_SIZE];
+    uint32_t head = g_wifi_rx_head;
+    uint32_t last = g_wifi_rx_last;
+    uint32_t n = 0;
+
+    if (wifi_prov_wait == 0)
+        return 0;
+
+    /* 线性化接收缓冲,便于查找与打印 */
+    while (head != last && n < (uint32_t)(WIFI_RX_BUF_SIZE - 1))
+    {
+        tmp[n++] = (char)g_wifi_rx_buf[head];
+        head = (head + 1) % WIFI_RX_BUF_SIZE;
+    }
+    tmp[n] = '\0';
+
+    if (strstr(tmp, "OK") != NULL ||
+        (uint32_t)(sysinf.conter10ms - wifi_prov_tick) >= wifi_prov_timeout)
+    {
+        /* 打印查询结果(含OK结尾),确认热点IP与DHCP状态 */
+        Van_Device_Printf(VAN_LOG_WIFI, "\r\n[Provision] <-%s", tmp);
+        wifi_receive_clear();
+        wifi_prov_wait  = 0;
+        wifi_prov_retry = 0;
+        wifi_prov_state = okState;
+        return 1;
+    }
+    return 0;
+}
+
+/* 热点配网状态机主处理(每次进入WIFI_process调用) */
+static void wifi_prov_process(void)
+{
+    switch (wifi_prov_state)
+    {
+    case WIFI_PROV_IDLE:
+    default:
+        break;
+
+    /* 启动初始化 */
+    case WIFI_PROV_START:
+        wifi_receive_clear();
+        wifi_prov_ipd_len = 0;
+        wifi_prov_ipd[0]  = '\0';
+        wifi_prov_wait    = 0;
+        wifi_prov_state   = WIFI_PROV_RST;
+        break;
+
+    /* 1. 复位ESP32,重启进入AP主机模式 */
+    case WIFI_PROV_RST:
+        wifi_prov_send_cmd("AT+RST", WIFI_PROV_RST_TIMEOUT);
+        wifi_prov_state   = WIFI_PROV_WAIT_READY;
+        wifi_prov_retry   = 0;
+        break;
+
+    case WIFI_PROV_WAIT_READY:
+        if (NULL != find_str_loop(g_wifi_rx_buf, WIFI_RX_BUF_SIZE, &g_wifi_rx_head,
+                                  g_wifi_rx_last, "ready") ||
+            NULL != find_str_loop(g_wifi_rx_buf, WIFI_RX_BUF_SIZE, &g_wifi_rx_head,
+                                  g_wifi_rx_last, "boot"))
+        {
+            wifi_receive_clear();
+            wifi_prov_state = WIFI_PROV_MODE;
+        }
+        else if ((uint32_t)(sysinf.conter10ms - wifi_prov_tick) >= wifi_prov_timeout)
+        {
+            wifi_receive_clear();
+            if (++wifi_prov_retry < WIFI_PROV_MAX_RETRY)
+                wifi_prov_state = WIFI_PROV_RST;   /* 重试复位 */
+            else
+                wifi_prov_abort();
+        }
+        break;
+
+    /* 2. 设置AP模式 */
+    case WIFI_PROV_MODE:
+        if (wifi_prov_wait != 0)
+            wifi_prov_check_cmd_resp(WIFI_PROV_SAP);
+        else
+            wifi_prov_send_cmd("AT+CWMODE=2", WIFI_PROV_CMD_TIMEOUT);
+        break;
+
+    /* 3. 建立热点: 名称OneChip_Mini7, 密码test1234 */
+    case WIFI_PROV_SAP:
+    {
+        char cStr[128] = {0};
+        if (wifi_prov_wait != 0)
+            wifi_prov_check_cmd_resp(WIFI_PROV_IP_Q);
+        else
+        {
+            my_sprintf(cStr, sizeof(cStr), "AT+CWSAP=\"%s\",\"%s\",%d,%d",
+                       WIFI_PROV_AP_SSID, WIFI_PROV_AP_PWD,
+                       WIFI_PROV_AP_CHANNEL, WIFI_PROV_AP_ECN);
+            wifi_prov_send_cmd(cStr, WIFI_PROV_CMD_TIMEOUT);
+        }
+        break;
+    }
+
+    /* 4. 查询SoftAP接口IP(默认192.168.4.1),确认热点地址 */
+    case WIFI_PROV_IP_Q:
+        if (wifi_prov_wait != 0)
+            wifi_prov_query_done(WIFI_PROV_DHCP_Q);
+        else
+            wifi_prov_send_cmd("AT+CIPAP?", WIFI_PROV_CMD_TIMEOUT);
+        break;
+
+    /* 5. 查询DHCP使能状态(确认AP侧能分配192.168.4.x) */
+    case WIFI_PROV_DHCP_Q:
+        if (wifi_prov_wait != 0)
+            wifi_prov_query_done(WIFI_PROV_MUX);
+        else
+            wifi_prov_send_cmd("AT+CWDHCP?", WIFI_PROV_CMD_TIMEOUT);
+        break;
+
+    /* 6. 打开多连接 */
+    case WIFI_PROV_MUX:
+        if (wifi_prov_wait != 0)
+            wifi_prov_check_cmd_resp(WIFI_PROV_SERVER);
+        else
+            wifi_prov_send_cmd("AT+CIPMUX=1", WIFI_PROV_CMD_TIMEOUT);
+        break;
+
+    /* 7. 启动TCP服务端: 端口8002 */
+    case WIFI_PROV_SERVER:
+    {
+        char cStr[48] = {0};
+        if (wifi_prov_wait != 0)
+        {
+            if (wifi_prov_check_cmd_resp(WIFI_PROV_LISTEN) == 1)
+                wifi_prov_tick = sysinf.conter10ms;  /* 记录开始等待配网的时间 */
+        }
+        else
+        {
+            my_sprintf(cStr, sizeof(cStr), "AT+CIPSERVER=1,%d", WIFI_PROV_TCP_PORT);
+            wifi_prov_send_cmd(cStr, WIFI_PROV_CMD_TIMEOUT);
+        }
+        break;
+    }
+
+    /* 8. 等待手机经TCP下发配网JSON(总超时300s) */
+    case WIFI_PROV_LISTEN:
+        if ((uint32_t)(sysinf.conter10ms - wifi_prov_tick) >= WIFI_PROV_LISTEN_TIMEOUT)
+        {
+            Van_Device_Printf(VAN_LOG_WIFI, "\r\n[Provision] wait cfg timeout");
+            wifi_prov_wait  = 0;
+            wifi_prov_state = WIFI_PROV_DONE;   /* 超时未收到配置,直接退出 */
+            break;
+        }
+        /* 接收并解析+IPD报文 */
+        {
+            int ipdRlt = wifi_prov_capture_ipd();
+            if (ipdRlt > 0)
+            {
+                if (wifi_prov_ipd_len >= (uint16_t)(sizeof(wifi_prov_ipd) - 2))
+                {
+                    /* 累积超限,丢弃重收 */
+                    wifi_receive_clear();
+                    wifi_prov_ipd_len = 0;
+                    wifi_prov_ipd[0]  = '\0';
+                }
+            }
+            else
+            {
+                /* 存在接收数据但未解析出有效+IPD,且距最近接收超1s: 打印残留并清空 */
+                if (g_wifi_rx_head != g_wifi_rx_last &&
+                    (uint32_t)(sysinf.conter10ms - g_wifi_rx_last_tick) >= WIFI_PROV_RX_IDLE_TIMEOUT)
+                {
+                    Van_Device_Printf(VAN_LOG_WIFI, "\r\n%s", g_wifi_rx_buf);
+                    wifi_receive_clear();
+                    wifi_prov_ipd_len = 0;
+                    wifi_prov_ipd[0]  = '\0';
+                }
+            }
+        }
+        /* 累积到完整JSON后解析 */
+        if (wifi_prov_ipd_len > 0 &&
+            wifi_prov_ipd[0] == '{' &&
+            wifi_prov_ipd[wifi_prov_ipd_len - 1] == '}')
+        {
+            char ssid[sizeof(sysinf.wifiname)];
+            char pwd[sizeof(sysinf.password)];
+            if (wifi_prov_parse_json(wifi_prov_ipd, ssid, (uint16_t)sizeof(ssid),
+                                     pwd, (uint16_t)sizeof(pwd)) == 0)
+            {
+                memset(sysinf.wifiname, 0, sizeof(sysinf.wifiname));
+                memset(sysinf.password, 0, sizeof(sysinf.password));
+                strcpy(sysinf.wifiname, ssid);
+                strcpy(sysinf.password, pwd);
+                wifi_prov_json_ok = 1;
+                Van_Device_Printf(VAN_LOG_WIFI, "\r\n[Provision] got cfg: ssid=%s pwd=%s",
+                                  sysinf.wifiname, sysinf.password);
+            }
+            else
+            {
+                wifi_prov_json_ok = 0;
+                Van_Device_Printf(VAN_LOG_WIFI, "\r\n[Provision] bad json: %s",
+                                  wifi_prov_ipd);
+            }
+            wifi_prov_build_ack(wifi_prov_json_ok, "invalid");
+            wifi_prov_state = WIFI_PROV_ACK_SEND;
+        }
+        break;
+
+    /* 9. 向手机应答(accepted/rejected) */
+    case WIFI_PROV_ACK_SEND:
+    {
+        char cStr[48] = {0};
+        Van_Device_Printf(VAN_LOG_WIFI, "\r\nack json: %s",
+                                  wifi_prov_ack);
+        wifi_receive_clear();
+        my_sprintf(cStr, sizeof(cStr), "AT+CIPSEND=%d,%d",
+                   wifi_prov_linkid, (int)strlen(wifi_prov_ack));
+        wifi_prov_send_cmd(cStr, WIFI_PROV_CMD_TIMEOUT);
+        /* CIPSEND属非透传"先握手后发数": 复位wait,由ACK_DATA收到OK后真正发载荷 */
+        wifi_prov_wait = 0;
+        wifi_prov_state = WIFI_PROV_ACK_DATA;
+        break;
+    }
+
+    case WIFI_PROV_ACK_DATA:
+        if (wifi_prov_wait == 0)
+        {
+            /* 等待 'OK\r\n' 提示符后发送应答数据 */
+            if(NULL != find_str_loop(g_wifi_rx_buf, WIFI_RX_BUF_SIZE ,&g_wifi_rx_head, g_wifi_rx_last, "OK\r\n"))
+            {
+                wifi_receive_clear();
+                wifi_prov_wait      = 1;
+                wifi_prov_tick      = sysinf.conter10ms;
+                wifi_prov_timeout   = WIFI_PROV_CMD_TIMEOUT;
+                WriteComm2(wifi_prov_ack, strlen(wifi_prov_ack));
+            }
+            else if (NULL != find_str_loop(g_wifi_rx_buf, WIFI_RX_BUF_SIZE, &g_wifi_rx_head,
+                                           g_wifi_rx_last, "ERROR") ||
+                     NULL != find_str_loop(g_wifi_rx_buf, WIFI_RX_BUF_SIZE, &g_wifi_rx_head,
+                                           g_wifi_rx_last, "CLOSED") ||
+                     (uint32_t)(sysinf.conter10ms - wifi_prov_tick) >= wifi_prov_timeout)
+            {
+                /* 应答失败或超时,继续后续流程 */
+                Van_Device_Printf(VAN_LOG_WIFI, "\r\n%s", g_wifi_rx_buf);
+                wifi_receive_clear();
+                wifi_prov_wait = 0;
+                wifi_prov_state = (wifi_prov_json_ok != 0) ? WIFI_PROV_SAVE : WIFI_PROV_LISTEN;
+                wifi_prov_ipd_len = 0;
+                wifi_prov_ipd[0]  = '\0';
+                if (wifi_prov_json_ok == 0)
+                    wifi_prov_tick = sysinf.conter10ms;  /* 重新计时 */
+            }
+        }
+        else
+        {
+            /* 等待SEND OK */
+            if (NULL != find_str_loop(g_wifi_rx_buf, WIFI_RX_BUF_SIZE, &g_wifi_rx_head,
+                                      g_wifi_rx_last, "SEND OK") ||
+                NULL != find_str_loop(g_wifi_rx_buf, WIFI_RX_BUF_SIZE, &g_wifi_rx_head,
+                                      g_wifi_rx_last, "ERROR") ||
+                NULL != find_str_loop(g_wifi_rx_buf, WIFI_RX_BUF_SIZE, &g_wifi_rx_head,
+                                      g_wifi_rx_last, "CLOSED") ||
+                (uint32_t)(sysinf.conter10ms - wifi_prov_tick) >= wifi_prov_timeout)
+            {
+                Van_Device_Printf(VAN_LOG_WIFI, "\r\n%s", g_wifi_rx_buf);
+                wifi_receive_clear();
+                wifi_prov_wait = 0;
+                wifi_prov_state = (wifi_prov_json_ok != 0) ? WIFI_PROV_SAVE : WIFI_PROV_LISTEN;
+                wifi_prov_ipd_len = 0;   /* 无效配置则清空缓存重新等待 */
+                wifi_prov_ipd[0]  = '\0';
+                if (wifi_prov_json_ok == 0)
+                    wifi_prov_tick = sysinf.conter10ms;  /* 重新计时 */
+            }
+        }
+        break;
+
+    /* 10. 保存配网参数到Flash */
+    case WIFI_PROV_SAVE:
+    {
+        uint8_t saveRlt = cfg_save_cfg_param();
+        Van_Device_Printf(VAN_LOG_WIFI, "\r\n[Provision] cfg_save_cfg_param=%d", saveRlt);
+        wifi_prov_state = WIFI_PROV_DONE;
+        break;
+    }
+
+    /* 11. 配网完成: 复位ESP32进入正常模式 */
+    case WIFI_PROV_DONE:
+        wifi_prov_exit();
+        break;
+    }
+}
+
 
 //wifi任务
 void WIFI_process()
 {
+    /* 热点配网模式优先处理 */
+    if (wifi_prov_request != 0 || wifi_prov_active != 0)
+    {
+        if (wifi_prov_request != 0)
+        {
+            wifi_prov_request = 0;
+            wifi_prov_enter();
+        }
+        wifi_prov_process();
+        return;
+    }
 
     if(sysinf.pc_channel_recv == 2  &&  wifi_state > WIFI_SETMUX){
         /*串口状态下,不走wifi处理*/

+ 3 - 0
DriverInterface/ESP32_WROOM_32D.h

@@ -67,6 +67,9 @@ char* find_str(void *src, int size,char *str);
 void WIFI_set_ssid(void);
 void WIFI_process(void);
 void WIFI_set_CloseConnect(void);
+/* 热点配网(SoftAP WiFi配网): 按键长按30s触发 */
+uint8_t WIFI_provision_request(void);
+uint8_t WIFI_provision_active(void);
 int WIFI_Send_Data(uint8_t *data, uint16_t len, uint8_t flag);
 int WIFI_Send_MSG(uint8_t*msgHead,uint8_t *data, uint8_t * crc, uint16_t len);
 int WIFI_ReSend(void);

+ 2 - 1
DriverInterface/onchip_flash.c

@@ -254,8 +254,9 @@ uint8_t cfg_param_verify(void)
 
 	if((saveCrc == calCrc) && (saveCrc != 0xffffffff))	{
 		Flash_ReadBuffer(CFG_PARAM_START_ADDR,(uint8_t *)&sysinf.SN, pramlen);
+		return 1;	/* 校验通过 */
 	}
-	return 0;
+	return 0;	/* 校验不通过 */
 }
 
 /********************************************************************

+ 25 - 1
DriverInterface/usr_motor.c

@@ -12,6 +12,7 @@
 #define LONG_PRESS_THRESHOLD 80 // 800ms长按阈值
 #define CONTINUOUS_INTERVAL 40 // 300ms内算连续按键
 #define USER_STOP_TIMER 200 // 长按2s不放,停止混匀
+#define SUPER_LONG_PRESS_TIMER 3000 // 超长按30s(10ms单位),触发SmartConfig配网
 
 #define FIND_OPTIC_SPEED 50 // 找光开速度
 #define FIND_OPTIC_BACK_SPEED 43 // 过光开回退速度
@@ -52,6 +53,8 @@ volatile
   uint32_t key_valid_flag; // 连续按键标志
 } key_ctrl;
 
+static uint8_t key_superlong_flag = 0; // 30s超长按已触发SmartConfig配网
+
 swing_state_def sw_st, last_st;
 unsigned int keyState = 0;
 unsigned int MotorOptState = 0;
@@ -190,6 +193,14 @@ void KYE_IRQHandler(void) {
 
       } else { // 上升沿(释放)
         key_ctrl.release_tick = conter10ms;
+        if (key_superlong_flag) { // 30s超长按已触发SmartConfig,释放不产生按键事件
+          key_superlong_flag = 0;
+          key_ctrl.press_tick = 0;
+          key_ctrl.last_release_tick = 0;
+          key_ctrl.state = KEY_IDLE;
+          key_ctrl.key_valid_flag = 0;
+          return;
+        }
         uint32_t press_duration = 0;
 				if(key_ctrl.press_tick){
 					press_duration = key_ctrl.release_tick - key_ctrl.press_tick;
@@ -238,6 +249,17 @@ void check_keyShortValid(void) {
 	  key_ctrl.press_tick = 0;
     }
   }
+  // 超长按(≥30s,仅空闲状态能持续按住)触发ESP32 SmartConfig配网
+  if (!key_superlong_flag && key_ctrl.press_tick && !WIFI_provision_active()) {
+    if ((sysinf.conter10ms - key_ctrl.press_tick) >= SUPER_LONG_PRESS_TIMER) {
+      key_superlong_flag = 1;
+      key_ctrl.press_tick = 0; // 清除按压计时,释放时不再触发长按复位
+      key_ctrl.release_tick = 0;
+      key_ctrl.last_release_tick = 0;
+      Van_Device_Printf(VAN_LOG_NOTICE, "\n[key] 30s pressed, start smartconfig");
+	  WIFI_provision_request();
+    }
+  }
 }
 
 /**
@@ -476,7 +498,9 @@ uint32_t Motor_mix_work(void) {
 	conter10ms = sysinf.conter10ms;
   if (key_ctrl.key_valid_flag) {
     key_ctrl.key_valid_flag = 0;
-    Motor_deal_key(sw_st, key_ctrl.state);
+    if (!WIFI_provision_active()) { // SmartConfig配网期间屏蔽按键动作
+      Motor_deal_key(sw_st, key_ctrl.state);
+    }
     key_ctrl.state = KEY_IDLE;
   }
   if(sw_st == SWING_ERR){

+ 2 - 0
DriverInterface/van_BSP_ESPUart.c

@@ -15,6 +15,7 @@
 uint8_t g_wifi_rx_buf[WIFI_RX_BUF_SIZE];
 uint32_t g_wifi_rx_last;
 uint32_t g_wifi_rx_head;
+uint32_t g_wifi_rx_last_tick = 0;   /* 最近一次收到ESP数据的时间戳(10ms单位,0=未收到) */
 
 void esp_receive_Rec_IDLE(UART_HandleTypeDef *huart)
 {
@@ -33,6 +34,7 @@ void esp_receive_Rec_IDLE(UART_HandleTypeDef *huart)
 		if(g_wifi_rx_last == WIFI_RX_BUF_SIZE){
 			g_wifi_rx_last = 0;
 		}
+		g_wifi_rx_last_tick = sysinf.conter10ms;   /* 记录最近接收时间戳 */
         // 存缓冲区...
       
         // 清除RXNE标志(读DR后自动清除,这里不需要额外操作)

+ 56 - 24
MDK-ARM/mini7_V2.uvoptx

@@ -229,6 +229,54 @@
           <ExecCommand></ExecCommand>
           <Expression></Expression>
         </Bp>
+        <Bp>
+          <Number>5</Number>
+          <Type>0</Type>
+          <LineNumber>862</LineNumber>
+          <EnabledFlag>1</EnabledFlag>
+          <Address>0</Address>
+          <ByteObject>0</ByteObject>
+          <HtxType>0</HtxType>
+          <ManyObjects>0</ManyObjects>
+          <SizeOfObject>0</SizeOfObject>
+          <BreakByAccess>0</BreakByAccess>
+          <BreakIfRCount>0</BreakIfRCount>
+          <Filename>..\DriverInterface\ESP32_WROOM_32D.c</Filename>
+          <ExecCommand></ExecCommand>
+          <Expression></Expression>
+        </Bp>
+        <Bp>
+          <Number>6</Number>
+          <Type>0</Type>
+          <LineNumber>274</LineNumber>
+          <EnabledFlag>1</EnabledFlag>
+          <Address>0</Address>
+          <ByteObject>0</ByteObject>
+          <HtxType>0</HtxType>
+          <ManyObjects>0</ManyObjects>
+          <SizeOfObject>0</SizeOfObject>
+          <BreakByAccess>0</BreakByAccess>
+          <BreakIfRCount>0</BreakIfRCount>
+          <Filename>../Core/Src/main.c</Filename>
+          <ExecCommand></ExecCommand>
+          <Expression></Expression>
+        </Bp>
+        <Bp>
+          <Number>7</Number>
+          <Type>0</Type>
+          <LineNumber>299</LineNumber>
+          <EnabledFlag>1</EnabledFlag>
+          <Address>0</Address>
+          <ByteObject>0</ByteObject>
+          <HtxType>0</HtxType>
+          <ManyObjects>0</ManyObjects>
+          <SizeOfObject>0</SizeOfObject>
+          <BreakByAccess>0</BreakByAccess>
+          <BreakIfRCount>0</BreakIfRCount>
+          <Filename>..\DriverInterface\onchip_flash.c</Filename>
+          <ExecCommand></ExecCommand>
+          <Expression></Expression>
+        </Bp>
       </Breakpoint>
       <WatchWindow1>
         <Ww>
@@ -490,50 +538,34 @@
         <Bp>
           <Number>0</Number>
           <Type>0</Type>
-          <LineNumber>510</LineNumber>
+          <LineNumber>299</LineNumber>
           <EnabledFlag>1</EnabledFlag>
-          <Address>134246126</Address>
+          <Address>134261298</Address>
           <ByteObject>0</ByteObject>
           <HtxType>0</HtxType>
           <ManyObjects>0</ManyObjects>
           <SizeOfObject>0</SizeOfObject>
           <BreakByAccess>0</BreakByAccess>
           <BreakIfRCount>1</BreakIfRCount>
-          <Filename>..\DriverInterface\usr_motor.c</Filename>
+          <Filename>..\DriverInterface\onchip_flash.c</Filename>
           <ExecCommand></ExecCommand>
-          <Expression>\\mini7_V2\../DriverInterface/usr_motor.c\510</Expression>
+          <Expression>\\mini7_V2\../DriverInterface/onchip_flash.c\299</Expression>
         </Bp>
         <Bp>
           <Number>1</Number>
           <Type>0</Type>
-          <LineNumber>303</LineNumber>
+          <LineNumber>274</LineNumber>
           <EnabledFlag>1</EnabledFlag>
-          <Address>134247576</Address>
+          <Address>134272320</Address>
           <ByteObject>0</ByteObject>
           <HtxType>0</HtxType>
           <ManyObjects>0</ManyObjects>
           <SizeOfObject>0</SizeOfObject>
           <BreakByAccess>0</BreakByAccess>
           <BreakIfRCount>1</BreakIfRCount>
-          <Filename>..\DriverInterface\usr_motor.c</Filename>
-          <ExecCommand></ExecCommand>
-          <Expression>\\mini7_V2\../DriverInterface/usr_motor.c\303</Expression>
-        </Bp>
-        <Bp>
-          <Number>2</Number>
-          <Type>0</Type>
-          <LineNumber>217</LineNumber>
-          <EnabledFlag>1</EnabledFlag>
-          <Address>134261172</Address>
-          <ByteObject>0</ByteObject>
-          <HtxType>0</HtxType>
-          <ManyObjects>0</ManyObjects>
-          <SizeOfObject>0</SizeOfObject>
-          <BreakByAccess>0</BreakByAccess>
-          <BreakIfRCount>1</BreakIfRCount>
-          <Filename>..\DriverInterface\state_mini.c</Filename>
+          <Filename>../Core/Src/main.c</Filename>
           <ExecCommand></ExecCommand>
-          <Expression>\\mini7_V2\../DriverInterface/state_mini.c\217</Expression>
+          <Expression>\\mini7_V2\../Core/Src/main.c\274</Expression>
         </Bp>
       </Breakpoint>
       <WatchWindow1>

+ 1 - 1
MDK-ARM/mini7_V2.uvprojx

@@ -1101,7 +1101,7 @@
             <v6Rtti>0</v6Rtti>
             <VariousControls>
               <MiscControls></MiscControls>
-              <Define>USE_HAL_DRIVER,STM32F103xE,ONECHIP_MINI_PLUS,MINI7_V2_DEBUG</Define>
+              <Define>USE_HAL_DRIVER,STM32F103xE,ONECHIP_MINI_ONE,MINI7_V2_DEBUG</Define>
               <Undefine></Undefine>
               <IncludePath>../Core/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy;../Drivers/CMSIS/Device/ST/STM32F1xx/Include;../Drivers/CMSIS/Include;../BoardInterface;../DriverInterface;../tshell</IncludePath>
             </VariousControls>