摘要本文介绍基于 STM32 与 ESP01s 的联网时钟校准方案。针对 STM32 RTC 因晶振限制导致时间漂移的问题通过 ESP01s 联网获取实时时间实现自动校准并同步获取当地天气。文章详细说明了初始化流程、AT 指令响应判断、WiFi 异常状态检测、通信帧格式设计以及基于 FreeRTOS 任务通知的收发实现最后给出实验结果验证方案有效性。使用STM32来做RTC时钟因为其晶振的限制在准确性上会出现逐渐漂移的情况基于这个问题引入ESP01s来联网获取实时时间实现自动校准同时还可以获取当地天气体现智能化的理念。设计初始化流程如下作为任务启动后首先执行的内容AT命令对应的回复判断如下所示ai写代码太爽辣只用管逻辑。int wait_response(const char *expect, uint32_t timeout_ms) { uint8_t buf[128]; TickType_t start xTaskGetTickCount(); while((xTaskGetTickCount() - start) pdMS_TO_TICKS(timeout_ms)) { size_t n xStreamBufferReceive(uartStreamBuffer, buf, sizeof(buf) - 1, pdMS_TO_TICKS(100)); if(n 0) { buf[n] \0; if(strstr((char *)buf, expect) ! NULL) return 1; // 收到预期响应 if(strstr((char *)buf, ERROR) ! NULL || strstr((char *)buf, FAIL) ! NULL) return -1; // 出错 } } return 0; // 超时 } // 返回值说明 // 3 - STATUS:3 已建立 TCP/UDP 连接 // 1 - STATUS:1 或 5 未连接 WiFi / 未获取 IP // 2 - STATUS:2 或 4 已获取 IP 但未建立 TCP或连接已断开 // -1 - 模块返回 ERROR 或 FAIL // 0 - 超时 int wait_connect_response(uint32_t timeout_ms) { uint8_t buf[256]; // 累积缓冲区 size_t total 0; // 已累积字节数 TickType_t start xTaskGetTickCount(); while ((xTaskGetTickCount() - start) pdMS_TO_TICKS(timeout_ms)) { uint8_t temp[128]; size_t n xStreamBufferReceive(uartStreamBuffer, temp, sizeof(temp), pdMS_TO_TICKS(100)); if (n 0) { // 防止累积缓冲区溢出 if (total n sizeof(buf) - 1) { total 0; // 溢出则丢弃旧数据重新开始累积 } memcpy(buf total, temp, n); total n; buf[total] \0; // 确保字符串函数安全 // 至少要有 4 个字节才能检查结尾 if (total 4) { // 1. 检查结尾是否为 OK\r\n (0D 0A) if (buf[total - 4] O buf[total - 3] K buf[total - 2] \r buf[total - 1] \n) { // 关键修改在缓冲区中查找 STATUS:而不是要求它在开头 char *p strstr((char *)buf, STATUS:); if (p ! NULL) { // STATUS: 长度为 7状态码在 p[7] // 检查 p[7] 是否在有效范围内 if ((uint8_t *)p 7 buf total) { char status p[7]; if (status 3) return 3; if (status 1 || status 5) return 1; if (status 2 || status 4) return 2; } } } // 2. 检查 ERROR 的尾部 OR\r\n (0D 0A) if (buf[total - 4] O buf[total - 3] R buf[total - 2] \r buf[total - 1] \n) { return -1; } // 3. 检查 FAIL 的尾部 IL\r\n (0D 0A) if (buf[total - 4] I buf[total - 3] L buf[total - 2] \r buf[total - 1] \n) { return -1; } } } } return 0; // 超时 }实际需要执行的任务内容分为两部分定时连接状态检查、串口内容收发。设计WiFi异常状态检测执行流程如下图。异常检测功能围绕ATCIPSTATUS指令设计根据其返回的5种状态码代表的不同状态执行不同操作。循环执行直到连接成功。连接检查代码如下这里留一个形参是用来判断当前是初始化时调用还是连接检查时调用避免多余的执行。1已连接 Wi-Fi但未获取 IP2已连接 Wi-Fi 并获取 IP但未建立 TCP/UDP 连接3已建立 TCP/UDP 连接4TCP/UDP 连接已断开0未连接 Wi-Fivoid connect_init(int status) { int istatus; int retry 0; uart1_send_bytes(,3);//开启透传模式 wait_response(OK\r\n,1000); while(i ! 3 || retry 3) { if(i 0) { // 1. 基础配置 uart1_send_at(ATE0); wait_response(OK\r\n, 1500); uart1_send_at(ATUART115200,8,1,0,1); wait_response(OK\r\n, 1500); uart1_send_at(ATCWMODE1); wait_response(OK\r\n, 1500); i; } if(i 1 retry 3) { uart1_send_at(ATCWJAP\TP-LINK_3372\,\12345678\); wait_response(OK\r\n,2000); i; } if(i 2 retry 3) { uart1_send_at(ATCIPSTART\TCP\,\192.168.1.103\,7755); wait_response(OK\r\n,2000); i; } uart1_send_at(ATCIPSTATUS);//检查连接状态 i wait_connect_response(3000); retry; } uart1_send_at(ATCIPMODE1);//开启透传模式 wait_response(OK\r\n,1000); uart1_send_at(ATCIPSEND);//进入透传 wait_response(,1000); }通信帧上设置固定发送帧10字节如下字节值含义00x55帧头10x44帧头20x01时间功能码3data-get_time时间有效位0x00无效 /0x01有效40x02地区码功能码5data-area_index地区码内容0x00不更新0x01~0x03对应地区60x00保留70x00保留80x33帧尾90x22帧尾接收帧设计为固定16字节字节内容说明00x55帧头10x44帧头20x01时间功能码30x00/0x01时间有效位4~10时间数据7 字节 BCD码110x02地点功能码120x00/0x01地点有效位13地点数据0x01/0x02/0x03140x33帧尾150x22帧尾程序使用FreeRTOS的任务通知作为信号通知由 API 自动管理。void send1(void) { // Implementation for sending area code send_data.area_index 0x01; xTaskNotify(xPrintTaskHandle, 0x01, eSetValueWithOverwrite); }//wifi发送任务 void print_task(void *pvParameters) { uint8_t buf[100]; uint32_t notifyValue; connect_init(0); while(1) { // Receive xStreamBufferReceive(uartStreamBuffer, buf, sizeof(buf), portMAX_DELAY); // 从 StreamBuffer 中接收数据 if(xTaskNotifyWait(0, 0, notifyValue, portMAX_DELAY) pdTRUE) { Data_pack(send_data); uart1_send_bytes(send_buf, 10); } vTaskDelay(pdMS_TO_TICKS(10)); } }实验结果如图发送两条不同的地区配置消息消息发送效果符合预期。