13 KiB
项目上下文(AI 协作速查,新会话开场贴给 AI 阅读)
本文件汇总多项目的结构、约定和踩坑记录。新会话开始时提供给 AI,可跳过漫长的项目探索。
1. 仓库与工程
| 项目 | 本地路径 | git 仓库 | 说明 |
|---|---|---|---|
| 控制终端(投料机/四路继电器) | 控制终端/STM32F103_App1 + STM32F103_BootLoader |
ssh://nky@58.17.14.95:90/helei/shuichan_new.git |
当前 V1.7.0 (FreeRTOS,新板引脚重排,不兼容老板;老仓库 shuichan.git 定格 V1.6.5) |
| 采集终端(农业传感器采集) | 采集终端/V1.0/STM32F103_App1 + STM32F103_BootLoader |
http://58.17.14.95:8001/helei/nongye.git |
当前 V1.0.1 |
| 自制土壤传感器 | 土壤传感器/代码/Soil_HR_TP_V0.2(APP+BL)+ soil_calib.py/soil_calib_gui.py + dist/土壤标定工具.exe |
无 git | STM32F030,FreeModbus |
| 自制叶面传感器 | 传感器/叶面传感器/代码/Leaf_HR_TP_V1.0(APP+BL) |
无 git | STM32F030,FreeModbus |
| 百叶箱环境采集 | 传感器/百叶窗环境采集/代码/环境采集终端代码-无气压传感器/terminal-appV2兼容精讯 |
无 git | STM32F030,FreeModbus |
| LoRa 参考工程 | LoRA/GW、LoRA/SUB、LoRA/参考 |
无 git | 网关/从机 V1.7 参考 |
- 云平台:私有 MQTT broker
58.17.14.95:1880,topic/iot/data/up|down/<ClientID> - 编译:Keil
C:\Keil_v5\ARM\ARMCC,命令行UV4.exe -b <proj>.uvprojx -j0 -o Log/build.log,bin 产物在OutputBin/ - 烧录:J-Link(
JLink_V796e),BL 不能 OTA 只能线刷 - 提交 git 前先跑
keilkill.bat清编译产物
2. 源码约定(极重要,反复踩坑)
-
版本号
FIRMWARE_VERSION_STR不要主动加:功能更新时用户没明确说升版本就不动(小改动也涨版本会失控) -
所有 APP 源文件(.c/.h)是 GBK 编码,Read/Edit 工具无法直接处理,一律用 python 脚本(读 gbk、改、encode 验证后写)
-
heredoc 转义陷阱(python 脚本写 C 代码时高发):
\22在 python 字符串里会变成八进制 0x12——构造 AT 命令 JSON 转义时必须用chr(92)拼反斜杠'\\r'/'\\n'会被写成字面 CR/LF 导致编译错——用chr(13)或chr(92)+'r'构造\a会变 BEL(0x07) 曾把 uvprojx 搞坏- 写中文字符串后必须
s.encode('gbk')验证再落盘
-
Keil 工程必须带编译选项
--no_multibyte_chars(中文字符串字节原样输出) -
串口日志是 GBK 中文,上位机/蓝牙 APP 字符集必须选 GBK(否则乱码+吞换行)
-
修改文件路径含中文时,先
cd进目录再用相对路径(绝对路径在 bash/python 里偶发编码错乱)
3. 硬件资源分配(两个终端共用板型,GD32F103RCT6 256K)
注意:下表为老板型(采集终端仍用)。控制终端 V1.7.0 起换新板,引脚重排见本节末尾附表。
| 用途 | 接口 | 引脚/说明 |
|---|---|---|
| 日志+蓝牙 HLK-B40 | USART1 | PA9/PA10,115200;BT_MODE=PB0(开漏),BT_RST=PA7 |
| 485 传感器 | USART2 | PA2/PA3,9600,DMA+IDLE 定帧 |
| 4G Air780E/EG | USART3 | PB10/PB11,921600;CAT_PWR=PC4(开漏拉低=开机,置高=断电) |
| WiFi ESP-01S | UART4 | PC10/PC11,921600;ESP_RST=PA5,ESP_EN=PA6(高有效) |
| HLW8032 计量 | UART5 | PD2 只收(仅控制终端) |
| 继电器×4 | 输出 | PB7/PB8/PC0/PC2,高吸合;反馈 PB6/PB9/PC1/PC3 |
| 电量/断电检测 | ADC1 | PA0,分压比 11 |
| OLED(带字库IC) | 软件SPI | CLK=PC12 MOSI=PC9 DC=PC8 CS1=PC7 FSO=PC6 CS2=PB15 RST=PB14 |
| W25Q64 | 软件SPI | CS=PB4 CLK=PB3 DO=PB5 DI=PA15 |
| EEPROM AT24C02 | 软件I2C | 存 MQTT 配置等 |
| LED | LED2=PC13(喂 TPL5010 外狗),NET_LED=PA1 | |
| 看门狗 | IWDG + TPL5010 | idle 钩子喂 IWDG;LED2 翻转喂外狗,BL 等待循环也必须喂外狗 |
控制终端新板引脚表(V1.7.0 起,仅控制终端)
| 用途 | 引脚 |
|---|---|
| 串口(USART1 蓝牙日志 / USART2 485 / USART3 4G / UART4 WiFi / UART5 计量) | 不变:PA9/PA10、PA2/PA3、PB10/PB11、PC10/PC11、PD2 |
| CAT_PWR(DCDC EN,4G 供电) | PC1 开漏,释放=工作、拉低=掉电(与老板反相!) |
| CAT_RST(直连 Air780E 复位脚) | PA4 开漏,拉低=复位、释放=工作(复位脉冲后必须释放) |
| BT_MODE / BT_RST | PC4 / PA7 |
| RELAY1~4 / 反馈 | PC2/PA1/PB12/PB1;反馈 PC0/PC3/PC5/PB0 |
| NET_LED | PB13(LED2 仍 PC13) |
| EEPROM I2C(SCL/SDA) | PB5/PB4 |
| W25Q64(CS/DO/CLK/DI) | PB8/PB9/PB6/PB7 |
| OLED(CS2/FSO/CS1/DC/MOSI/CLK/RES) | PC12/PA12/PC8/PC9/PA15/PB3/PA11 |
| HR_IN 缺料检测 | PA8 下拉输入,高=缺料→停投料关继电器1(只记日志),不接传感器不影响投料 |
| KEY1~4 | PC6/PC7/PB14/PB15 上拉输入,功能预留 |
| 取消 | PA8 手动模式开关(MANUAL_MODE 已删) |
| 开机二维码 | 已配置身份时开机 OLED 显示 10s:内容=ClientID(qrcodegen 生成),右侧附 ID 文本 |
485 总线多从机(V1.7.1 起):称重秤 0x01(Modbus 读重量)+ 扩展继电器板 0x10(FC05 控制/FC03 读状态,协议见 Docs/RS485扩展继电器协议.md);收帧统一在 feedScaleProcess 按站号分发,发送共用 uart.c 的 rs485_bus 时间戳做 50ms 静默保护;平台标识符 sw5~sw8,蓝牙 R5ON~R8OFF;扩展板状态断电记忆(Relay_State[5] 位掩码),离线→上线自动比对恢复
4. 云平台协议
- 上报:
{"header":"iot.prop.post","body":{...}};4G 通道 AT 报文里引号用字面三字符\22(C 源码写\\22),不能丢反斜杠(踩过两次) - 平台只认 UTF-8:发往平台的中文文案不能直接写汉字(GBK 源码→GBK 字节→平台乱码,实测),必须用 UTF-8 字节的
\x转义写法(如"负载断开或继电器损坏"="\xE8\xB4\x9F\xE8\xBD\xBD\xE6\x96\xAD\xE5\xBC\x80\xE6\x88\x96\xE7\xBB\xA7\xE7\x94\xB5\xE5\x99\xA8\xE6\x8D\x9F\xE5\x9D\x8F");串口日志则相反必须 GBK(上位机字符集是 GBK) - 下行:
iot.prop.set(控制终端继电器)、iot.ota.upgrade.post(OTA) - 标识符:sw1~sw4/feeding(板载继电器)、sw5~sw8(485 扩展继电器板,V1.7.1 起,在线才上报)、weight/zero/onceWt(秤)、energy/pow、power="220V,0.21A,46.2W"字符串、GPS("经度,纬度"高德格式)、iccid、fwver("1.6.5:3"版本串+序号)、ver(数字版本序号)、BAT_VAL(采集终端电量)
- 传感器标识符(采集终端):PH/EC/BS_T/BS_RH/BS_DO/DS_T/DO/BY_TP/BY_RH/BY_HV/BY_CO2/YSMD_TP/YSMD_RH/SOIL_TP/RH/NV/PV/KV/PH/RV/WIND
- 协议解析助手在
m_app/proto.c(不用完整 JSON 库)
5. EEPROM 布局(AT24C02,两个终端共用)
- 0~110:
MqttInfo_Str(ClientID/Username/Password/ServerIP/Topic/Port/Relay_State[7]/Magic=0xDEADBEEF/Ver) - 102:继电器状态(Relay_State[1~4]=板载 sw1~sw4,[5]=485 扩展板 sw5~sw8 位掩码,0xFF 空白视为全断);120~124:秤去皮/单次投料/初始化标志
- 132~157:BLE 名缓存;158:BLE 功率;160:日志等级
- 164~171:电能脉冲(控制终端);172:产品模式;173:GPS 开关;175~194:OTA 任务 id
- 200~244:WiFi 热点配置(magic=0xA6@200,SSID 22B@201,PASS 22B@223)
- 250:EEPROM 探测字节
- 身份配置:固件不含 MQTT 身份,
SETMQTT蓝牙灌入 EEPROM;Magic+ClientID 非空=已配置,否则进待配置模式(跳过网络,OLED 提示) FACTORY:整片 EEPROM 写 0xFF 恢复出厂(清 WiFi 配置但不清 ESP 模块里 SmartConfig 的热点,那个用 WIFIRESET)
6. 蓝牙命令(当前全集,不区分大小写,空格分隔参数)
RESET / INFO / MQTTINFO / STATUS / HELP(逐条中文) SETMQTT id user pass(保存后自动重启)/ SETIP ip port / FACTORY / SETWIFI ssid pass / WIFIINFO / WIFICLEAR / WIFIRESET R1ON~R4OFF/ALLON/ALLOFF(控制终端板载)/ R5ON~R8OFF(485 扩展继电器板,V1.7.1 起) / FEEDSTART kg / FEEDSTOP / FEED(控制终端投喂) SCALE/SCALE0/SCALE1(控制终端)/ GPS/GPS0/GPS1 / ENCLR(控制终端电能清零)/ HISTORY(控制终端重启记录)/ BLESET / LOGTRACE~LOGFATAL / OTA
7. OTA 架构(两终端相同)
- 流程:MQTT 下发 iot.ota.upgrade.post → HTTP Range 3072B/块下载 → W25Q64 暂存 → CRC16+固件头校验 → 复位进 BL → BL 备份 APP1/搬运/试运行计数(3 次失败回退)→ APP 联网后确认(MQTT ready + 首次发布 + 延迟9s)
- 版本防重复:
new_ver > MqttInfoStr.Ver才下载;健康检查:MQTT ready 后 1 分钟无首次 MPUB 复位并消耗一次试错 - OLED 进度页:
NET_UI_OTA阶段 +g_ota_pct(0~100),入口air780e_trigger_ota/net_wifi_ota置位 - OTA 期间业务全停:
g_ota_active标志,sensor_process/relayCmdProcess 跳过;WiFi 中止自动恢复,4G 失败路径本就复位
8. 网络层(network.c 抽象)
- 4G(模组内置 MQTT,Air780 AT 指令)与 WiFi(ESP-01S TCP 透传+MCU 软件 MQTT 3.1.1)统一 NET_* 接口,共享 U2_CopyBuff 接收缓冲,net_at_mutex 互斥
- 通道选择(V1.7.1 起改为开机按键手动选择,仅控制终端新板):欢迎页后 10s 倒计时,KEY1=4G/KEY2=WiFi固定/KEY3=配网,超时=EEPROM(130)记忆的上次选择(首次默认4G);选定后锁定通道原地重试,不再自动回退;老版本(含采集终端)仍是 EEPROM 记忆优先+4G/WiFi 自动回退循环
- 心跳阶梯(防误复位):30s PINGREQ,无应答立即 3s 快探测,连续 5 次(约42s)判死 → 先原 TCP 重发 CONNECT,失败再完整重建链路,不整机复位
- ESP_EN 断电重启:esp_reset 先断电 500ms 再上电(治模块半死);CWJAP 冷启动失败自动重试一次;连上后 AT+CWJAP? 打印信号强度%(含 busy 重试)
- WiFi 就绪后 4G 断电省电(老板 CAT_PWR 置高;控制终端新板 PC1 反相为拉低=掉电)
9. 485 传感器总线(采集终端)
| 地址 | 传感器 | 寄存器 | 上报标识符 |
|---|---|---|---|
| 0x01 | 水体PH-EC | 0x0000/2 | PH, EC |
| 0x02 | 大气压温湿度 | 0x0000/12 | BS_T, BS_RH, BS_DO |
| 0x03 | 溶解氧DO59 | 0x2500启动/0x2600读4 | DS_T, DO |
| 0x06 | 百叶箱 | 0x0000/9 | BY_TP, BY_RH, BY_HV, BY_CO2 |
| 0x07 | 叶面温湿度(自制) | 0x0020/2 | YSMD_TP, YSMD_RH |
| 0x08 | 土壤温湿度(自制) | 0x0012/2 | SOIL_TP, SOIL_RH |
| 0x09 | 氮磷钾 | 0x001E/3 | SOIL_NV/PV/KV |
| 0x0A | 土壤PH | 0x0006/1 | SOIL_PH |
| 0x0B | 土壤EC | 0x0015/1 | SOIL_RV |
| 0x0C | 风速(聚碳) | 0x0000/1 值×10 | WIND |
- 传感器间必须 ≥50ms 间隔(从机 485 释放/回声沉降,否则下一个查询撞帧读不到——实测踩坑)
- DO59 必须用真实地址 0x03 查询,禁用 0xFF 通配(聚碳风速也认 0xFF 会抢答导致误上线)
- 各传感器实际地址可能和表不符(不同批次),接不上先查地址;精讯 JXBS 改地址寄存器 0x0100,聚碳风速 0x07D0+波特率 0x07D1
10. 自制传感器固件(土壤/叶面,STM32F030)
- 寄存器:0x12湿度/0x13温度(0.1单位),0xB5 实时 ADC,0xA0 标定标志(0x8000+点数),0xA1~RH点,0xAB~ADC点,0x0100 地址,0x0101 波特率(0/1/2=2400/4800/9600)
- 标定写入顺序:先清标志→写点→最后写标志(标志变化才落 Flash);先写点后写标志否则不保存
- 土壤 ADC:湿度越大 ADC 越大;叶面相反(湿度越大 ADC 越小)——上位机已做方向自动识别
- 标定:两点法(空气=0%/水中=100%)够用;多点法需天平+105℃烘干箱按官方流程
- 上位机:
土壤传感器/soil_calib_gui.py→ PyInstaller 打包dist/土壤标定工具.exe(单文件免环境);打包环境在packenv/+python/(独立 Python3.13,本机 Python3.14 太新不支持 PyInstaller);GitHub 下载需开系统代理(127.0.0.1:7897) - 编译老工程(2023 年 SPL 工程)三件套:IncludePath 补
Library\CMSIS\Device\ST\STM32F0xx\Include+Library\CMSIS\Include(DFP 版本冲突)、fromelf 路径改本机、pArmCC/pCCUsed 编译器锁定字段清空;Flash 算法$$Device宏失效要改绝对路径C:\Keil_v5\ARM\PACK\Keil\STM32F0xx_DFP\2.1.0\CMSIS\Flash\STM32F0xx_64.FLM
11. 已知历史问题(已修,勿再引入)
- FreeModbus portserial.c:TC 中断重复驱动发送状态机(查询快传感器重启)——TC 只清标志
- SHT20 测量就绪 do-while 无超时——已加 200 次上限(约1.2s)返回上次值
- BL 门禁等待循环只喂 IWDG 没喂 TPL5010 外狗——已补 EXT_WDOG_FEED
- 低电压两级滞回:APP <10% 主动复位,BL <20% 不放行(阈值按各自 BAT 公式算 ADC 原始值)
- 心跳超时复位后发现热点 CWJAP:3(路由器没活)→ 冷启动重试已加