shuichan_old/V1.3/STM32F103_App1
helei 6df97c1e37 修复 RECOVERY 命令:透传态下先 +++ 退透传再 AT+RESTORE(V1.2~V1.4) 2026-08-08 21:50:39 +08:00
..
.vscode 水产投料机与四路继电器二合一:V1.1/V1.2/V1.3(4G/WiFi双通道、GPS定位、双通道OTA) 2026-08-08 20:04:09 +08:00
Drivers 水产投料机与四路继电器二合一:V1.1/V1.2/V1.3(4G/WiFi双通道、GPS定位、双通道OTA) 2026-08-08 20:04:09 +08:00
HardWare 修复 RECOVERY 命令:透传态下先 +++ 退透传再 AT+RESTORE(V1.2~V1.4) 2026-08-08 21:50:39 +08:00
Inc 水产投料机与四路继电器二合一:V1.1/V1.2/V1.3(4G/WiFi双通道、GPS定位、双通道OTA) 2026-08-08 20:04:09 +08:00
MDK-ARM 水产投料机与四路继电器二合一:V1.1/V1.2/V1.3(4G/WiFi双通道、GPS定位、双通道OTA) 2026-08-08 20:04:09 +08:00
Src 水产投料机与四路继电器二合一:V1.1/V1.2/V1.3(4G/WiFi双通道、GPS定位、双通道OTA) 2026-08-08 20:04:09 +08:00
m_app 水产投料机与四路继电器二合一:V1.1/V1.2/V1.3(4G/WiFi双通道、GPS定位、双通道OTA) 2026-08-08 20:04:09 +08:00
README.md 新增 V1.4(FreeRTOS 版);V1.1~V1.3 版本号统一为文件夹规则 2026-08-08 21:13:56 +08:00
keilkill.bat 水产投料机与四路继电器二合一:V1.1/V1.2/V1.3(4G/WiFi双通道、GPS定位、双通道OTA) 2026-08-08 20:04:09 +08:00

README.md

智能水产投料机 V1.3.0

工程概述

本工程基于 STM32F103RCT6 + STM32Cube HAL + Keil MDK,实现设备的控制、联网与投喂。

  • BootLoaderSTM32F103_BootLoader/,负责 OTA 固件烧录、试运行计数与失败回滚
  • 应用固件STM32F103_App1/,当前运行版本 V1.3.0
  • 双网络通道4G(Air780E/EG) 优先WiFi(ESP-01S) 兜底,见下文"双通道网络"

产品型号配置

同一套板子适配两种产品,编译前在 Inc/main.h 顶部通过一个宏二选一:

#define PRODUCT_WITH_FEED_SCALE   0
取值 产品 投喂行为
1 智能水产投料机(接 RS485 磅秤) feedingonceWt 定量自动投喂,带重量反增/超时保护;sw1 指令被忽略
0 基础四路继电器(不接磅秤) feeding / sw1 直接开关投喂继电器 1由平台侧自行计时

硬件接口

接口 用途 引脚 参数
USART1 蓝牙串口调试 / 日志输出 PA9/PA10 115200-8N1
USART2 RS485 喂料秤 PA2/PA3 9600-8N1
USART3 4G Air780E/EG MQTT+GNSS PB10/PB11 921600-8N1见下文波特率自适应
UART4 WiFi ESP-01S PC10/PC11 115200→921600-8N1见下文 WiFi 波特率同步)
ESP_RST WiFi 模块复位 PA5 低电平 500ms 复位
Relay1 投喂电机控制 PB7 高电平吸合
Relay2~4 备用控制 PB8/PC0/PC2 高电平吸合
I2C软件 AT24C02 配置存储 PA11/PA12 页写入
SPI软件 W25Q64 外部 Flash PB3/PB4/PA15/PB5 OTA 固件暂存
ADC1 电池电压检测 PA0 低电量上报 pow:0
PA8 手动模式检测 PA8 高电平=手动模式
PC13 LED2 心跳 / TPL5010 喂狗 PC13 每 1s 翻转(见下文看门狗)

看门狗(双狗架构)

看门狗 超时 喂狗点 作用
IWDG片内独立看门狗 ~26sLSI 漂移下最短 ~17s APP 主循环、app_bg_service()(所有 AT/OTA 等待每 20ms 让出一次BL 的擦除/拷贝循环 固件卡死 → 复位BL 启动OTA 试运行期计一次试错
TPL5010外置硬件狗 7 分钟 PC13(LED2) 电平翻转APP activeEvents() 每秒翻转BL 进入时及长循环中翻转 MCU 完全失控(含 IWDG 失效)时的最后兜底

注意LED2 同时承担运行指示和外部狗喂狗,修改 LED2 闪烁逻辑时必须保持周期性翻转,否则 7 分钟后整板复位。

双通道网络4G 优先WiFi 兜底)

网络层 HardWare/NETWORK/network.c 对业务层屏蔽底层模组差异,业务只调 NET_* 接口4G 后端(CAR/air780e.c,模组内置 MQTT与 WiFi 后端(NETWORK/net_wifi.cTCP 透传 + 软件 MQTT同构运行时互斥切换。

通道切换状态机

EEPROM偏移 130记忆上次成功联网方式4G / WiFi固定热点 / WiFi配网热点),仅方式变化时写一次;开机优先跑上次成功的方式:

上次 4G 成功4G无 SIM/无信号每 10s 重试3 次失败)→ WiFi → 回 4G…循环
上次 WiFi 成功WiFi 一轮建链失败 → 4G同上重试→ 回 WiFi…循环

WiFi 建链子顺序net_wifi_init阻塞式期间 bg_delay 喂狗):
  ① 上次是配网热点AT+CIPSTATUS 查已存 AP 自动连接10sSTATUS:2=已拿到IP
  ② AT+CWJAP 连代码里固定的 SSID/PASSesp8266.h 的 WIFI_SSID/WIFI_PASS60s 倒计时)
  ③ ESP-Touch SmartConfig 智能配网60s 倒计时,配到的热点由模块保存)
  全部失败 → 回 4G
WiFi 运行30s MQTT PINGREQ 心跳TCP 断开(CLOSED)/心跳超时 → 自动重连,重连失败回 4G

注:已存 AP 的判断用 AT+CIPSTATUS 主动查询而非被动等 WIFI GOT IP 打印—— 后者在波特率同步阶段就会被清缓冲擦掉,永远等不到。

WiFi 通道实现要点

  • TCP 透传 + 软件 MQTTESP-01S 用 AT+CIPMODE=1 + AT+CIPSEND 进透传MQTT 3.1.1 协议栈在 MCU 软件实现(HardWare/WIFI/mqtt_client.c),不用 AT+MQTT
  • WiFi 波特率同步ESP-01S 出厂/RESTORE 后 115200初始化时探测并用 AT+UART_DEF=921600 固化掉电保存MCU 侧 UART4_SetBaudRate() 同步;失败自动回滚维持 115200
  • 接收缓冲共享WiFi 与 4G 互斥运行,共用 Rx_Buf/U2_CopyBuffstm32f1xx_it.c 两路 IDLE 均带激活后端守卫RAM 增量 <2KB
  • AT 应答严格匹配:发送后才清接收缓冲(防上一条应答迟到尾巴造成"假成功")、二进制安全匹配、收到 ERROR/FAIL 立即失败;进透传前先 AT+CIPCLOSE 清残留连接、CIPMUX=0 先于 CIPMODE=1
  • ORE 自愈921600 高波特率下 UART 溢出会让 HAL 中止 DMA 接收,HAL_UART_ErrorCallback 里按当前激活通道立即重新武装DMA 填满(连续流无 IDLE 间隙)时在 HAL_UART_RxCpltCallback 里搬运并重启,防接收停摆
  • WiFi 通道 OTA:支持,透传 TCP 上软件实现 HTTP Range 分块下载,见下文"OTA 升级"章节

4G 波特率自适应115200 → 921600

  • Air780E 出厂/自适应模式只覆盖 9600~115200921600 必须用 AT+IPR 固定。
  • APP 联网时永远先按 115200 探测(避免向自适应侦测窗口发送其他波特率的乱码):
    • 通 → 发 AT+IPR=921600 + AT&W 固化,切换到 921600日志出现 baud -> 921600
    • 不通(模块已固化 921600→ 直接切 921600 连接。
  • 切换由 USART3_SetBaudRate() 运行中重写 BRR 完成,不影响 GPIO/DMA/NVIC。

4G 信号检测AT+CSQ

  • 网络附着CGATT成功后查询一次 AT+CSQrssi(0~31) 转百分比串口输出:> 4G: signal 77% (rssi=24)
  • 无信号rssi=99 或 <2g_no_signal_detected,与无 SIM 走同一套重试/回退 WiFi 逻辑
  • 信号弱(<20%):告警但继续联网

GPS 定位上报(仅 4G 通道Air780EG

参考工程《air780EG-V1.1加入北斗GPS数据》移植实现air780e.cair780e_gps_poll())。

工作流程

  1. GNSS 初始化4G 拨号(AT+SAPBR=1,1)成功后执行 AT+CGNSPWR=1(开 GNSS 电源)+ AT+CGNSAID=31,1,1,1A-GPS 辅助定位)。非 EG 版本模组回 ERROR 时仅告警并关闭轮询(s_gnss_present=0),不影响联网
  2. 周期查询:联网 60 秒后首次查询(GPS_FIRST_DELAY_MS,留搜星时间),之后每 5 分钟一次(GPS_REPORT_INTERVAL_MSAT+CGNSINF
  3. 解析+CGNSINF: run,fix,UTC,lat,lon,...,手工按逗号切分(不依赖 sscanf scanset定位无效fix≠1跳过本次上报(室内常见),不发空串
  4. 坐标转换WGS-84 → GCJ-02见下节
  5. 上报:模组内置 MQTT AT+MPUBtopic /iot/data/up/<ClientID>

上报格式

云平台字段标识符 GPS经度在前、纬度在后(高德/GeoJSON 习惯):

{"header":"iot.prop.post","body":{"GPS":"115.924092,28.679470"}}

坐标系转换WGS-84 → GCJ-02

GPS 模块输出 WGS-84 原始坐标,直接输入高德地图会有 100~600m 加密偏移(国内地图强制使用 GCJ-02"火星坐标系")。固件在上报前用公开标准算法完成转换(wgs84_to_gcj02(),三角函数近似,中国境外自动跳过),上报的坐标可直接输入高德地图/坐标拾取器。

注意:若改用百度地图,需要再转一层 GCJ-02 → BD-09。

保护机制

  • OTA 下载期间(g_net_tx_busy)禁止 GPS 查询插队,避免污染 HTTP 数据流
  • 查询等待期间有平台下行命令(+MSUB:)插队时主动让行,不吞继电器指令

使用注意

  • GPS 天线需能看到天空:室内基本无法定位(> GPS: no fix, skip 属正常),窗边/阳台通常可定,室外最佳;冷启动首次定位约 1~3 分钟
  • 验证:串口日志 > GPS: 115.924092,28.679470,或直接看平台 GPS 字段

OTA 升级

流程

平台下发 iot.ota.upgrade.post(url, crc16, ver)
  → APP: 模块硬复位 → HTTP HEAD 拿大小 → 擦 W25Q64 槽 B
  → 3072 字节/块分段下载AT+HTTPPARA BREAK/BREAKEND + HTTPACTION + HTTPREAD
  → 每块写 W25Q64 并回读校验,全程累计 CRC16
  → 整包 CRC + 栈顶/复位向量校验 → 可靠上报 result.post等 OK + 重试)
  → 写 OTA 信息区(状态 DOWNLOADED) → 复位进 BootLoader
BootLoader:
  → 擦除备份槽 → 备份当前 APP1 到槽 A失败则中止更新留在旧版本
  → 擦 APP1 → 写入新固件 → CRC + 向量校验 → 状态 INSTALLED → 跳转
新 APP 试运行:
  → MQTT 就绪 + 首次发布成功 + 3s → 上报 confirm.post
  → 确认标志写 W25Q64回读验证 + 3 次重试)→ 版本号存 EEPROM
  → 下次启动 BL 清理 OTA 标志,升级完成

Flash 分区

内部 Flash256KB

区域 地址 大小
BootLoader 0x08000000 32KB
APP1 运行区 0x08008000 224KB

W25Q64 外部 Flash8MB

区域 地址 用途
槽 A 0x000000 (256KB) 当前固件备份(回滚镜像)
槽 B 0x040000 (256KB) 新固件下载暂存
信息区 A/B 0x080000 / 0x081000 (各 4KB) OTA 状态双副本magic + 序号 + checksum

健康检查与回退

新固件"健康" = 4G 附着 → MQTT 连接 → 首次发布成功。回退判据(3 次真实故障才回退):

  • 看门狗复位IWDG/WWDG固件真的卡死→ BL 计一次试错(读 RCC->CSR 判别);
  • 健康超时MQTT 就绪 1 分钟仍无首次发布)→ APP 复位前自行计一次;
  • 上电/按键/普通软件复位不计次——现场断电、断网不会误触发回退;
  • 试运行期间完全断网 → 不计次、不回退,持续等待网络恢复。

掉电/断网安全性

故障点 结果
下载中断网/掉电 信息区未更新,旧固件照常运行,平台可重推
BL 备份阶段掉电 backup_valid=0,重启后重新备份
BL 擦写 APP1 中途掉电 状态 COPYING 视为 DOWNLOADED,重启后完整重做(备份不会被坏镜像覆盖)
备份创建失败 中止更新,留在旧版本,下次重启重试
回退过程掉电 重启后重新执行回退拷贝

其他要点

  • 双通道均支持 OTA4G 用模组内置 HTTPAT+HTTP* 分块WiFi 通道由 MCU 在透传 TCP 上软件实现 HTTP Range 分块下载NETWORK/net_wifi_ota.c
    • 流程:收 OTA 指令 → 停投喂 → +++ 退透传MQTT 连接被顶替,单连接限制)→ 连固件服务器 → 每 3072B 一个 GETRange: bytes=off-endMCU 自定节奏,防止服务器一次灌爆接收缓冲)→ 每块写 W25Q64 槽 B + 回读校验 + 累计 CRC16 → 整包 CRC + 固件头校验 → 重连 MQTT 上报 result → 写信息区复位进 BL
    • 下载期间 MQTT 断开属正常,进度只在开始/结束上报;失败自动重连 MQTT 恢复业务
  • OTA 触发时先强制停止投喂(断开继电器 1
  • 下载期间蓝牙命令、秤轮询、LED 心跳、电量检测照常运行(bg_delay 切片让出),业务状态上报暂时屏蔽
  • 分段接收按 +HTTPREAD: <len> 声明长度收齐即处理无静默等待40KB 固件全程约 38 秒(含模块复位重连 ~15s

主要功能

  1. 双通道 MQTT 联网4G(Air780E/EG) 优先 + WiFi(ESP-01S) 兜底自动切换,见上文"双通道网络"
  2. 远程控制:平台下发开关 sw1/sw2/sw3/sw4feeding 指令(投料机模式下 sw1 被忽略,投喂只能走 feeding
  3. 自动投喂PRODUCT_WITH_FEED_SCALE = 1
    • 平台设置单次投喂量 onceWt
    • 平台下发 feeding=true 启动投喂
    • 继电器 1 吸合RS485 秤实时读取重量
    • 当重量减少 ≥ onceWt 时自动停止,带重量反增与 10 分钟超时保护
  4. 去皮/标定:平台设置 zero 空载重量,设置后按最新原始重量重新计算剩余料重并立即上报
  5. GPS 定位上报:见上文专题(仅 4G 通道)
  6. 4G 信号检测:见上文专题
  7. OTA 升级:见上文专题(仅 4G 通道)
  8. 蓝牙调试USART1 支持 RESET/INFO/STATUS/R1ON~R4OFF/ALLON/ALLOFF/OTA/RECOVERY/HELP 等命令
  9. 掉电安全(断电记忆)联网成功MQTT 首次就绪)后按 EEPROM 恢复继电器上次状态,随后全量上报让平台与电路一致
    • 无秤模式(PRODUCT_WITH_FEED_SCALE = 0四路全部恢复(含 SW1
    • 投料机模式(PRODUCT_WITH_FEED_SCALE = 1继电器 1 不恢复且上电强制断开(防止误投料浪费饲料),继电器 2~4 恢复

平台数据字段

字段 含义 说明
ver 固件版本号 每次状态上报携带,平台可据此识别设备是否发生回退
weight 空载重量 平台显示为去皮重量 zero
zero 剩余料重 平台显示为当前剩余料重
onceWt 单次投喂量 每次投喂目标重量
sw1 / feeding 第一路继电器状态 1=投喂中0=停止;无秤模式下两字段同时上报,平台两个开关同步刷新
sw2~sw4 备用继电器状态 0/1
pow 电量状态 1=正常0=低电量
GPS 定位坐标 仅 4G 通道(Air780EG)GCJ-02 高德坐标系,格式 "经度,纬度",每 5 分钟上报

下行报文由 m_app/proto.c 按键名定位解析("sw2" 不会误中 "sw21",其他字段里的 true/false 不会造成误判)。

串口调试命令

通过 USART1蓝牙串口发送

RESET    - 系统复位
INFO     - 打印 MQTT 配置
STATUS   - 打印继电器状态
R1ON     - 打开继电器 1
R1OFF    - 关闭继电器 1
R2ON/R2OFF ~ R4ON/R4OFF - 控制继电器 2~4
ALLON    - 打开所有继电器
ALLOFF   - 关闭所有继电器
OTA      - 复位进入 BootLoader 检查升级
RECOVERY - WiFi 模块 AT+RESTORE 恢复出厂(忘掉已存热点)+ 整机复位,用于测试 SmartConfig
HELP     - 显示支持命令

SmartConfig 测试流程:关掉固定热点 → 发 RECOVERY → 设备复位走完 4G 重试进入 WiFi → 已存热点(20s)、固定热点(60s) 都失败后自动进 SmartConfig60s 倒计时)→ 用 ESP-Touch APP 广播密码。

代码分层APP

main.c 只保留三行初始化和主循环,逻辑全部下沉到模块:

int main(void)
{
    SCB->VTOR = FLASH_BASE | Application_1_Addr;
    Board_Init();       // 基础硬件HAL/时钟/GPIO/DMA/UART/看门狗SYSINIT/system.c
    app_init();         // 业务模块初始化SYSINIT/app_loop.c
    app_loop_init();
    while (1) {
        IWDG_Feed();
        app_loop_process();   // 业务调度SYSINIT/app_loop.c
    }
}

目录结构

STM32F103_App1/
├── Src/
│   └── main.c              # 仅 VTOR + Board_Init/app_init/主循环
├── Inc/
│   └── main.h              # 产品型号宏 PRODUCT_WITH_FEED_SCALE、MQTT 默认配置
├── m_app/
│   ├── proto.c/h           # 平台 JSON 报文取值工具(按键名定位)
│   └── ota_app.h           # Flash 分区定义BootLoader 32K + APP1 224K
├── HardWare/
│   ├── USER_CMD/           # 蓝牙串口命令处理
│   ├── FEED_SCALE/         # RS485 喂料秤与定量投喂状态机
│   ├── RELAY/              # 继电器控制、反馈检测、平台指令执行、断电记忆恢复
│   ├── NETWORK/            # 网络层network.c 通道切换/统一分发 + net_wifi.c WiFi 后端 + net_wifi_ota.c WiFi OTA
│   ├── WIFI/               # esp8266.c ESP-01S AT驱动/透传/SmartConfig + mqtt_client.c 软件MQTT
│   ├── CAR/                # Air780E/EG AT 驱动、波特率自适应、CSQ 信号检测、GNSS 定位、OTA 下载
│   ├── UART/               # USART1/2/3+UART4 初始化、运行中切波特率、中断/DMA 接收、ORE 自愈
│   ├── 24C02/              # EEPROM 读写(页写入)
│   ├── IIC/                # 软件 I2C
│   ├── ADC/                # 电池电压检测
│   ├── LED/                # 指示灯LED2 兼任 TPL5010 喂狗)
│   ├── LOG/                # 分级日志USART1 输出)
│   ├── SYSINIT/            # Board_Init/IWDG、app_init、主循环调度、后台任务切片
│   └── W25Q64/             # 外部 Flash 驱动与 OTA 双槽信息区
└── MDK-ARM/                # Keil 工程文件

STM32F103_BootLoader/
├── Src/main.c              # 硬件初始化 + IWDG + 入口
├── m_app/ota_boot.c        # OTA 状态机:备份/烧录/试运行计数/回退/跳转
├── HardWare/W25Q64/        # 外部 Flash 驱动(与 APP 共用设计)
├── HardWare/LOG/           # 分级日志(与 APP 相同)
└── MDK-ARM/                # Keil 工程文件

编译烧录

Keil GUI

  1. 打开 MDK-ARM/STM32F103rb_App1.uvprojxBootLoader 工程同理)
  2. 按产品形态设置 Inc/main.h 中的 PRODUCT_WITH_FEED_SCALE
  3. 编译下载(先烧 BootLoader 到 0x08000000APP1 起始地址 0x08008000
  4. 首次启动会自动把默认 MQTT 配置写入 AT24C02

命令行CI/脚本可用):

# 编译Keil 安装目录的 UV4-r 全量 -b 增量,-o 输出日志)
/c/Keil_v5/UV4/UV4.exe -r -j0 STM32F103rb_App1.uvprojx -o build.txt

# 烧录J-Link 命令脚本,工程 MDK-ARM 目录下有现成的 jlink_flash.jlink
JLink.exe -CommanderScript jlink_flash.jlink

版本

  • Version: 1.3.0
  • Author: Helei