shuichan_old/V1.4/STM32F103_App1/HardWare/NETWORK/net_wifi_ota.c

275 lines
11 KiB
C
Raw Normal View History

/* net_wifi_ota.c - WiFi 通道 OTA 固件下载
*
* ESP-01S CIPMUX=0OTA MQTT
* 退("+++") HTTP GET
* 3072B W25Q64 B + + CRC16
* CRC + MQTT OTA BL
*
* 4WIFI和4G V3.4 WIFI无回弹
* W25Q64 + CRC + + BootLoader 退
* Flash APP2 TCP MCU
*
*/
#include "net_wifi_ota.h"
#include "net_wifi.h"
#include "esp8266.h"
#include "network.h"
#include "air780e.h" /* bg_delay / U2_CopyBuff / Verify_Firmware_External / crc16_modbus_update */
#include "ota_app.h" /* Application_Size */
#include "uart.h"
#include "relay.h"
#include "feed_scale.h"
#include "led.h"
#include "log.h"
#include "../W25Q64/w25q64.h"
#include "stdio.h"
#include "string.h"
#include <stdlib.h>
#define WIFI_OTA_CHUNK 3072u /* 与 4G OTA 相同的分块粒度 */
#define WIFI_OTA_HDR_TIMEOUT 15000u /* 等 HTTP 响应头超时 */
#define WIFI_OTA_IDLE_TIMEOUT 20000u /* 数据流静默超时TCP 流控可能主动降速) */
static uint8_t s_blk[WIFI_OTA_CHUNK]; /* 分块暂存写flash/校验/CRC 都按块处理 */
/* 二进制安全子串查找 */
static uint8_t *ota_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;
}
/* 大小写不敏感查找Content-Length 头各服务器大小写不一) */
static uint8_t *ota_memfind_i(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++) {
uint16_t j;
for (j = 0; j < nlen; j++) {
char a = hay[i + j], b = needle[j];
if (a >= 'A' && a <= 'Z') a += 32;
if (b >= 'A' && b <= 'Z') b += 32;
if (a != b) break;
}
if (j == nlen) return hay + i;
}
return NULL;
}
/* 消费接收缓冲前 n 字节 */
static void ota_consume(uint16_t n)
{
if (n >= U2_CopyIndex) {
U2_CopyIndex = 0;
} else {
memmove(U2_CopyBuff, U2_CopyBuff + n, U2_CopyIndex - n);
U2_CopyIndex -= n;
}
U2_CopyBuff[U2_CopyIndex] = '\0';
}
/*==== HTTP Range 分块下载固件到 W25Q64 槽 B成功返回 0 且 *out_size=固件大小 ====
* 3072B GETRange: bytes=off-endMCU
*
* 6 57KBMCU flash */
static int wifi_ota_download(const char *host, int port, const char *path,
uint16_t crc16, uint32_t *out_size)
{
char req[224];
uint32_t offset = 0, total_size = 0;
uint16_t crc_acc = 0xFFFF;
int last_pct = -1;
/* 拆掉 MQTT 连接,换连固件服务器(同为透传模式) */
esp_exit_transparent();
if (esp_enter_transparent(host, (uint16_t)port) != 0) {
log_warn("> WiFi OTA: connect firmware server failed");
return -1;
}
while (1) {
/* 最后一块按总大小截断total_size 第一块响应头里才拿到) */
uint32_t want_end = offset + WIFI_OTA_CHUNK - 1;
if (total_size > 0 && want_end >= total_size) want_end = total_size - 1;
/* HTTP/1.1 默认 keep-alive同一 TCP 连接上顺序发多个 Range 请求 */
snprintf(req, sizeof(req),
"GET %s HTTP/1.1\r\nHost: %s:%d\r\nUser-Agent: esp8266-ota\r\nAccept: */*\r\nRange: bytes=%u-%u\r\n\r\n",
path, host, port, (unsigned)offset, (unsigned)want_end);
esp_rx_clear();
esp_send((uint8_t *)req, strlen(req));
/* 等响应头完整(\r\n\r\n 结束) */
uint32_t hdr_end = 0;
uint32_t tick = HAL_GetTick();
while (HAL_GetTick() - tick < WIFI_OTA_HDR_TIMEOUT) {
bg_delay(50);
uint8_t *p = ota_memfind(U2_CopyBuff, U2_CopyIndex, "\r\n\r\n");
if (p) { hdr_end = (uint32_t)(p - U2_CopyBuff) + 4; break; }
if (ota_memfind(U2_CopyBuff, U2_CopyIndex, "CLOSED")) {
log_warn("> WiFi OTA: server closed @%u", (unsigned)offset);
return -1;
}
}
if (hdr_end == 0) {
log_warn("> WiFi OTA: header timeout @%u", (unsigned)offset);
return -1;
}
/* 必须 206服务器支持 Range200 说明忽略了 Range整包灌流量消费不动放弃 */
if (strncmp((char *)U2_CopyBuff, "HTTP/", 5) != 0) {
log_warn("> WiFi OTA: bad response @%u", (unsigned)offset);
return -1;
}
if (!ota_memfind(U2_CopyBuff, 16, " 206")) {
U2_CopyBuff[hdr_end < 120 ? hdr_end : 120] = '\0';
log_warn("> WiFi OTA: not 206 (server no Range?): %.60s", U2_CopyBuff);
return -1;
}
/* 第一块:从 Content-Range: bytes 0-x/TOTAL 拿总大小,然后擦槽 B */
if (total_size == 0) {
uint8_t *cr = ota_memfind_i(U2_CopyBuff, hdr_end, "content-range:");
uint8_t *slash = cr ? (uint8_t *)strchr((char *)cr, '/') : NULL;
if (slash) total_size = (uint32_t)atoi((char *)slash + 1);
if (total_size == 0 || total_size > Application_Size) {
log_warn("> WiFi OTA: bad total size %u", (unsigned)total_size);
return -1;
}
log_info("**************************************");
log_info("* WiFi OTA Update Start *");
log_info("* Size: %u bytes *", (unsigned)total_size);
log_info("**************************************");
log_info("> Erasing W25Q64 new slot...");
W25Q64_EraseRegion(W25Q64_SLOT_NEW_ADDR, W25Q64_SLOT_SIZE);
log_info("> W25Q64 new slot erased");
}
/* 本块长度 = Content-Length≤3072最后一块可能更小 */
uint32_t clen = 0;
{
uint8_t *cl = ota_memfind_i(U2_CopyBuff, hdr_end, "content-length:");
if (cl) clen = (uint32_t)atoi((char *)cl + 15);
}
if (clen == 0 || clen > WIFI_OTA_CHUNK) {
log_warn("> WiFi OTA: bad chunk len %u @%u", (unsigned)clen, (unsigned)offset);
return -1;
}
ota_consume((uint16_t)hdr_end);
/* 精确收 clen 字节固件体到暂存块 */
uint16_t got = 0;
tick = HAL_GetTick();
while (got < clen) {
if (U2_CopyIndex > 0) {
tick = HAL_GetTick();
uint16_t n = U2_CopyIndex;
if (n > (uint16_t)(clen - got)) n = (uint16_t)(clen - got);
memcpy(s_blk + got, U2_CopyBuff, n);
got += n;
ota_consume(n);
} else {
if (HAL_GetTick() - tick > WIFI_OTA_IDLE_TIMEOUT) {
log_warn("> WiFi OTA: body timeout @%u got %u/%u",
(unsigned)offset, got, (unsigned)clen);
log_warn("> OTA diag: CNDTR=%u RxState=%u rxcplt=%u ore=%u pending=%u",
(unsigned)huart4.hdmarx->Instance->CNDTR,
(unsigned)huart4.RxState,
(unsigned)g_uart4_rxcplt_cnt,
(unsigned)g_uart4_ore_cnt,
(unsigned)U2_CopyIndex);
return -1;
}
bg_delay(20);
}
}
/* 写 W25Q64 槽 B + 回读校验 + 累计 CRC */
W25Q64_WriteBuffer(W25Q64_SLOT_NEW_ADDR + offset, s_blk, clen);
if (!W25Q64_Verify(W25Q64_SLOT_NEW_ADDR + offset, s_blk, clen)) {
log_error("> WiFi OTA: W25Q64 verify fail at offset=%u", (unsigned)offset);
return -1;
}
for (uint16_t i = 0; i < clen; i++) {
crc_acc = crc16_modbus_update(crc_acc, s_blk[i]);
}
offset += clen;
int pct = (int)(offset * 100 / total_size);
if (pct > 100) pct = 100;
if (pct / 10 > last_pct / 10) {
last_pct = pct;
log_info("> [%3d%%] %u/%uB", pct, (unsigned)offset, (unsigned)total_size);
LED2_TOGGLE;
}
if (offset >= total_size) break;
}
/* 整包 CRC16 校验 */
log_info("> W25Q64 CRC16=0x%04X (server=0x%04X)", crc_acc, crc16);
if (crc16 != 0 && crc_acc != crc16) {
log_error("> WiFi OTA: CRC16 mismatch! abort");
return -1;
}
/* 固件头合法性校验(栈顶/复位向量) */
if (!Verify_Firmware_External(W25Q64_SLOT_NEW_ADDR, total_size)) {
log_error("> WiFi OTA: firmware header invalid");
return -1;
}
*out_size = total_size;
log_info("> WiFi OTA: download complete, %uB OK", (unsigned)total_size);
return 0;
}
/*==== WiFi OTA 入口:与 air780e_trigger_ota 对齐的语义 ====
* result=1 BootLoader
* result=0 MQTT */
void net_wifi_ota(const char *host, int port, const char *path,
const char *ota_id, uint16_t new_ver, uint16_t crc16)
{
uint32_t file_size = 0;
log_info("> WiFi OTA: in");
feedScaleStop(); /* 称重模式下停止投喂状态机 */
relaySet(1, 0); /* 无秤模式下直接断开投喂继电器 */
/* MQTT 还在线先上报开始progress 0再屏蔽业务发布 */
NET_mqtt_report("iot.ota.progress.post", ota_id, 0);
bg_delay(500);
g_net_tx_busy = 1;
g_wifi_mqtt_ready = 0; /* MQTT 链路即将被 OTA 连接顶替 */
int ret = wifi_ota_download(host, port, path, crc16, &file_size);
/* 重连 MQTT走完整 WiFi 建链复位模块→连AP→透传→CONNECT/SUB上报结果 */
if (net_wifi_init() == 0) {
g_net_tx_busy = 0;
NET_mqtt_report("iot.ota.result.post", ota_id, ret == 0 ? 1 : 0);
bg_delay(2000); /* 等结果消息发完 */
} else {
g_net_tx_busy = 0;
log_warn("> WiFi OTA: MQTT reconnect failed, result report lost");
}
if (ret == 0) {
log_info("**************************************");
log_info("* WiFi OTA Download Complete! *");
log_info("* Rebooting to BootLoader... *");
log_info("**************************************");
/* 只把固件信息写入 W25Q64版本号在 APP 确认成功后再写入 EEPROM */
W25Q64_OTA_SetNewInfo(new_ver, file_size, crc16);
bg_delay(100);
NVIC_SystemReset();
}
log_warn("> WiFi OTA: aborted, resume normal operation");
}