2026-09-04 21:08:56 +08:00
|
|
|
|
/* ext_relay.c - 485 <20><>չ<EFBFBD>̵<EFBFBD><CCB5><EFBFBD><EFBFBD><EFBFBD>(4 ·<><C2B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD>)<29><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֧<EFBFBD><D6A7>
|
|
|
|
|
|
*
|
|
|
|
|
|
* Ӳ<EFBFBD><EFBFBD>: <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>سӹ<EFBFBD><EFBFBD><EFBFBD> USART2 RS485 <EFBFBD><EFBFBD><EFBFBD><EFBFBD>(9600-8N1, DMA+IDLE <EFBFBD><EFBFBD>֡)
|
|
|
|
|
|
* Э<EFBFBD><EFBFBD>: Modbus RTU, <EFBFBD>ӻ<EFBFBD><EFBFBD><EFBFBD>ַ 0x10, <EFBFBD><EFBFBD><EFBFBD><EFBFBD> Docs/RS485<EFBFBD><EFBFBD>չ<EFBFBD>̵<EFBFBD><EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD>.md
|
|
|
|
|
|
* FC05 д<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ȧ(<EFBFBD><EFBFBD>Ȧ0~3=<EFBFBD>̵<EFBFBD><EFBFBD><EFBFBD>1~4, 0xFF00<EFBFBD><EFBFBD><EFBFBD><EFBFBD>/0x0000<EFBFBD>Ͽ<EFBFBD>), <EFBFBD>ӻ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
* FC03 <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ּĴ<EFBFBD><EFBFBD><EFBFBD> 0x0000 <EFBFBD><EFBFBD> 4 <EFBFBD><EFBFBD>: <EFBFBD><EFBFBD><EFBFBD><EFBFBD>״̬/<EFBFBD><EFBFBD><EFBFBD><EFBFBD>״̬/<EFBFBD><EFBFBD><EFBFBD>ϱ<EFBFBD>־/<EFBFBD>̼<EFBFBD><EFBFBD>汾
|
|
|
|
|
|
* ʱ<EFBFBD><EFBFBD>: <EFBFBD><EFBFBD><EFBFBD>߾<EFBFBD>Ĭ >=50ms <EFBFBD>ŷ<EFBFBD>֡(<EFBFBD><EFBFBD><EFBFBD>Ӳ<EFBFBD>ѯ<EFBFBD><EFBFBD><EFBFBD><EFBFBD> rs485_bus ʱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>);
|
|
|
|
|
|
* Ӧ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ 300ms <EFBFBD><EFBFBD><EFBFBD><EFBFBD> 1 <EFBFBD><EFBFBD>; <EFBFBD><EFBFBD><EFBFBD><EFBFBD>ÿ 2s <EFBFBD><EFBFBD>ѯһ<EFBFBD><EFBFBD>״̬<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "ext_relay.h"
|
|
|
|
|
|
#include "relay.h"
|
2026-09-04 21:23:05 +08:00
|
|
|
|
#include "24c02.h"
|
2026-09-04 21:08:56 +08:00
|
|
|
|
#include "uart.h"
|
|
|
|
|
|
#include "log.h"
|
|
|
|
|
|
#include "network.h"
|
|
|
|
|
|
#include "FreeRTOS.h"
|
|
|
|
|
|
#include "task.h"
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
|
|
|
|
#define EXT_RESP_TIMEOUT_MS 300u /* Ӧ<><D3A6><EFBFBD><EFBFBD>ʱ */
|
|
|
|
|
|
#define EXT_FB_SETTLE_MS 150u /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ժ<EFBFBD><D4BA>ȴ<EFBFBD><C8B4><EFBFBD>/<2F><><EFBFBD><EFBFBD><EFBFBD>ȶ<EFBFBD><C8B6>ٶ<EFBFBD><D9B6><EFBFBD><EFBFBD><EFBFBD> */
|
|
|
|
|
|
#define EXT_POLL_INTERVAL_MS 2000u /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѯ<EFBFBD><D1AF><EFBFBD><EFBFBD> */
|
|
|
|
|
|
#define EXT_OFFLINE_MISS 3u /* <20><><EFBFBD><EFBFBD>ʧ<EFBFBD>ܴ<EFBFBD><DCB4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
|
|
|
|
|
|
|
|
|
|
|
/* Modbus CRC16(<28><><EFBFBD><EFBFBD>ͬ<EFBFBD><CDAC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽ) */
|
|
|
|
|
|
static uint16_t ext_crc16(const uint8_t *data, uint16_t len)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint16_t crc = 0xFFFF;
|
|
|
|
|
|
for (uint16_t i = 0; i < len; i++) {
|
|
|
|
|
|
crc ^= data[i];
|
|
|
|
|
|
for (uint8_t j = 0; j < 8; j++) {
|
|
|
|
|
|
crc = (crc & 0x0001) ? ((crc >> 1) ^ 0xA001) : (crc >> 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return crc;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* <20><><EFBFBD>߷<EFBFBD><DFB7><EFBFBD>: <20><> 50ms <20><>Ĭ + <20><><EFBFBD><EFBFBD><EFBFBD>ջ<EFBFBD><D5BB><EFBFBD>(<28><><EFBFBD>¾<EFBFBD>֡) + <20><><EFBFBD><EFBFBD> + ˢ<><CBA2>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD> */
|
|
|
|
|
|
static void ext_tx(const uint8_t *frame, uint16_t len)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint32_t dt = HAL_GetTick() - rs485_bus_last_tick();
|
|
|
|
|
|
if (dt < 50u) vTaskDelay(50u - dt);
|
|
|
|
|
|
USART2_FlushRxBuf();
|
|
|
|
|
|
HAL_UART_Transmit(&huart2, (uint8_t *)frame, len, 100);
|
|
|
|
|
|
rs485_bus_touch();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void ext_send_fc05(uint8_t ch, uint8_t on)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint8_t f[8];
|
|
|
|
|
|
f[0] = EXT_RELAY_ADDR;
|
|
|
|
|
|
f[1] = 0x05;
|
|
|
|
|
|
f[2] = 0x00;
|
|
|
|
|
|
f[3] = (uint8_t)(ch - 1); /* <20><>Ȧ<EFBFBD><C8A6>ַ 0~3 */
|
|
|
|
|
|
f[4] = on ? 0xFF : 0x00;
|
|
|
|
|
|
f[5] = 0x00;
|
|
|
|
|
|
uint16_t c = ext_crc16(f, 6);
|
|
|
|
|
|
f[6] = (uint8_t)(c & 0xFF);
|
|
|
|
|
|
f[7] = (uint8_t)(c >> 8);
|
|
|
|
|
|
ext_tx(f, 8);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void ext_send_fc03(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint8_t f[8];
|
|
|
|
|
|
f[0] = EXT_RELAY_ADDR;
|
|
|
|
|
|
f[1] = 0x03;
|
|
|
|
|
|
f[2] = 0x00; f[3] = 0x00; /* <20><>ʼ<EFBFBD>Ĵ<EFBFBD><C4B4><EFBFBD> 0x0000 */
|
|
|
|
|
|
f[4] = 0x00; f[5] = 0x04; /* <20><> 4 <20><> */
|
|
|
|
|
|
uint16_t c = ext_crc16(f, 6);
|
|
|
|
|
|
f[6] = (uint8_t)(c & 0xFF);
|
|
|
|
|
|
f[7] = (uint8_t)(c >> 8);
|
|
|
|
|
|
ext_tx(f, 8);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* <20><>չ<EFBFBD><D5B9>״̬(<28><><EFBFBD><EFBFBD>һ<EFBFBD><D2BB> FC03 Ӧ<><D3A6>) */
|
|
|
|
|
|
static uint8_t s_online = 0;
|
|
|
|
|
|
static uint8_t s_state = 0; /* REG0 <20><><EFBFBD><EFBFBD>״̬ bit0~3 */
|
|
|
|
|
|
static uint8_t s_fb = 0; /* REG1 <20><><EFBFBD><EFBFBD>״̬ */
|
|
|
|
|
|
static uint8_t s_fault = 0; /* REG2 <20><><EFBFBD>ϱ<EFBFBD>־ */
|
|
|
|
|
|
static uint16_t s_ver = 0; /* REG3 <20>ӻ<EFBFBD><D3BB>汾 */
|
|
|
|
|
|
static uint8_t s_miss = 0; /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӧ<EFBFBD><D3A6><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
|
|
|
|
|
static uint32_t s_last_poll = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/* <20><><EFBFBD><EFBFBD>״̬<D7B4><CCAC> */
|
|
|
|
|
|
static uint8_t cmd_pending = 0; /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
|
|
|
|
|
static uint8_t cmd_stage = 0; /* 0<><30> 1<><31>FC05<30><35><EFBFBD><EFBFBD> 2<><32><EFBFBD><EFBFBD><EFBFBD>ȶ<EFBFBD><C8B6><EFBFBD>ʱ 3<><33>FC03Ӧ<33><D3A6> */
|
|
|
|
|
|
static uint8_t cmd_ch = 0, cmd_on = 0, cmd_retry = 0;
|
|
|
|
|
|
static uint8_t cmd_is_poll = 0; /* <20><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѯ(<28><>Ӧ<EFBFBD><D3A6>ƽ̨) */
|
2026-09-04 21:23:05 +08:00
|
|
|
|
static uint8_t cmd_is_restore = 0;/* <20><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD>Ƕϵ<C7B6><CFB5>ָ<EFBFBD>(<28><>Ӧ<EFBFBD><D3A6>ƽ̨) */
|
2026-09-04 21:08:56 +08:00
|
|
|
|
static uint32_t cmd_tick = 0;
|
|
|
|
|
|
static char cmd_id[20] = {0};
|
|
|
|
|
|
|
|
|
|
|
|
/* ֡<>¼<EFBFBD>(on_frame <20><>λ, process <20><><EFBFBD><EFBFBD>; ͬ<><CDAC> app_task <20><><EFBFBD>߳<EFBFBD>) */
|
|
|
|
|
|
static uint8_t rx_echo_ok = 0;
|
|
|
|
|
|
static uint8_t rx_state_ok = 0;
|
|
|
|
|
|
static uint8_t rx_exc = 0;
|
|
|
|
|
|
|
2026-09-04 21:23:05 +08:00
|
|
|
|
#define EXT_STATE_EEPROM_IDX 5 /* MqttInfoStr.Relay_State[5] ԭ<><D4AD><EFBFBD><EFBFBD><EFBFBD>ֽ<EFBFBD>: <20><>չ<EFBFBD><D5B9>״̬λ<CCAC><CEBB><EFBFBD><EFBFBD> */
|
|
|
|
|
|
|
|
|
|
|
|
/* <20>ϵ<EFBFBD><CFB5>ָ<EFBFBD>״̬ */
|
|
|
|
|
|
static uint8_t restore_diff = 0; /* <20><><EFBFBD>ָ<EFBFBD>λ(ʵ<><CAB5>!=<3D><><EFBFBD><EFBFBD>) */
|
|
|
|
|
|
static uint8_t restore_target = 0; /* <20>ָ<EFBFBD>Ŀ<EFBFBD><C4BF><EFBFBD><EFBFBD><EFBFBD><EFBFBD>(EEPROM <20><><EFBFBD><EFBFBD>) */
|
|
|
|
|
|
static uint32_t restore_next_tick = 0; /* <20>ָ<EFBFBD>ʧ<EFBFBD>ܺ<EFBFBD><DCBA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>˱<EFBFBD> */
|
|
|
|
|
|
|
|
|
|
|
|
/* EEPROM <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>չ<EFBFBD><D5B9>״̬(0xFF=<3D>հ<EFBFBD><D5B0><EFBFBD>Ϊȫ<CEAA><C8AB>) */
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* <20><> FC03 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʵ<EFBFBD><CAB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>״̬ˢ<CCAC><CBA2> EEPROM(<28>仯<EFBFBD><E4BBAF>д) */
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-04 21:08:56 +08:00
|
|
|
|
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; }
|
|
|
|
|
|
|
2026-09-04 21:23:05 +08:00
|
|
|
|
static void ext_note_success(int fresh_state)
|
2026-09-04 21:08:56 +08:00
|
|
|
|
{
|
|
|
|
|
|
s_miss = 0;
|
|
|
|
|
|
if (!s_online) {
|
|
|
|
|
|
s_online = 1;
|
|
|
|
|
|
log_info("> EXT: <20><>չ<EFBFBD>̵<EFBFBD><CCB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (<28>ӻ<EFBFBD><D3BB>汾 V%u.%02X)", s_ver >> 8, s_ver & 0xFF);
|
|
|
|
|
|
relayStatueUpdata(); /* <20><><EFBFBD>ߺ<EFBFBD><DFBA><EFBFBD>һ<EFBFBD><D2BB>ȫ״̬, ƽ̨ˢ<CCA8><CBA2> sw5~sw8 */
|
2026-09-04 21:23:05 +08:00
|
|
|
|
/* <20>ϵ<EFBFBD><CFB5>ָ<EFBFBD>: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͷ<EFBFBD><CDB7><EFBFBD><EFBFBD><EFBFBD><EFBFBD>״̬ʱ, <20>ȶ<EFBFBD> EEPROM <20><><EFBFBD><EFBFBD>, <20><>һ<EFBFBD>µĻ<C2B5>·<EFBFBD><C2B7><EFBFBD><EFBFBD>·<EFBFBD><C2B7><EFBFBD><EFBFBD> */
|
|
|
|
|
|
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: <20><>չ<EFBFBD><D5B9>״̬<D7B4><CCAC><EFBFBD>ϵ<EFBFBD><CFB5><EFBFBD><EFBFBD>䲻<EFBFBD><E4B2BB>(ʵ<><CAB5>=%02X <20><><EFBFBD><EFBFBD>=%02X), <20><>·<EFBFBD>ָ<EFBFBD>", s_state, saved);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-09-04 21:08:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void ext_note_fail(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (s_miss < 255) s_miss++;
|
|
|
|
|
|
if (s_online && s_miss >= EXT_OFFLINE_MISS) {
|
|
|
|
|
|
s_online = 0;
|
|
|
|
|
|
log_warn("> EXT: <20><>չ<EFBFBD>̵<EFBFBD><CCB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>(<28><><EFBFBD><EFBFBD> %u <20><><EFBFBD><EFBFBD>Ӧ<EFBFBD><D3A6>)", EXT_OFFLINE_MISS);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void ext_respond(int ok, const char *msg)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (cmd_id[0]) {
|
|
|
|
|
|
NET_publish_response(cmd_id, ok, msg);
|
|
|
|
|
|
cmd_id[0] = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ext_relay_cmd(uint8_t ch, uint8_t on, const char *id)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ch < 1 || ch > EXT_RELAY_CH) return;
|
|
|
|
|
|
if (!s_online) {
|
|
|
|
|
|
log_warn("> EXT: <20><>չ<EFBFBD><D5B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20>ܾ<EFBFBD> SW%d <20><><EFBFBD><EFBFBD>", ch + 4);
|
|
|
|
|
|
if (id) NET_publish_response(id, 0, "ext relay board offline");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (cmd_pending || cmd_stage != 0) {
|
|
|
|
|
|
log_warn("> EXT: <20><>һ<EFBFBD><D2BB><EFBFBD><EFBFBD>δ<EFBFBD><CEB4><EFBFBD><EFBFBD>, <20>ܾ<EFBFBD> SW%d <20><><EFBFBD><EFBFBD>", ch + 4);
|
|
|
|
|
|
if (id) NET_publish_response(id, 0, "ext relay busy");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd_ch = ch;
|
|
|
|
|
|
cmd_on = on ? 1 : 0;
|
|
|
|
|
|
if (id) {
|
|
|
|
|
|
strncpy(cmd_id, id, sizeof(cmd_id) - 1);
|
|
|
|
|
|
cmd_id[sizeof(cmd_id) - 1] = 0;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
cmd_id[0] = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd_pending = 1;
|
|
|
|
|
|
log_info("> EXT: SW%d %s (<28>·<EFBFBD><C2B7><EFBFBD>չ<EFBFBD><D5B9>)", ch + 4, on ? "<EFBFBD><EFBFBD>" : "<EFBFBD><EFBFBD>");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 485 ֡<><D6A1><EFBFBD><EFBFBD>: feed_scale <20><>֡<EFBFBD><D6A1><EFBFBD><EFBFBD>վ<EFBFBD><D5BE> 0x10 <20>ַ<EFBFBD><D6B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
|
|
|
|
|
|
void ext_relay_on_frame(const uint8_t *b, uint16_t len)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (len < 5) return;
|
|
|
|
|
|
uint16_t c = ext_crc16(b, (uint16_t)(len - 2));
|
|
|
|
|
|
uint16_t r = (uint16_t)(((uint16_t)b[len - 1] << 8) | b[len - 2]);
|
|
|
|
|
|
if (c != r) {
|
|
|
|
|
|
log_warn("> EXT: ֡ CRC <20><><EFBFBD><EFBFBD> (len=%u)", (unsigned)len);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (b[1] & 0x80) { /* Modbus <20>쳣Ӧ<ECB3A3><D3A6> */
|
|
|
|
|
|
log_warn("> EXT: <20>쳣Ӧ<ECB3A3><D3A6> FC=%02X <20><>=%02X", b[1] & 0x7F, b[2]);
|
|
|
|
|
|
rx_exc = 1;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (b[1] == 0x05 && len == 8) {
|
|
|
|
|
|
/* <20><><EFBFBD><EFBFBD>У<EFBFBD><D0A3>: <20><>Ȧ<EFBFBD><C8A6>ַ<EFBFBD><D6B7>ͨ<EFBFBD><CDA8>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB> */
|
|
|
|
|
|
if (b[2] == 0x00 && b[3] == (uint8_t)(cmd_ch - 1) &&
|
|
|
|
|
|
((b[4] == 0xFF && cmd_on) || (b[4] == 0x00 && !cmd_on)) && b[5] == 0x00) {
|
|
|
|
|
|
rx_echo_ok = 1;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
log_warn("> EXT: FC05 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>");
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (b[1] == 0x03 && len == 13 && b[2] == 0x08) {
|
|
|
|
|
|
/* 4 <20><><EFBFBD>Ĵ<EFBFBD><C4B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: <20><><EFBFBD><EFBFBD>/<2F><><EFBFBD><EFBFBD>/<2F><><EFBFBD><EFBFBD>ֻ<EFBFBD>õ<EFBFBD><C3B5>ֽ<EFBFBD> */
|
|
|
|
|
|
s_state = b[4] & 0x0F;
|
|
|
|
|
|
s_fb = b[6] & 0x0F;
|
|
|
|
|
|
s_fault = b[8] & 0x0F;
|
|
|
|
|
|
s_ver = (uint16_t)(((uint16_t)b[9] << 8) | b[10]);
|
|
|
|
|
|
rx_state_ok = 1;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
log_warn("> EXT: δʶ<CEB4><CAB6>֡ FC=%02X len=%u", b[1], (unsigned)len);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ext_relay_process(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint32_t now = HAL_GetTick();
|
|
|
|
|
|
if (now < 3000u) return; /* <20>ϵ<EFBFBD><CFB5>ȶ<EFBFBD><C8B6><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD>״β<D7B4>ѯ<EFBFBD><D1AF><EFBFBD><EFBFBD> */
|
|
|
|
|
|
|
|
|
|
|
|
/* ȡ<><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>(<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ) */
|
|
|
|
|
|
if (cmd_pending && cmd_stage == 0) {
|
|
|
|
|
|
cmd_pending = 0;
|
|
|
|
|
|
cmd_is_poll = 0;
|
|
|
|
|
|
cmd_retry = 0;
|
|
|
|
|
|
cmd_stage = 1;
|
|
|
|
|
|
ext_send_fc05(cmd_ch, cmd_on);
|
|
|
|
|
|
cmd_tick = now;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (cmd_stage == 1) { /* <20><> FC05 <20><><EFBFBD><EFBFBD> */
|
|
|
|
|
|
if (rx_echo_ok) {
|
|
|
|
|
|
rx_echo_ok = 0;
|
2026-09-04 21:23:05 +08:00
|
|
|
|
ext_note_success(0);
|
2026-09-04 21:08:56 +08:00
|
|
|
|
cmd_stage = 2;
|
|
|
|
|
|
cmd_tick = now;
|
|
|
|
|
|
} else if (rx_exc || (now - cmd_tick) > EXT_RESP_TIMEOUT_MS) {
|
|
|
|
|
|
rx_exc = 0;
|
|
|
|
|
|
if (cmd_retry < 1) {
|
|
|
|
|
|
cmd_retry++;
|
|
|
|
|
|
ext_send_fc05(cmd_ch, cmd_on);
|
|
|
|
|
|
cmd_tick = now;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
log_warn("> EXT: SW%d <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӧ<EFBFBD><D3A6>", cmd_ch + 4);
|
2026-09-04 21:23:05 +08:00
|
|
|
|
if (!cmd_is_restore) ext_respond(0, "ext relay no response");
|
|
|
|
|
|
else { cmd_is_restore = 0; restore_next_tick = now + 2000; }
|
2026-09-04 21:08:56 +08:00
|
|
|
|
ext_note_fail();
|
|
|
|
|
|
cmd_stage = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (cmd_stage == 2) { /* <20><><EFBFBD><EFBFBD>/<2F><><EFBFBD><EFBFBD><EFBFBD>ȶ<EFBFBD><C8B6><EFBFBD>ʱ */
|
|
|
|
|
|
if ((now - cmd_tick) >= EXT_FB_SETTLE_MS) {
|
|
|
|
|
|
cmd_retry = 0;
|
|
|
|
|
|
cmd_stage = 3;
|
|
|
|
|
|
ext_send_fc03();
|
|
|
|
|
|
cmd_tick = now;
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (cmd_stage == 3) { /* <20><> FC03 ״̬Ӧ<CCAC><D3A6>(<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>β<EFBFBD><CEB2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѯ<EFBFBD><D1AF><EFBFBD><EFBFBD>) */
|
|
|
|
|
|
if (rx_state_ok) {
|
|
|
|
|
|
rx_state_ok = 0;
|
2026-09-04 21:23:05 +08:00
|
|
|
|
ext_note_success(1);
|
|
|
|
|
|
if (cmd_is_restore) {
|
|
|
|
|
|
/* <20>ϵ<EFBFBD><CFB5>ָ<EFBFBD><D6B8><EFBFBD>һ·<D2BB><C2B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: <20><>λ, ȫ<><C8AB><EFBFBD><EFBFBD><EFBFBD>ɲ<EFBFBD>ͳһ<CDB3><D2BB><EFBFBD><EFBFBD>+<2B>ϱ<EFBFBD> */
|
|
|
|
|
|
cmd_is_restore = 0;
|
|
|
|
|
|
restore_diff &= (uint8_t)~(1u << (cmd_ch - 1));
|
|
|
|
|
|
if (!restore_diff) {
|
|
|
|
|
|
log_info("> EXT: <20>ϵ<EFBFBD>״̬<D7B4>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD> (<28><><EFBFBD><EFBFBD>=%02X)", s_state);
|
|
|
|
|
|
ext_save_state();
|
|
|
|
|
|
relayStatueUpdata();
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (cmd_is_poll) {
|
2026-09-04 21:08:56 +08:00
|
|
|
|
cmd_is_poll = 0;
|
|
|
|
|
|
if (s_fault) {
|
|
|
|
|
|
log_warn("> EXT: <20><><EFBFBD><EFBFBD><EFBFBD>쳣<EFBFBD><ECB3A3>־=%02X (<28><><EFBFBD><EFBFBD>=%02X <20><><EFBFBD><EFBFBD>=%02X)", s_fault, s_state, s_fb);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
uint8_t bit = (uint8_t)(1u << (cmd_ch - 1));
|
|
|
|
|
|
int ok = (((s_state & bit) != 0) == cmd_on) && ((s_fault & bit) == 0);
|
|
|
|
|
|
if (ok) {
|
|
|
|
|
|
log_info("> EXT: SW%d %s<>ɹ<EFBFBD> (<28><><EFBFBD><EFBFBD>=%02X)", cmd_ch + 4, cmd_on ? "<EFBFBD><EFBFBD>" : "<EFBFBD><EFBFBD>", s_fb);
|
|
|
|
|
|
ext_respond(1, "OK");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
log_warn("> EXT: SW%d %sδ<73><CEB4>Ч (<28><><EFBFBD><EFBFBD>=%02X <20><><EFBFBD><EFBFBD>=%02X <20><><EFBFBD><EFBFBD>=%02X)",
|
|
|
|
|
|
cmd_ch + 4, cmd_on ? "<EFBFBD><EFBFBD>" : "<EFBFBD><EFBFBD>", s_state, s_fb, s_fault);
|
|
|
|
|
|
ext_respond(0, "ext relay failed / load unpowered");
|
|
|
|
|
|
}
|
2026-09-04 21:23:05 +08:00
|
|
|
|
ext_save_state(); /* <20>ϵ<EFBFBD><CFB5><EFBFBD><EFBFBD><EFBFBD>: <20><>ʵ<EFBFBD><CAB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>״̬<D7B4><CCAC><EFBFBD><EFBFBD> */
|
2026-09-04 21:08:56 +08:00
|
|
|
|
relayStatueUpdata(); /* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɺ<EFBFBD><C9BA>ϱ<EFBFBD>ȫ״̬ */
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd_stage = 0;
|
|
|
|
|
|
} else if (rx_exc || (now - cmd_tick) > EXT_RESP_TIMEOUT_MS) {
|
|
|
|
|
|
rx_exc = 0;
|
|
|
|
|
|
if (cmd_retry < 1) {
|
|
|
|
|
|
cmd_retry++;
|
|
|
|
|
|
ext_send_fc03();
|
|
|
|
|
|
cmd_tick = now;
|
|
|
|
|
|
} else {
|
2026-09-04 21:23:05 +08:00
|
|
|
|
if (!cmd_is_poll && !cmd_is_restore) {
|
2026-09-04 21:08:56 +08:00
|
|
|
|
log_warn("> EXT: SW%d ״̬ȷ<CCAC><C8B7><EFBFBD><EFBFBD>Ӧ<EFBFBD><D3A6>", cmd_ch + 4);
|
|
|
|
|
|
ext_respond(0, "ext relay no response");
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd_is_poll = 0;
|
2026-09-04 21:23:05 +08:00
|
|
|
|
if (cmd_is_restore) { cmd_is_restore = 0; restore_next_tick = now + 2000; }
|
2026-09-04 21:08:56 +08:00
|
|
|
|
ext_note_fail();
|
|
|
|
|
|
cmd_stage = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-04 21:23:05 +08:00
|
|
|
|
/* <20>ϵ<EFBFBD><CFB5>ָ<EFBFBD>: <20><>·<EFBFBD><C2B7><EFBFBD><EFBFBD> FC05 ʹ<><CAB9>չ<EFBFBD><D5B9><EFBFBD>ص<EFBFBD> EEPROM <20><><EFBFBD><EFBFBD>״̬ */
|
|
|
|
|
|
if (restore_diff && now >= restore_next_tick) {
|
|
|
|
|
|
uint8_t bit = (uint8_t)(restore_diff & (uint8_t)(-(int)restore_diff)); /* <20><><EFBFBD>ʹ<EFBFBD><CDB4>ָ<EFBFBD>λ */
|
|
|
|
|
|
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: <20>ָ<EFBFBD> SW%d -> %s", ch + 4, cmd_on ? "<EFBFBD><EFBFBD>" : "<EFBFBD><EFBFBD>");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-04 21:08:56 +08:00
|
|
|
|
/* <20><><EFBFBD><EFBFBD>: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѯ(<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><DFBC><EFBFBD>) */
|
|
|
|
|
|
if ((now - s_last_poll) >= EXT_POLL_INTERVAL_MS) {
|
|
|
|
|
|
s_last_poll = now;
|
|
|
|
|
|
cmd_is_poll = 1;
|
|
|
|
|
|
cmd_retry = 0;
|
|
|
|
|
|
cmd_stage = 3;
|
|
|
|
|
|
ext_send_fc03();
|
|
|
|
|
|
cmd_tick = now;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|