shuichan_new/AI项目上下文.md

151 lines
13 KiB
Markdown
Raw Permalink Normal View History

# 项目上下文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 | STM32F030FreeModbus |
| 自制叶面传感器 | `传感器/叶面传感器/代码/Leaf_HR_TP_V1.0`APP+BL | 无 git | STM32F030FreeModbus |
| 百叶箱环境采集 | `传感器/百叶窗环境采集/代码/环境采集终端代码-无气压传感器/terminal-appV2兼容精讯` | 无 git | STM32F030FreeModbus |
| 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/PA10115200BT_MODE=PB0(开漏)BT_RST=PA7 |
| 485 传感器 | USART2 | PA2/PA39600DMA+IDLE 定帧 |
| 4G Air780E/EG | USART3 | PB10/PB11921600CAT_PWR=PC4(开漏拉低=开机,置高=断电) |
| WiFi ESP-01S | UART4 | PC10/PC11921600ESP_RST=PA5ESP_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 钩子喂 IWDGLED2 翻转喂外狗,**BL 等待循环也必须喂外狗** |
### 控制终端新板引脚表V1.7.0 起,仅控制终端)
| 用途 | 引脚 |
| --- | --- |
| 串口USART1 蓝牙日志 / USART2 485 / USART3 4G / UART4 WiFi / UART5 计量) | 不变PA9/PA10、PA2/PA3、PB10/PB11、PC10/PC11、PD2 |
| CAT_PWRDCDC EN4G 供电) | **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 I2CSCL/SDA | **PB5/PB4** |
| W25Q64CS/DO/CLK/DI | **PB8/PB9/PB6/PB7** |
| OLEDCS2/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内容=ClientIDqrcodegen 生成),右侧附 ID 文本 |
**485 总线多从机V1.7.1 起)**:称重秤 0x01Modbus 读重量)+ 扩展继电器板 0x10FC05 控制/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~sw8485 扩展继电器板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~157BLE 名缓存158BLE 功率160日志等级
- 164~171电能脉冲控制终端172产品模式173GPS 开关175~194OTA 任务 id
- **200~244WiFi 热点配置magic=0xA6@200SSID 22B@201PASS 22B@223**
- 250EEPROM 探测字节
- **身份配置**:固件不含 MQTT 身份,`SETMQTT` 蓝牙灌入 EEPROMMagic+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~R8OFF485 扩展继电器板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模组内置 MQTTAir780 AT 指令)与 WiFiESP-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 实时 ADC0xA0 标定标志(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 太新不支持 PyInstallerGitHub 下载需开系统代理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.cTC 中断重复驱动发送状态机查询快传感器重启——TC 只清标志
- SHT20 测量就绪 do-while 无超时——已加 200 次上限约1.2s)返回上次值
- BL 门禁等待循环只喂 IWDG 没喂 TPL5010 外狗——已补 EXT_WDOG_FEED
- 低电压两级滞回APP <10% 主动复位BL <20% 不放行阈值按各自 BAT 公式算 ADC 原始值
- 心跳超时复位后发现热点 CWJAP:3路由器没活→ 冷启动重试已加