485扩展继电器板状态断电记忆+上线恢复(Relay_State[5]位掩码存EEPROM,离线->上线自动比对纠正);版本号不动保持1.7.1
This commit is contained in:
parent
b658a403c8
commit
8f0e137c57
|
|
@ -68,7 +68,7 @@
|
||||||
| KEY1~4 | **PC6/PC7/PB14/PB15 上拉输入,功能预留** |
|
| KEY1~4 | **PC6/PC7/PB14/PB15 上拉输入,功能预留** |
|
||||||
| 取消 | PA8 手动模式开关(MANUAL_MODE 已删) |
|
| 取消 | PA8 手动模式开关(MANUAL_MODE 已删) |
|
||||||
|
|
||||||
**485 总线多从机(V1.7.1 起)**:称重秤 0x01(Modbus 读重量)+ 扩展继电器板 0x10(FC05 控制/FC03 读状态,协议见 `Docs/RS485扩展继电器协议.md`);收帧统一在 feedScaleProcess 按站号分发,发送共用 uart.c 的 rs485_bus 时间戳做 50ms 静默保护;平台标识符 sw5~sw8,蓝牙 R5ON~R8OFF
|
**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. 云平台协议
|
## 4. 云平台协议
|
||||||
|
|
||||||
|
|
@ -81,7 +81,7 @@
|
||||||
## 5. EEPROM 布局(AT24C02,两个终端共用)
|
## 5. EEPROM 布局(AT24C02,两个终端共用)
|
||||||
|
|
||||||
- 0~110:`MqttInfo_Str`(ClientID/Username/Password/ServerIP/Topic/Port/Relay_State[7]/Magic=0xDEADBEEF/Ver)
|
- 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~157:BLE 名缓存;158:BLE 功率;160:日志等级
|
- 132~157:BLE 名缓存;158:BLE 功率;160:日志等级
|
||||||
- 164~171:电能脉冲(控制终端);172:产品模式;173:GPS 开关;175~194:OTA 任务 id
|
- 164~171:电能脉冲(控制终端);172:产品模式;173:GPS 开关;175~194:OTA 任务 id
|
||||||
- **200~244:WiFi 热点配置(magic=0xA6@200,SSID 22B@201,PASS 22B@223)**
|
- **200~244:WiFi 热点配置(magic=0xA6@200,SSID 22B@201,PASS 22B@223)**
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,8 @@
|
||||||
|
|
||||||
## 4. 从机行为要求
|
## 4. 从机行为要求
|
||||||
|
|
||||||
1. **上电默认 4 路全部断开**(主控不负责恢复扩展板断电前状态)
|
1. **上电默认 4 路全部断开**(主控侧有断电记忆:每次离线→上线时,主控会比对 EEPROM 记忆状态与 REG0 实际状态,
|
||||||
|
不一致的回路会**逐路补发 FC05 纠正**——所以从机上线后紧跟着收到几条 FC05 属正常恢复流程,务必如实执行并回显)
|
||||||
2. FC05 收到并校验通过(地址/功能码/线圈范围/CRC)后**立即执行**再回显
|
2. FC05 收到并校验通过(地址/功能码/线圈范围/CRC)后**立即执行**再回显
|
||||||
3. 反馈输入:每路继电器配负载反馈检测(同主控板载继电器:吸合后负载得电=高)。从机自行维护 REG2 故障位:某路输出状态与反馈不一致**持续超过 1s** 置 1,恢复一致后清 0
|
3. 反馈输入:每路继电器配负载反馈检测(同主控板载继电器:吸合后负载得电=高)。从机自行维护 REG2 故障位:某路输出状态与反馈不一致**持续超过 1s** 置 1,恢复一致后清 0
|
||||||
4. 反馈采样建议 10~50ms 周期,加简单消抖(如连续 3 次一致才更新 REG1)
|
4. 反馈采样建议 10~50ms 周期,加简单消抖(如连续 3 次一致才更新 REG1)
|
||||||
|
|
@ -88,6 +89,7 @@
|
||||||
|
|
||||||
- **命令流程**:平台/蓝牙下发 → FC05 → 等回显(300ms 超时,补发 1 次)→ 延时 150ms 等触点稳定 → FC03 读状态 → 校验"输出位=目标状态 且 故障位=0" → 应答平台 OK/失败 → 上报全状态
|
- **命令流程**:平台/蓝牙下发 → FC05 → 等回显(300ms 超时,补发 1 次)→ 延时 150ms 等触点稳定 → FC03 读状态 → 校验"输出位=目标状态 且 故障位=0" → 应答平台 OK/失败 → 上报全状态
|
||||||
- **周期轮询**:空闲每 2s 发一次 FC03,兼在线检测;连续 3 次无应答判离线(日志提示)
|
- **周期轮询**:空闲每 2s 发一次 FC03,兼在线检测;连续 3 次无应答判离线(日志提示)
|
||||||
|
- **断电记忆恢复**:扩展板状态存在主控 EEPROM(偏移 102 的 Relay_State[5],bit0~3);每次离线→上线时比对实际状态与记忆,不一致的回路逐路补发 FC05 纠正,恢复完成统一上报一次
|
||||||
- **离线行为**:离线期间下发命令直接回复平台 "ext relay board offline";板子恢复应答后自动上线并补一次状态上报
|
- **离线行为**:离线期间下发命令直接回复平台 "ext relay board offline";板子恢复应答后自动上线并补一次状态上报
|
||||||
- 上电 3s 内不发帧(避开上电窗口)
|
- 上电 3s 内不发帧(避开上电窗口)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include "ext_relay.h"
|
#include "ext_relay.h"
|
||||||
#include "relay.h"
|
#include "relay.h"
|
||||||
|
#include "24c02.h"
|
||||||
#include "uart.h"
|
#include "uart.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "network.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_stage = 0; /* 0闲 1等FC05回显 2触点稳定延时 3等FC03应答 */
|
||||||
static uint8_t cmd_ch = 0, cmd_on = 0, cmd_retry = 0;
|
static uint8_t cmd_ch = 0, cmd_on = 0, cmd_retry = 0;
|
||||||
static uint8_t cmd_is_poll = 0; /* 当前事务是轮询(不应答平台) */
|
static uint8_t cmd_is_poll = 0; /* 当前事务是轮询(不应答平台) */
|
||||||
|
static uint8_t cmd_is_restore = 0;/* 当前事务是断电恢复(不应答平台) */
|
||||||
static uint32_t cmd_tick = 0;
|
static uint32_t cmd_tick = 0;
|
||||||
static char cmd_id[20] = {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_state_ok = 0;
|
||||||
static uint8_t rx_exc = 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_online(void) { return s_online; }
|
||||||
uint8_t ext_relay_state_mask(void) { return s_state; }
|
uint8_t ext_relay_state_mask(void) { return s_state; }
|
||||||
uint8_t ext_relay_fault_mask(void) { return s_fault; }
|
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;
|
s_miss = 0;
|
||||||
if (!s_online) {
|
if (!s_online) {
|
||||||
s_online = 1;
|
s_online = 1;
|
||||||
log_info("> EXT: 扩展继电器板上线 (从机版本 V%u.%02X)", s_ver >> 8, s_ver & 0xFF);
|
log_info("> EXT: 扩展继电器板上线 (从机版本 V%u.%02X)", s_ver >> 8, s_ver & 0xFF);
|
||||||
relayStatueUpdata(); /* 上线后补一次全状态, 平台刷新 sw5~sw8 */
|
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 (cmd_stage == 1) { /* 等 FC05 回显 */
|
||||||
if (rx_echo_ok) {
|
if (rx_echo_ok) {
|
||||||
rx_echo_ok = 0;
|
rx_echo_ok = 0;
|
||||||
ext_note_success();
|
ext_note_success(0);
|
||||||
cmd_stage = 2;
|
cmd_stage = 2;
|
||||||
cmd_tick = now;
|
cmd_tick = now;
|
||||||
} else if (rx_exc || (now - cmd_tick) > EXT_RESP_TIMEOUT_MS) {
|
} else if (rx_exc || (now - cmd_tick) > EXT_RESP_TIMEOUT_MS) {
|
||||||
|
|
@ -219,7 +256,8 @@ void ext_relay_process(void)
|
||||||
cmd_tick = now;
|
cmd_tick = now;
|
||||||
} else {
|
} else {
|
||||||
log_warn("> EXT: SW%d 命令无应答", cmd_ch + 4);
|
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();
|
ext_note_fail();
|
||||||
cmd_stage = 0;
|
cmd_stage = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -240,8 +278,17 @@ void ext_relay_process(void)
|
||||||
if (cmd_stage == 3) { /* 等 FC03 状态应答(命令收尾或周期轮询共用) */
|
if (cmd_stage == 3) { /* 等 FC03 状态应答(命令收尾或周期轮询共用) */
|
||||||
if (rx_state_ok) {
|
if (rx_state_ok) {
|
||||||
rx_state_ok = 0;
|
rx_state_ok = 0;
|
||||||
ext_note_success();
|
ext_note_success(1);
|
||||||
if (cmd_is_poll) {
|
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;
|
cmd_is_poll = 0;
|
||||||
if (s_fault) {
|
if (s_fault) {
|
||||||
log_warn("> EXT: 反馈异常标志=%02X (输出=%02X 反馈=%02X)", s_fault, s_state, s_fb);
|
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);
|
cmd_ch + 4, cmd_on ? "开" : "关", s_state, s_fb, s_fault);
|
||||||
ext_respond(0, "ext relay failed / load unpowered");
|
ext_respond(0, "ext relay failed / load unpowered");
|
||||||
}
|
}
|
||||||
|
ext_save_state(); /* 断电记忆: 以实际输出状态落盘 */
|
||||||
relayStatueUpdata(); /* 命令完成后上报全状态 */
|
relayStatueUpdata(); /* 命令完成后上报全状态 */
|
||||||
}
|
}
|
||||||
cmd_stage = 0;
|
cmd_stage = 0;
|
||||||
|
|
@ -267,11 +315,12 @@ void ext_relay_process(void)
|
||||||
ext_send_fc03();
|
ext_send_fc03();
|
||||||
cmd_tick = now;
|
cmd_tick = now;
|
||||||
} else {
|
} else {
|
||||||
if (!cmd_is_poll) {
|
if (!cmd_is_poll && !cmd_is_restore) {
|
||||||
log_warn("> EXT: SW%d 状态确认无应答", cmd_ch + 4);
|
log_warn("> EXT: SW%d 状态确认无应答", cmd_ch + 4);
|
||||||
ext_respond(0, "ext relay no response");
|
ext_respond(0, "ext relay no response");
|
||||||
}
|
}
|
||||||
cmd_is_poll = 0;
|
cmd_is_poll = 0;
|
||||||
|
if (cmd_is_restore) { cmd_is_restore = 0; restore_next_tick = now + 2000; }
|
||||||
ext_note_fail();
|
ext_note_fail();
|
||||||
cmd_stage = 0;
|
cmd_stage = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -279,6 +328,23 @@ void ext_relay_process(void)
|
||||||
return;
|
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) {
|
if ((now - s_last_poll) >= EXT_POLL_INTERVAL_MS) {
|
||||||
s_last_poll = now;
|
s_last_poll = now;
|
||||||
|
|
|
||||||
9
版本说明.md
9
版本说明.md
|
|
@ -15,9 +15,14 @@
|
||||||
- **蓝牙命令新增 R5ON~R8OFF**;STATUS 显示扩展板在线状态与各路开关/故障位
|
- **蓝牙命令新增 R5ON~R8OFF**;STATUS 显示扩展板在线状态与各路开关/故障位
|
||||||
- 协议文档:`Docs/RS485扩展继电器协议.md`(从机板按此实现)
|
- 协议文档:`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)
|
## V1.7.0 —— 新硬件板型引脚重排(2026-09-04,不兼容老板,仓库迁至 shuichan_new)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue