shuichan_new/STM32F103_App1/HardWare/USER_CMD/user_cmd.c

431 lines
17 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* user_cmd.c - 蓝牙串口调试命令处理
*
* 通过 USART1 接收简单文本命令,用于现场调试:
* RESET/OTA - 复位整机
* WIFIRESET - WiFi模块 AT+RESTORE 忘掉已存热点(测试SmartConfig) + 复位
* INFO - 打印 MQTT 配置信息
* SETMQTT <ID> <用户名> <密码> - 灌入设备身份(写 EEPROM保存后自动重启生效
* SETIP <ip> <port> - 修改服务器地址/端口(写 EEPROM保存后自动重启生效
* MQTTINFO - 查询当前 MQTT 配置
* FACTORY - 恢复出厂:清空 EEPROM 全部配置,重启进入待配置模式
* SETWIFI ssid 密码 - 配置 WiFi 固定热点(写 EEPROM保存后自动重启生效
* WIFIINFO - 查询当前 WiFi 热点配置
* WIFICLEAR - 清除 SETWIFI 配置的热点,回到编译默认(自动重启生效)
* FEEDSTART 投料量KG - 带秤模式:开始投喂(如 FEEDSTART 1.5
* FEEDSTOP - 停止投喂FEED - 查询投喂状态
* STATUS - 打印继电器状态
* R1ON ~ R4OFF - 单路继电器开关
* ALLON / ALLOFF - 全部继电器开关
* HISTORY - 打印最近 10 条重启记录(开机不再自动打印)
* HELP - 显示支持命令
*/
#include "user_cmd.h"
#include <stdlib.h>
#include "ble_at.h"
#include "app_common.h"
#include "relay.h"
#include "ext_relay.h"
#include "24c02.h"
#include "uart.h"
#include "log.h"
#include "network.h"
#include "net_wifi.h"
#include "esp8266.h"
#include "reset_log.h"
#include "feed_scale.h"
#include "air780e.h"
#include "hlw8032.h"
/* 本地命令字符串不区分大小写比较 */
static int cmd_is(const char *a, const char *b)
{
while (*a && *b) {
char ca = *a, cb = *b;
if (ca >= 'A' && ca <= 'Z') ca += 'a' - 'A';
if (cb >= 'A' && cb <= 'Z') cb += 'a' - 'A';
if (ca != cb) return 0;
a++; b++;
}
return (*a == 0 && *b == 0);
}
/* 通过蓝牙串口直接控制单个继电器,并保存/上报状态 */
static void relay_set(uint8_t ch, uint8_t on)
{
if (ch < 1 || ch > 4) return;
relaySet(ch, on);
log_info("> BT: SW%d %s", ch, on ? "" : "");
eepromWriteData(102, &MqttInfoStr.Relay_State[0], 7);
relayRequestReport();
}
/* 不区分大小写的命令前缀比较cmd 以 prefix 开头返回 1 */
static int cmd_prefix_is(const char *cmd, const char *prefix)
{
while (*prefix) {
char ca = *cmd, cb = *prefix;
if (ca == 0) return 0;
if (ca >= 'A' && ca <= 'Z') ca += 'a' - 'A';
if (cb >= 'A' && cb <= 'Z') cb += 'a' - 'A';
if (ca != cb) return 0;
cmd++; prefix++;
}
return 1;
}
/* 解析命令参数:空格分隔(如 "SETMQTT id user pass")。
* 返回参数个数t 为输出(每个参数上限 cap-1 字符) */
static int parse_tokens(const char *s, char t[][20], int cap)
{
int n = 0;
for (;;) {
while (*s == ' ') s++; /* 跳过分隔空格 */
if (*s == 0) break;
if (n >= 3) return 4; /* 参数过多 */
int i = 0;
while (*s && *s != ' ' && i < cap - 1) t[n][i++] = *s++;
t[n][i] = 0;
n++;
}
return n;
}
/* 校验字符串:非空、长度 < cap、仅字母数字 */
static int token_valid(const char *t, int cap)
{
int len = (int)strlen(t);
if (len <= 0 || len >= cap) return 0;
for (const char *p = t; *p; p++) {
if (!((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z'))) return 0;
}
return 1;
}
/* WiFi 名称/密码校验:非空、长度 < cap、可打印非空格字符 */
static int wifi_token_valid(const char *t, int cap)
{
int len = (int)strlen(t);
if (len <= 0 || len >= cap) return 0;
for (const char *p = t; *p; p++) {
if (*p < 0x21 || *p > 0x7E) return 0;
}
return 1;
}
void userCmdInit(void)
{
/* 无需额外初始化 */
}
/* 处理 USART1 蓝牙调试命令 */
void process_usart1_command(void)
{
const char *cmd = USART1_GetCommand();
if (!cmd) return;
/* 去掉首尾空格:手机输入法/APP 容易带尾巴,命令判断更宽容 */
while (*cmd == ' ') cmd++;
{
char *end = (char *)cmd + strlen(cmd);
while (end > cmd && (end[-1] == ' ' || end[-1] == '\r' || end[-1] == '\n')) *--end = 0;
if (*cmd == 0) return;
}
/* 产品模式查询/切换SCALE 查询SCALE0=四路继电器(无秤)SCALE1=称重投料(带秤),切换后自动重启生效 */
if (cmd_is(cmd, "SCALE")) {
log_info("> BT: 产品模式 %s", g_feed_scale_on ? "SCALE1 称重投料(带秤)" : "SCALE0 四路继电器(无秤)");
return;
}
if (cmd_is(cmd, "SCALE0") || cmd_is(cmd, "SCALE1")) {
uint8_t on = (cmd[5] == '1') ? 1 : 0;
if (on == g_feed_scale_on) {
log_info("> BT: 已是该模式,无需切换");
return;
}
feed_scale_set_mode(on);
log_warn("> BT: 产品模式已切到 %s重启生效...", on ? "称重投料(带秤)" : "四路继电器(无秤)");
reset_log_mark(RSN_CMD_RESET);
HAL_Delay(200);
NVIC_SystemReset();
return;
}
/* GPS 定位开关GPS 查询GPS0=关闭GPS1=开启,切换后自动重启生效 */
if (cmd_is(cmd, "GPS")) {
log_info("> BT: GPS 定位 %s", g_gps_on ? "开启(GPS1)" : "关闭(GPS0)");
return;
}
if (cmd_is(cmd, "GPS0") || cmd_is(cmd, "GPS1")) {
uint8_t on = (cmd[3] == '1') ? 1 : 0;
if (on == g_gps_on) {
log_info("> BT: 已是该状态,无需切换");
return;
}
gps_set_mode(on);
log_warn("> BT: GPS 定位已切到 %s重启生效...", on ? "开启" : "关闭");
reset_log_mark(RSN_CMD_RESET);
HAL_Delay(200);
NVIC_SystemReset();
return;
}
/* 累计电能清零EEPROM 同步落盘,无需重启) */
if (cmd_is(cmd, "ENCLR")) {
hlw8032_clear();
log_warn("> BT: 累计电能已清零");
return;
}
if (cmd_is(cmd, "RESET")) {
log_warn("> USART1: 收到 RESET 命令,正在重启...");
reset_log_mark(RSN_CMD_RESET);
HAL_Delay(100);
NVIC_SystemReset();
}
else if (cmd_is(cmd, "INFO")) {
log_info("> BT INFO: 版本=%u, 服务器=%s:%d, 客户端=%s",
MqttInfoStr.Ver, MqttInfoStr.ServerIP,
MqttInfoStr.ServerPort, MqttInfoStr.ClientID);
}
else if (cmd_prefix_is(cmd, "SETMQTT")) {
char args[3][20] = {{0}};
char *id = args[0], *user = args[1], *pass = args[2];
int n = parse_tokens(cmd + 7, args, 20);
if (n != 3 || !token_valid(id, sizeof(MqttInfoStr.ClientID)) ||
!token_valid(user, sizeof(MqttInfoStr.Username)) ||
!token_valid(pass, sizeof(MqttInfoStr.Passward))) {
log_warn("> BT: 格式/字符不对,用法: SETMQTT <ID> <用户名> <密码>仅字母数字各不超过19位");
return;
}
strcpy(MqttInfoStr.ClientID, id);
strcpy(MqttInfoStr.Username, user);
strcpy(MqttInfoStr.Passward, pass);
MqttInfoStr.Magic = EEPROM_MAGIC;
eepromWriteData(0, &MqttInfoStr, MQTT_STRUCT_LEN);
log_info("> BT: 身份已保存 ID=%s 用户=%s 密码=%s自动重启生效...", id, user, pass);
HAL_Delay(1000); /* 给蓝牙留出发送时间 */
NVIC_SystemReset();
}
else if (cmd_prefix_is(cmd, "SETIP")) {
char args[3][20] = {{0}};
int port = 0, dots = 0;
int n = parse_tokens(cmd + 5, args, 20);
char *ip = args[0];
if (n == 2) port = atoi(args[1]);
for (const char *q = ip; *q; q++) {
if (*q == '.') dots++;
else if (*q < '0' || *q > '9') { dots = -1; break; }
}
if (n != 2 || dots != 3 || port < 1 || port > 65535) {
log_warn("> BT: 格式不对,用法: SETIP <ip> <port>(如 SETIP 58.17.14.95 1880");
return;
}
strcpy(MqttInfoStr.ServerIP, ip);
MqttInfoStr.ServerPort = (uint16_t)port;
MqttInfoStr.Magic = EEPROM_MAGIC;
eepromWriteData(0, &MqttInfoStr, MQTT_STRUCT_LEN);
log_info("> BT: 服务器已保存 %s:%d自动重启生效...", ip, port);
HAL_Delay(1000);
NVIC_SystemReset();
}
else if (cmd_is(cmd, "MQTTINFO")) {
log_info("> BT MQTT: 状态=%s", g_mqtt_configured ? "已配置" : "未配置");
log_info("> BT MQTT: 服务器=%s:%d", MqttInfoStr.ServerIP, MqttInfoStr.ServerPort);
log_info("> BT MQTT: ID=%s 用户=%s 密码=%s 版本=%u",
MqttInfoStr.ClientID[0] ? MqttInfoStr.ClientID : "(空)",
MqttInfoStr.Username[0] ? MqttInfoStr.Username : "(空)",
MqttInfoStr.Passward[0] ? MqttInfoStr.Passward : "(空)",
MqttInfoStr.Ver);
}
else if (cmd_is(cmd, "FACTORY")) {
/* 恢复出厂:整片 EEPROM 写 0xFF身份/服务器/继电器记忆/模式/电能累计等全清),
* 重启后走"全新板"路径Magic 无效 -> 待配置模式,版本号归 0 */
log_warn("> BT: 恢复出厂设置,正在清空 EEPROM...");
uint8_t ff[16];
memset(ff, 0xFF, sizeof(ff));
for (uint16_t addr = 0; addr < 256; addr += sizeof(ff)) {
eepromWriteData((uint8_t)addr, ff, sizeof(ff));
}
log_warn("> BT: 已清空,重启进入待配置模式");
HAL_Delay(500);
NVIC_SystemReset();
}
else if (cmd_prefix_is(cmd, "SETWIFI")) {
char args[3][20] = {{0}};
int n = parse_tokens(cmd + 7, args, 20);
if (n != 2 || !wifi_token_valid(args[0], 20) || !wifi_token_valid(args[1], 20)) {
log_warn("> BT: 格式不对,用法: SETWIFI 名称 密码空格分隔各不超过19位");
return;
}
uint8_t buf[WIFI_CFG_STR_LEN];
uint8_t magic = WIFI_CFG_MAGIC;
eepromWriteData(WIFI_CFG_MAGIC_ADDR, &magic, 1);
memset(buf, 0, sizeof(buf)); strcpy((char *)buf, args[0]);
eepromWriteData(WIFI_CFG_SSID_ADDR, buf, sizeof(buf));
memset(buf, 0, sizeof(buf)); strcpy((char *)buf, args[1]);
eepromWriteData(WIFI_CFG_PASS_ADDR, buf, sizeof(buf));
log_info("> BT: WiFi 热点已保存 '%s',自动重启生效...", args[0]);
HAL_Delay(1000);
NVIC_SystemReset();
}
else if (cmd_is(cmd, "WIFIINFO")) {
uint8_t magic = 0;
char ssid[WIFI_CFG_STR_LEN] = {0}, pass[WIFI_CFG_STR_LEN] = {0};
eepromReadData(WIFI_CFG_MAGIC_ADDR, &magic, 1);
if (magic == WIFI_CFG_MAGIC) {
eepromReadData(WIFI_CFG_SSID_ADDR, ssid, WIFI_CFG_STR_LEN);
eepromReadData(WIFI_CFG_PASS_ADDR, pass, WIFI_CFG_STR_LEN);
log_info("> BT WiFi: EEPROM 配置 名称=%s 密码=%s", ssid, pass);
} else {
log_info("> BT WiFi: 未配置(用编译默认热点 '%s'SETWIFI 名称 密码 可修改", WIFI_SSID);
}
}
else if (cmd_is(cmd, "WIFICLEAR")) {
/* 清掉 EEPROM 里的 SETWIFI 配置magic 置无效),回到编译默认热点,自动重启生效 */
uint8_t magic = 0;
eepromReadData(WIFI_CFG_MAGIC_ADDR, &magic, 1);
if (magic != WIFI_CFG_MAGIC) {
log_info("> BT: 本来就没有 SETWIFI 配置(当前用编译默认 '%s'", WIFI_SSID);
return;
}
magic = 0xFF;
eepromWriteData(WIFI_CFG_MAGIC_ADDR, &magic, 1);
log_info("> BT: SETWIFI 配置已清除,回到编译默认 '%s',自动重启...", WIFI_SSID);
HAL_Delay(1000);
NVIC_SystemReset();
}
else if (cmd_prefix_is(cmd, "FEEDSTART")) {
if (!g_feed_scale_on) { log_warn("> BT: 当前无秤模式,控制继电器 1 请用 R1ON/R1OFF"); return; }
char args[3][20] = {{0}};
int n = parse_tokens(cmd + 9, args, 20);
float kg = (n == 1) ? (float)atof(args[0]) : 0.0f;
uint16_t w100 = (uint16_t)(kg * 10.0f + 0.5f);
if (n != 1 || kg <= 0.0f || w100 < FEED_SCALE_ONCEWT_MIN || w100 > FEED_SCALE_ONCEWT_MAX) {
log_warn("> BT: 格式不对,用法: FEEDSTART 投料量KG0.1~500.0,如 FEEDSTART 1.5");
return;
}
log_info("> BT: 请求开始投喂 %.1fKG", (double)kg);
feedScaleStart(w100);
}
else if (cmd_is(cmd, "FEEDSTOP")) {
feedScaleStop();
log_info("> BT: 已请求停止投喂");
}
else if (cmd_is(cmd, "FEED")) {
static const char *sts[] = {"空闲", "投喂中", "投喂完成", "异常结束"};
FeedScaleState st = feedScaleGetState();
log_info("> BT FEED: 状态=%s 剩余=%.1fKG 单次=%.1fKG 已减=%.1fKG",
sts[(int)st],
(double)(feedScaleGetWeight() / 10.0f),
(double)(feedScaleGetOnceWeight() / 10.0f),
(double)(feedScaleGetReduced() / 10.0f));
}
else if (cmd_is(cmd, "R1ON")) { relay_set(1, 1); }
else if (cmd_is(cmd, "R1OFF")) { relay_set(1, 0); }
else if (cmd_is(cmd, "R2ON")) { relay_set(2, 1); }
else if (cmd_is(cmd, "R2OFF")) { relay_set(2, 0); }
else if (cmd_is(cmd, "R3ON")) { relay_set(3, 1); }
else if (cmd_is(cmd, "R3OFF")) { relay_set(3, 0); }
else if (cmd_is(cmd, "R4ON")) { relay_set(4, 1); }
else if (cmd_is(cmd, "R4OFF")) { relay_set(4, 0); }
else if (cmd_is(cmd, "R5ON")) { ext_relay_cmd(1, 1, NULL); } /* 485 扩展继电器板 */
else if (cmd_is(cmd, "R5OFF")) { ext_relay_cmd(1, 0, NULL); }
else if (cmd_is(cmd, "R6ON")) { ext_relay_cmd(2, 1, NULL); }
else if (cmd_is(cmd, "R6OFF")) { ext_relay_cmd(2, 0, NULL); }
else if (cmd_is(cmd, "R7ON")) { ext_relay_cmd(3, 1, NULL); }
else if (cmd_is(cmd, "R7OFF")) { ext_relay_cmd(3, 0, NULL); }
else if (cmd_is(cmd, "R8ON")) { ext_relay_cmd(4, 1, NULL); }
else if (cmd_is(cmd, "R8OFF")) { ext_relay_cmd(4, 0, NULL); }
else if (cmd_is(cmd, "ALLON")) {
relay_set(1, 1); relay_set(2, 1); relay_set(3, 1); relay_set(4, 1);
}
else if (cmd_is(cmd, "ALLOFF")) {
relay_set(1, 0); relay_set(2, 0); relay_set(3, 0); relay_set(4, 0);
}
else if (cmd_is(cmd, "STATUS")) {
log_info("> BT STATUS: SW1=%s, SW2=%s, SW3=%s, SW4=%s",
MqttInfoStr.Relay_State[1] ? "" : "",
MqttInfoStr.Relay_State[2] ? "" : "",
MqttInfoStr.Relay_State[3] ? "" : "",
MqttInfoStr.Relay_State[4] ? "" : "");
if (ext_relay_online()) {
uint8_t m = ext_relay_state_mask();
log_info("> BT STATUS: 扩展板 SW5=%s, SW6=%s, SW7=%s, SW8=%s (故障=%02X)",
(m & 1) ? "" : "",
(m & 2) ? "" : "",
(m & 4) ? "" : "",
(m & 8) ? "" : "",
ext_relay_fault_mask());
} else {
log_info("> BT STATUS: 扩展板离线/未接");
}
}
else if (cmd_is(cmd, "OTA")) {
log_info("> BT OTA: 重启进入 BootLoader 检查升级");
reset_log_mark(RSN_CMD_OTA);
HAL_Delay(100);
NVIC_SystemReset();
}
else if (cmd_is(cmd, "WIFIRESET")) {
/* WiFi 模块 AT+RESTORE 恢复出厂(忘掉已保存的热点),用于测试 SmartConfig
* 仅 WiFi 通道激活时才操作模块UART4 与 4G 共享接收缓冲,
* 4G 模式下动 UART4 会干扰 4G 数据),随后整机复位走完整重连流程 */
if (NET_GetActiveBackend() == NET_BACKEND_WIFI) {
log_warn("> BT WIFIRESET: 已安排 WiFi 恢复出厂设置"); g_wifi_restore_req = 1;
} else {
log_warn("> BT WIFIRESET: 4G 模式,不操作 WiFi 模块,仅重启");
reset_log_mark(RSN_CMD_RECOVERY);
HAL_Delay(100);
NVIC_SystemReset();
}
}
else if (cmd_is(cmd, "BLESET")) {
log_info("> BT: 强制重新设置蓝牙名称/UUID");
ble_force_setup();
}
else if (cmd_is(cmd, "LOGTRACE")) { log_set_level(LOG_TRACE); log_info("> BT: 日志等级 TRACE"); }
else if (cmd_is(cmd, "LOGDEBUG")) { log_set_level(LOG_DEBUG); log_info("> BT: 日志等级 DEBUG"); }
else if (cmd_is(cmd, "LOGINFO")) { log_set_level(LOG_INFO); log_info("> BT: 日志等级 INFO"); }
else if (cmd_is(cmd, "LOGWARN")) { log_set_level(LOG_WARN); log_info("> BT: 日志等级 WARN"); }
else if (cmd_is(cmd, "LOGERROR")) { log_set_level(LOG_ERROR); log_info("> BT: 日志等级 ERROR"); }
else if (cmd_is(cmd, "LOGFATAL")) { log_set_level(LOG_FATAL); log_info("> BT: 日志等级 FATAL"); }
else if (cmd_is(cmd, "LOG")) {
static const char *lvs[] = {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"};
log_info("> BT: 当前日志等级 %s", lvs[(int)log_get_level()]);
}
else if (cmd_is(cmd, "HISTORY")) {
reset_log_print_history();
}
else if (cmd_is(cmd, "HELP")) {
log_info("===蓝牙调试命令(空格分隔参数)===");
log_info("RESET - 系统复位");
log_info("INFO - 打印 MQTT 配置简况");
log_info("MQTTINFO - 查询 MQTT 配置详情");
log_info("SETMQTT ID 用户名 密码 - 灌入设备身份(保存后自动重启)");
log_info("SETIP ip 端口 - 修改服务器地址/端口(保存后自动重启)");
log_info("FACTORY - 恢复出厂:清空 EEPROM 全部配置并重启");
log_info("STATUS - 打印四路继电器状态");
log_info("R1ON/R1OFF~R4ON/R4OFF - 单路继电器开/关");
log_info("ALLON/ALLOFF - 全部继电器开/关");
log_info("OTA - 重启进 BootLoader 检查升级");
log_info("WIFICLEAR - 清除 SETWIFI 配置的热点,回到编译默认(自动重启)");
log_info("WIFIRESET - WiFi 模块恢复出厂(忘掉 SmartConfig 已存热点)并复位");
log_info("BLESET - 强制重设蓝牙名称/GATT UUID");
log_info("SCALE - 查询产品模式SCALE0 无秤 / SCALE1 带秤(切换后自动重启)");
log_info("GPS - 查询 GPS 开关GPS0 关 / GPS1 开(切换后自动重启)");
log_info("ENCLR - 累计电能清零");
log_info("SETWIFI 名称 密码 - 配置 WiFi 固定热点(保存后自动重启)");
log_info("WIFIINFO - 查询 WiFi 热点配置");
log_info("FEEDSTART 投料量KG - 带秤模式开始投喂FEEDSTOP 停止FEED 查询状态");
log_info("LOG - 查询日志等级LOGTRACE~LOGFATAL 六档设置(断电保存)");
log_info("HISTORY - 打印最近 10 条重启记录(原因+运行时长)");
log_info("HELP - 显示本帮助");
log_info("========= END =========");
}
else {
log_warn("> BT: 未知命令 %s", cmd);
}
}