485扩展继电器板状态断电记忆+上线恢复(Relay_State[5]位掩码存EEPROM,离线->上线自动比对纠正);版本号不动保持1.7.1

This commit is contained in:
helei 2026-09-04 21:23:05 +08:00
parent b658a403c8
commit 8f0e137c57
4 changed files with 84 additions and 11 deletions

View File

@ -68,7 +68,7 @@
| KEY1~4 | **PC6/PC7/PB14/PB15 上拉输入,功能预留** |
| 取消 | PA8 手动模式开关MANUAL_MODE 已删) |
**485 总线多从机V1.7.1 起)**:称重秤 0x01Modbus 读重量)+ 扩展继电器板 0x10FC05 控制/FC03 读状态,协议见 `Docs/RS485扩展继电器协议.md`);收帧统一在 feedScaleProcess 按站号分发,发送共用 uart.c 的 rs485_bus 时间戳做 50ms 静默保护;平台标识符 sw5~sw8蓝牙 R5ON~R8OFF
**485 总线多从机V1.7.1 起)**:称重秤 0x01Modbus 读重量)+ 扩展继电器板 0x10FC05 控制/FC03 读状态,协议见 `Docs/RS485扩展继电器协议.md`);收帧统一在 feedScaleProcess 按站号分发,发送共用 uart.c 的 rs485_bus 时间戳做 50ms 静默保护;平台标识符 sw5~sw8蓝牙 R5ON~R8OFF扩展板状态断电记忆Relay_State[5] 位掩码),离线→上线自动比对恢复
## 4. 云平台协议
@ -81,7 +81,7 @@
## 5. EEPROM 布局AT24C02两个终端共用
- 0~110`MqttInfo_Str`ClientID/Username/Password/ServerIP/Topic/Port/Relay_State[7]/Magic=0xDEADBEEF/Ver
- 102继电器状态120~124秤去皮/单次投料/初始化标志
- 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**

View File

@ -75,7 +75,8 @@
## 4. 从机行为要求
1. **上电默认 4 路全部断开**(主控不负责恢复扩展板断电前状态)
1. **上电默认 4 路全部断开**(主控侧有断电记忆:每次离线→上线时,主控会比对 EEPROM 记忆状态与 REG0 实际状态,
不一致的回路会**逐路补发 FC05 纠正**——所以从机上线后紧跟着收到几条 FC05 属正常恢复流程,务必如实执行并回显)
2. FC05 收到并校验通过(地址/功能码/线圈范围/CRC后**立即执行**再回显
3. 反馈输入:每路继电器配负载反馈检测(同主控板载继电器:吸合后负载得电=高)。从机自行维护 REG2 故障位:某路输出状态与反馈不一致**持续超过 1s** 置 1恢复一致后清 0
4. 反馈采样建议 10~50ms 周期,加简单消抖(如连续 3 次一致才更新 REG1
@ -88,6 +89,7 @@
- **命令流程**:平台/蓝牙下发 → FC05 → 等回显300ms 超时,补发 1 次)→ 延时 150ms 等触点稳定 → FC03 读状态 → 校验"输出位=目标状态 且 故障位=0" → 应答平台 OK/失败 → 上报全状态
- **周期轮询**:空闲每 2s 发一次 FC03兼在线检测连续 3 次无应答判离线(日志提示)
- **断电记忆恢复**:扩展板状态存在主控 EEPROM偏移 102 的 Relay_State[5]bit0~3每次离线→上线时比对实际状态与记忆不一致的回路逐路补发 FC05 纠正,恢复完成统一上报一次
- **离线行为**:离线期间下发命令直接回复平台 "ext relay board offline";板子恢复应答后自动上线并补一次状态上报
- 上电 3s 内不发帧(避开上电窗口)

View File

@ -10,6 +10,7 @@
#include "ext_relay.h"
#include "relay.h"
#include "24c02.h"
#include "uart.h"
#include "log.h"
#include "network.h"
@ -88,6 +89,7 @@ static uint8_t cmd_pending = 0; /*
static uint8_t cmd_stage = 0; /* 0闲 1等FC05回显 2触点稳定延时 3等FC03应答 */
static uint8_t cmd_ch = 0, cmd_on = 0, cmd_retry = 0;
static uint8_t cmd_is_poll = 0; /* 当前事务是轮询(不应答平台) */
static uint8_t cmd_is_restore = 0;/* 当前事务是断电恢复(不应答平台) */
static uint32_t cmd_tick = 0;
static char cmd_id[20] = {0};
@ -96,17 +98,52 @@ static uint8_t rx_echo_ok = 0;
static uint8_t rx_state_ok = 0;
static uint8_t rx_exc = 0;
#define EXT_STATE_EEPROM_IDX 5 /* MqttInfoStr.Relay_State[5] 原保留字节: 扩展板状态位掩码 */
/* 断电恢复状态 */
static uint8_t restore_diff = 0; /* 待恢复位(实际!=记忆) */
static uint8_t restore_target = 0; /* 恢复目标掩码(EEPROM 记忆) */
static uint32_t restore_next_tick = 0; /* 恢复失败后的重试退避 */
/* EEPROM 记忆的扩展板状态(0xFF=空白视为全断) */
static uint8_t ext_saved_mask(void)
{
uint8_t v = MqttInfoStr.Relay_State[EXT_STATE_EEPROM_IDX];
if (v == 0xFF) return 0;
return (uint8_t)(v & 0x0F);
}
/* 以 FC03 读到的实际输出状态刷新 EEPROM(变化才写) */
static void ext_save_state(void)
{
uint8_t nv = (uint8_t)(s_state & 0x0F);
if (MqttInfoStr.Relay_State[EXT_STATE_EEPROM_IDX] != nv) {
MqttInfoStr.Relay_State[EXT_STATE_EEPROM_IDX] = nv;
eepromWriteData(102, &MqttInfoStr.Relay_State[0], 7);
}
}
uint8_t ext_relay_online(void) { return s_online; }
uint8_t ext_relay_state_mask(void) { return s_state; }
uint8_t ext_relay_fault_mask(void) { return s_fault; }
static void ext_note_success(void)
static void ext_note_success(int fresh_state)
{
s_miss = 0;
if (!s_online) {
s_online = 1;
log_info("> EXT: 扩展继电器板上线 (从机版本 V%u.%02X)", s_ver >> 8, s_ver & 0xFF);
relayStatueUpdata(); /* 上线后补一次全状态, 平台刷新 sw5~sw8 */
/* 断电恢复: 刚上线且手头是最新状态时, 比对 EEPROM 记忆, 不一致的回路待逐路补发 */
if (fresh_state) {
uint8_t saved = ext_saved_mask();
uint8_t diff = (uint8_t)((s_state ^ saved) & 0x0F);
if (diff) {
restore_diff = diff;
restore_target = saved;
log_warn("> EXT: 扩展板状态与断电记忆不符(实际=%02X 记忆=%02X), 逐路恢复", s_state, saved);
}
}
}
}
@ -208,7 +245,7 @@ void ext_relay_process(void)
if (cmd_stage == 1) { /* 等 FC05 回显 */
if (rx_echo_ok) {
rx_echo_ok = 0;
ext_note_success();
ext_note_success(0);
cmd_stage = 2;
cmd_tick = now;
} else if (rx_exc || (now - cmd_tick) > EXT_RESP_TIMEOUT_MS) {
@ -219,7 +256,8 @@ void ext_relay_process(void)
cmd_tick = now;
} else {
log_warn("> EXT: SW%d 命令无应答", cmd_ch + 4);
ext_respond(0, "ext relay no response");
if (!cmd_is_restore) ext_respond(0, "ext relay no response");
else { cmd_is_restore = 0; restore_next_tick = now + 2000; }
ext_note_fail();
cmd_stage = 0;
}
@ -240,8 +278,17 @@ void ext_relay_process(void)
if (cmd_stage == 3) { /* 等 FC03 状态应答(命令收尾或周期轮询共用) */
if (rx_state_ok) {
rx_state_ok = 0;
ext_note_success();
if (cmd_is_poll) {
ext_note_success(1);
if (cmd_is_restore) {
/* 断电恢复的一路已完成: 清位, 全部完成才统一保存+上报 */
cmd_is_restore = 0;
restore_diff &= (uint8_t)~(1u << (cmd_ch - 1));
if (!restore_diff) {
log_info("> EXT: 断电状态恢复完成 (输出=%02X)", s_state);
ext_save_state();
relayStatueUpdata();
}
} else if (cmd_is_poll) {
cmd_is_poll = 0;
if (s_fault) {
log_warn("> EXT: 反馈异常标志=%02X (输出=%02X 反馈=%02X)", s_fault, s_state, s_fb);
@ -257,6 +304,7 @@ void ext_relay_process(void)
cmd_ch + 4, cmd_on ? "" : "", s_state, s_fb, s_fault);
ext_respond(0, "ext relay failed / load unpowered");
}
ext_save_state(); /* 断电记忆: 以实际输出状态落盘 */
relayStatueUpdata(); /* 命令完成后上报全状态 */
}
cmd_stage = 0;
@ -267,11 +315,12 @@ void ext_relay_process(void)
ext_send_fc03();
cmd_tick = now;
} else {
if (!cmd_is_poll) {
if (!cmd_is_poll && !cmd_is_restore) {
log_warn("> EXT: SW%d 状态确认无应答", cmd_ch + 4);
ext_respond(0, "ext relay no response");
}
cmd_is_poll = 0;
if (cmd_is_restore) { cmd_is_restore = 0; restore_next_tick = now + 2000; }
ext_note_fail();
cmd_stage = 0;
}
@ -279,6 +328,23 @@ void ext_relay_process(void)
return;
}
/* 断电恢复: 逐路补发 FC05 使扩展板回到 EEPROM 记忆状态 */
if (restore_diff && now >= restore_next_tick) {
uint8_t bit = (uint8_t)(restore_diff & (uint8_t)(-(int)restore_diff)); /* 最低待恢复位 */
uint8_t ch = 1;
while (!(bit & 1)) { bit >>= 1; ch++; }
cmd_is_poll = 0;
cmd_is_restore = 1;
cmd_ch = ch;
cmd_on = (uint8_t)((restore_target >> (ch - 1)) & 1u);
cmd_retry = 0;
cmd_stage = 1;
ext_send_fc05(cmd_ch, cmd_on);
cmd_tick = now;
log_info("> EXT: 恢复 SW%d -> %s", ch + 4, cmd_on ? "" : "");
return;
}
/* 空闲: 周期轮询(兼在线检测) */
if ((now - s_last_poll) >= EXT_POLL_INTERVAL_MS) {
s_last_poll = now;

View File

@ -15,9 +15,14 @@
- **蓝牙命令新增 R5ON~R8OFF**STATUS 显示扩展板在线状态与各路开关/故障位
- 协议文档:`Docs/RS485扩展继电器协议.md`(从机板按此实现)
### 追加2026-09-04
- **扩展板状态断电记忆+上电恢复**(与板载继电器一致):状态位掩码存 `MqttInfoStr.Relay_State[5]`(原保留字节,
bit0~3=扩展 1~4 路0xFF 空白视为全断),复用 EEPROM 偏移 102 现有落盘路径;
扩展板每次离线→上线时比对 FC03 实际状态与 EEPROM 记忆,不一致的回路逐路补发 FC05 纠正
(从机上电默认全断,即实现"断电后重连恢复到断电前状态"),恢复完成统一保存+上报一次
### 说明
- 扩展板不参与断电记忆恢复(从机上电默认全断);不挂扩展板时功能无影响(轮询无应答记离线日志)
- 无秤模式同样可用扩展板
- 不挂扩展板时功能无影响(轮询无应答记离线日志);无秤模式同样可用扩展板
## V1.7.0 —— 新硬件板型引脚重排2026-09-04不兼容老板仓库迁至 shuichan_new