Compare commits

..

No commits in common. "main" and "v1.4-chinese" have entirely different histories.

11134 changed files with 5834351 additions and 8826 deletions

27
.gitignore vendored
View File

@ -1,27 +0,0 @@
# 旧版本工程与手册已移除(历史见 git tag v1.1.0~v1.4.0、v1.4-chinese
*.zip
# 调试日志
debug.txt
# Keil 编译产物keilkill.bat 可清理,不入库)
Object/
OutputBin/
Log/
*.crf
*.d
*.o
*.axf
*.hex
*.lnp
*.lst
*.map
*.sct
*.dep
*.htm
*.bak
*.scvd
*.uvguix.*
JLinkSettings.ini
JLinkLog.txt
STM32F103rb_bootloader/

Binary file not shown.

View File

@ -1,95 +0,0 @@
#include "adc.h"
#include "hlw8032.h"
#include "oled.h"
#include "main.h"
#include "network.h"
#include "led.h"
#include "reset_log.h"
#include <string.h>
#include <stdio.h>
ADC_HandleTypeDef hadc1;
void adcInit(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
if (HAL_ADC_Init(&hadc1) != HAL_OK) Error_Handler();
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) Error_Handler();
}
uint16_t getAdc(void)
{
uint16_t adc_value = 0;
HAL_ADC_Start(&hadc1);
if (HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK) {
adc_value = HAL_ADC_GetValue(&hadc1);
}
HAL_ADC_Stop(&hadc1);
return adc_value;
}
void update_batter(void)
{
static _Bool powoff = 0;
uint16_t adc_value = getAdc();
float batter_valtage = adc_value * (3.3 / 4096.0);
batter_valtage = batter_valtage * 11.0;
batter_valtage += 0.6;
batter_valtage = 100 * (batter_valtage - 10.8) / (12.6 - 10.8);
/* 限幅 0~100%:法拉电容放电时 3.3V 基准下跌ADC 读数失真可能算出负数 */
if (batter_valtage < 0) batter_valtage = 0;
if (batter_valtage > 100) batter_valtage = 100;
/* 电量 <10% 基本等同外部 12V 断开。法拉电容(5.5F)续航有限,
* 50ms 2 100ms
* 500ms */
if (batter_valtage < 10 && powoff == 0) {
uint8_t confirm = 1;
for (int k = 0; k < 2; k++) {
HAL_Delay(50);
adc_value = getAdc();
float v = adc_value * (3.3 / 4096.0);
v = v * 11.0 + 0.6;
v = 100 * (v - 10.8) / (12.6 - 10.8);
if (v >= 10) { confirm = 0; break; }
}
if (!confirm) return;
/* 1. 先发掉电消息(等模组回 OK最多 ~1.5s/次,最多试 3 次;
* 3 */
{
static uint8_t pub_try = 0;
if (NET_publish_power_wait(0) != 0 && ++pub_try < 3) return;
pub_try = 0;
}
powoff = 1;
log_info("> 电量低: %d%%", (int)batter_valtage);
/* 2. 屏幕提示掉电事件,展示 3 秒(法拉电容续航足够) */
oled_clear();
oled_locate(1, 0);
oled_print("发生掉电事件!");
hlw8032_save(); /* 本地记录:电量、断电原因(都很快) */
reset_log_power_loss();
/* 3. 3 秒后熄屏(这段延时也顺便给了模组发报文的空口时间),随后复位 */
HAL_Delay(3000);
oled_off();
NVIC_SystemReset();
/* BootLoader 会检测主电源12V 未恢复前不跳 APP防止外设没上电时程序乱跑 */
}
/* LED1: 低电量时常亮 */
if (powoff) LED1_ON; else LED1_OFF;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,60 +0,0 @@
#ifndef AIR780E_H
#define AIR780E_H
#include "main.h"
#include <stdint.h>
/*===== 4G 模组引脚定义 (Air780E 复位控制, 低电平有效) =====*/
#define CAT_RST_PIN GPIO_PIN_4
#define CAT_RST_PORT GPIOA
#define CAT_RST_LOW() HAL_GPIO_WritePin(CAT_RST_PORT, CAT_RST_PIN, GPIO_PIN_RESET)
#define CAT_RST_HIGH() HAL_GPIO_WritePin(CAT_RST_PORT, CAT_RST_PIN, GPIO_PIN_SET)
#define CAT_RST_LOW_MS 4000 /* 拉低 4s 硬件复位 */
/*===== MQTT 服务器配置 =====*/
typedef struct {
char ClientID[20];
char Username[20];
char Passward[20];
char ServerIP[20];
uint16_t ServerPort;
char Topic[20];
} MqttConfig;
/* 默认 MQTT 参数已迁移到 main.h 的 MQTT_xxx 宏,避免多处维护不一致 */
/*===== U2_CopyBuff =====*/
#define U2_COPY_SIZE 10240
extern uint8_t U2_CopyBuff[U2_COPY_SIZE];
extern volatile uint16_t U2_CopyIndex;
extern volatile uint8_t U2_CopyFlag;
/*===== 运行状态标志 (供 main.c 判断 OTA 业务健康) =====*/
extern volatile uint8_t g_air780e_mqtt_ready;
extern volatile uint8_t g_air780e_first_publish_ok;
/*===== API =====*/
uint8_t catSendCmd(const char *cmd, const char *expect, uint8_t retry, uint8_t timeout);
int air780e_send_at(const char *cmd, const char *expect, int timeout_ms);
void bg_delay(uint32_t ms);
/* GPS 定位运行时可切EEPROM(偏移173) 存 0/1空白(0xFF) 用 main.h 的
* PRODUCT_WITH_GPS GPS GPS0/GPS1 */
extern uint8_t g_gps_on;
void gps_load_mode(void);
void gps_set_mode(uint8_t on);
#define GPS_MODE_ADDR 173 /* EEPROM GPS 开关字节172 为产品模式) */ /* 后台切片延时(喂狗+业务轮询), WiFi 后端 AT 等待同样复用 */
int Verify_Firmware_External(uint32_t ext_addr, uint32_t size); /* 外部Flash固件头校验, WiFi OTA 复用 */
uint16_t crc16_modbus_update(uint16_t crc, uint8_t data); /* MODBUS CRC16 累加, WiFi OTA 复用 */
void air780e_init(void);
void air780e_process(void);
void air780e_trigger_ota(const char *host, int port, const char *path, const char *ota_id, uint16_t new_ver, uint16_t crc16);
int air780e_mqtt_report(const char *header, const char *ota_id, int percent_or_success); /* 返回 0=送达 */
void air780e_mqtt_result_nowait(const char *header, const char *ota_id, int val); /* 结果上报发了就走不等OK */
int air780e_ota_confirm(void); /* 0=通知已送达 1=无挂起 -1=通知未送达(需重试) */
uint16_t ota_get_version(void);
void CAT1_printf(const char *fmt, ...);
#endif /* AIR780E_H */

View File

@ -1,467 +0,0 @@
/* feed_scale.c - 485 喂料秤控制
*
* : USART2 RS485 PA2(TX), PA3(RX)9600-8N1
* : {0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0xC4, 0x0B}
* : 9 (1 + 1 + 1 + 4 + CRC2)[3..6] float KG
* : onceWt(0.1KG ) 1
* - >=
*/
#include "feed_scale.h"
#include "relay.h"
#include "uart.h"
#include "log.h"
#include "network.h"
#include "24c02.h"
#include <string.h>
#include <stdio.h>
/* 查询秤的 Modbus 命令 */
static const uint8_t RS485_WEIGHT_CMD[8] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0xC4, 0x0B};
/* 运行状态 */
static FeedScaleState feed_state = FEED_IDLE;
static uint16_t once_weight = 0; /* 单次投喂量0.1KG 单位,从 EEPROM 读取 */
static uint16_t start_weight = 0; /* 本次投喂开始时的剩余重量 */
static uint16_t last_weight = 0; /* 最近一次有效净重(已去皮) */
static uint16_t last_raw_weight = 0; /* 最近一次 485 原始毛重(未去皮) */
static uint16_t reduced_weight = 0; /* 本次投喂减少的重量 */
static uint16_t zero_weight = 0; /* 去皮重量EEPROM 掉电保持 */
/* 轮询计时 */
static uint32_t last_query_tick = 0;
static uint32_t feed_start_tick = 0; /* 投喂开始时刻,用于超时保护 */
static uint8_t first_query_sent = 0; /* 首次上电查询已发送 */
static uint32_t await_reply_tick = 0; /* 查询发出时刻0=无等待中的查询 */
static uint8_t first_query_reported = 0; /* 首次上电有效重量已上报 */
/* 重量异常增加计数 */
static uint8_t add_weight_count = 0;
#define FEED_ADD_WEIGHT_LIMIT 20u /* 连续约 10s 重量反而增加,结束投喂 */
/* 单次投喂超时 10 分钟 */
#define FEED_TIMEOUT_MS (10u * 60u * 1000u)
/* EEPROM 中保存去皮重量和单次投喂量的偏移(在 MqttInfoStr 之外) */
#define FEED_SCALE_ZERO_ADDR 120u
#define FEED_SCALE_ONCEWT_ADDR 122u
#define FEED_SCALE_INIT_FLAG_ADDR 124u
#define FEED_SCALE_INIT_FLAG 0xA5u
/* 本地 CRC16 校验 (Modbus) */
static uint16_t crc16_modbus(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++) {
if (crc & 0x0001) {
crc = (crc >> 1) ^ 0xA001;
} else {
crc >>= 1;
}
}
}
return crc;
}
/* 485 原始帧 hex 打印(排查用:收到什么先原样打出来) */
static void feed_scale_dump_frame(const uint8_t *buf, uint16_t len)
{
char hex[3 * 24 + 1] = {0};
uint16_t n = len > 24 ? 24 : len;
for (uint16_t i = 0; i < n; i++) {
snprintf(hex + i * 3, 4, "%02X ", buf[i]);
}
log_info("> FEED: 485 收到 %d 字节: %s", (int)len, hex);
}
/* 从 485 应答中解析重量,应答 9 字节: 01 03 04 41 97 30 33 0B F6 */
volatile uint32_t g_parse_cnt = 0; /* 解析被调次数(排障) */
static int feed_scale_parse_weight(const uint8_t *buf, uint16_t len, uint16_t *weight_100g)
{
g_parse_cnt++;
/* 485 回声(自己发出去的查询被 RX 听到):静默丢弃,不打日志不报错 */
if (len == sizeof(RS485_WEIGHT_CMD) &&
memcmp(buf, RS485_WEIGHT_CMD, sizeof(RS485_WEIGHT_CMD)) == 0) {
return -7;
}
feed_scale_dump_frame(buf, len);
/* 定位应答帧头(站号+03+字节数04485 回声或前导垃圾可能混在帧头前面 */
int hdr = -1;
for (int i = 0; i + 8 < len; i++) {
if (buf[i] == RS485_WEIGHT_CMD[0] && buf[i+1] == 0x03 && buf[i+2] == 0x04) {
hdr = i;
break;
}
}
if (hdr > 0) {
log_info("> FEED: 跳过前导 %d 字节(回声/干扰)", hdr);
buf += hdr;
len -= hdr;
} else if (hdr < 0 && len >= 9) {
log_warn("> FEED: 解析失败-找不到帧头 01 03 04");
return -6;
}
if (len < 9) {
log_warn("> FEED: 解析失败-帧长度过短(%d<9),可能波特率不对或线路干扰", (int)len);
return -1;
}
if (buf[0] != RS485_WEIGHT_CMD[0]) {
log_warn("> FEED: 解析失败-站号不符(收0x%02X 期望0x%02X)", buf[0], RS485_WEIGHT_CMD[0]);
return -3;
}
if (buf[1] == (RS485_WEIGHT_CMD[1] | 0x80)) {
log_warn("> FEED: 解析失败-秤返回异常帧(功能码0x%02X 异常码0x%02X),寄存器地址/数量不对?", buf[1], buf[2]);
return -4;
}
if (buf[1] != RS485_WEIGHT_CMD[1]) {
log_warn("> FEED: 解析失败-功能码不符(收0x%02X 期望0x%02X)", buf[1], RS485_WEIGHT_CMD[1]);
return -5;
}
uint16_t calc_crc = crc16_modbus(buf, 7); /* 前 7 字节参与 CRC 计算 */
uint16_t recv_crc = (uint16_t)buf[8] << 8 | buf[7]; /* 最后两字节为 CRC */
if (calc_crc != recv_crc) {
log_warn("> FEED: 解析失败-CRC 校验错误(calc=0x%04X recv=0x%04X),可能字节序/帧边界错位或干扰", calc_crc, recv_crc);
return -2;
}
/* 大端 float: 数据字节[3]=MSB, [6]=LSB参考 APP12 解析方式 */
float fweight;
uint8_t *p = (uint8_t *)&fweight;
p[0] = buf[6];
p[1] = buf[5];
p[2] = buf[4];
p[3] = buf[3];
/* 转成 0.1KG 整数单位,限制范围 */
int32_t wt = (int32_t)(fweight * 10.0f);
if (wt < 0) wt = 0;
if (wt > FEED_SCALE_WEIGHT_MAX) wt = FEED_SCALE_WEIGHT_MAX;
*weight_100g = (uint16_t)wt;
log_info("> FEED: 485 原始毛重=%.1fKG (0.1kg=%d)", fweight, *weight_100g);
return 0;
}
/* 通过 USART2 发送 485 查询命令 */
static void feed_scale_send_query(void)
{
USART2_FlushRxBuf(); /* 只清软件缓冲(残留回声/垃圾),硬件接收常开不动 */
HAL_UART_Transmit(&huart2, (uint8_t *)RS485_WEIGHT_CMD, sizeof(RS485_WEIGHT_CMD), 100);
}
/* 读取 485 返回帧 */
static int feed_scale_read_frame(uint8_t *buf, uint16_t max_len)
{
return (int)USART2_PollFrame(buf, max_len); /* 原子取帧,无交接状态 */
}
/* 上报当前状态到平台: 开关、重量、单次投料量、去皮重量。返回 0=已发出 */
static int feed_scale_report_weight(const char *header)
{
(void)header;
/* 云平台物模型字段weight=剩余料重(净重)zero=空载重量(去皮)onceWt=单次投料量 */
/* sw1 与 feeding 均上报继电器 1 状态,平台两个开关都能同步刷新 */
int rc = NET_publish_status(
MqttInfoStr.Relay_State[1] ? 1 : 0,
MqttInfoStr.Relay_State[1] ? 1 : 0,
MqttInfoStr.Relay_State[2] ? 1 : 0,
MqttInfoStr.Relay_State[3] ? 1 : 0,
MqttInfoStr.Relay_State[4] ? 1 : 0,
(float)zero_weight / 10.0f,
(float)once_weight / 10.0f,
(float)last_weight / 10.0f);
log_info("> FEED: 净重=%.1fKG 单次投料=%.1fKG 去皮=%.1fKG",
(float)last_weight / 10.0f,
(float)once_weight / 10.0f,
(float)zero_weight / 10.0f);
return rc;
}
static uint32_t s_last_ok_tick = 0;
static int s_last_err = 1; /* 上电默认"无应答" */
int feedScaleGetLastErr(void) { return s_last_err; }
uint32_t feedScaleGetLastOkTick(void) { return s_last_ok_tick; }
uint8_t g_feed_scale_on = 0;
/* 加载产品模式EEPROM 空白时用编译宏做默认值 */
void feed_scale_load_mode(void)
{
uint8_t v = 0xFF;
eepromReadData(FEED_SCALE_MODE_ADDR, &v, 1);
if (v > 1) v = PRODUCT_WITH_FEED_SCALE;
g_feed_scale_on = v;
log_info("> 产品模式: %s", v ? "称重投料(带秤)" : "四路继电器(无秤)");
}
/* 蓝牙命令切换产品模式:写 EEPROM重启后完全生效 */
void feed_scale_set_mode(uint8_t on)
{
on = on ? 1 : 0;
g_feed_scale_on = on;
eepromWriteData(FEED_SCALE_MODE_ADDR, &on, 1);
}
void feedScaleInit(void)
{
uint8_t tmp[2] = {0};
/* 检测是否是空白/新刷机单片机,是则强制写入默认 1.0KG */
eepromReadData(FEED_SCALE_INIT_FLAG_ADDR, tmp, 1);
if (tmp[0] != FEED_SCALE_INIT_FLAG) {
tmp[0] = FEED_SCALE_INIT_FLAG;
eepromWriteData(FEED_SCALE_INIT_FLAG_ADDR, tmp, 1);
zero_weight = 10; /* 默认去皮 1.0KG */
once_weight = 10; /* 默认单次投料 1.0KG */
tmp[0] = (uint8_t)(zero_weight & 0xFF);
tmp[1] = (uint8_t)(zero_weight >> 8);
eepromWriteData(FEED_SCALE_ZERO_ADDR, tmp, 2);
tmp[0] = (uint8_t)(once_weight & 0xFF);
tmp[1] = (uint8_t)(once_weight >> 8);
eepromWriteData(FEED_SCALE_ONCEWT_ADDR, tmp, 2);
log_info("> FEED: 首次初始化, 设置默认 去皮=1.0KG, 单次投料=1.0KG");
} else {
eepromReadData(FEED_SCALE_ZERO_ADDR, tmp, 2);
zero_weight = (uint16_t)tmp[0] | ((uint16_t)tmp[1] << 8);
if (zero_weight == 0 || zero_weight > FEED_SCALE_WEIGHT_MAX) {
zero_weight = 10; /* 默认去皮 1.0KG */
}
eepromReadData(FEED_SCALE_ONCEWT_ADDR, tmp, 2);
once_weight = (uint16_t)tmp[0] | ((uint16_t)tmp[1] << 8);
if (once_weight == 0 || once_weight > FEED_SCALE_WEIGHT_MAX) {
once_weight = 10; /* 默认单次投料 1.0KG */
}
}
if (g_feed_scale_on) {
log_info("> FEED: 初始化, 去皮=%.1fKG, 单次投料=%.1fKG",
(float)zero_weight / 10.0f, (float)once_weight / 10.0f);
}
}
void feedScaleSetZero(uint16_t zero_100g)
{
if (zero_100g > FEED_SCALE_WEIGHT_MAX) return;
zero_weight = zero_100g;
/* 平台下发新空载重量后,用最新原始毛重重新计算剩余料重 */
if (last_raw_weight >= zero_weight) {
last_weight = last_raw_weight - zero_weight;
} else {
last_weight = 0;
}
uint8_t tmp[2] = { (uint8_t)(zero_weight & 0xFF), (uint8_t)(zero_weight >> 8) };
eepromWriteData(FEED_SCALE_ZERO_ADDR, tmp, 2);
log_info("> FEED: 设置去皮=%.1fKG, 重算净重=%.1fKG",
(float)zero_weight / 10.0f, (float)last_weight / 10.0f);
}
void feedScaleSetOnceWeight(uint16_t once_wt_100g)
{
if (once_wt_100g > FEED_SCALE_WEIGHT_MAX) return;
once_weight = once_wt_100g;
uint8_t tmp[2] = { (uint8_t)(once_weight & 0xFF), (uint8_t)(once_weight >> 8) };
eepromWriteData(FEED_SCALE_ONCEWT_ADDR, tmp, 2);
log_info("> FEED: 设置单次投料=%.1fKG", (float)once_weight / 10.0f);
}
uint16_t feedScaleGetOnceWeight(void)
{
return once_weight;
}
uint16_t feedScaleGetWeight(void)
{
return last_weight;
}
uint16_t feedScaleGetZero(void)
{
return zero_weight;
}
/* 获取最近一次 485 原始毛重未去皮0.1KG 单位) */
uint16_t feedScaleGetRawWeight(void)
{
return last_raw_weight;
}
FeedScaleState feedScaleGetState(void)
{
return feed_state;
}
uint16_t feedScaleGetReduced(void)
{
return reduced_weight;
}
void feedScaleStart(uint16_t once_wt_100g)
{
if (once_wt_100g < FEED_SCALE_ONCEWT_MIN || once_wt_100g > FEED_SCALE_ONCEWT_MAX) {
log_warn("> FEED: 单次投料量无效=%.1fKG", (float)once_wt_100g / 10.0f);
return;
}
if (feed_state == FEED_RUNNING) {
log_warn("> FEED: 正在投喂中, 忽略本次请求");
return;
}
if (first_query_reported == 0) {
log_warn("> FEED: 秤未就绪, 无法开始投喂");
return;
}
if (last_weight == 0) {
log_warn("> FEED: 剩余重量为 0, 无法开始投喂");
return;
}
once_weight = once_wt_100g;
start_weight = last_weight; /* 取最近一次重量作为起始重量 */
reduced_weight = 0;
add_weight_count = 0;
feed_start_tick = HAL_GetTick();
feed_state = FEED_RUNNING;
relaySet(1, 1);
eepromWriteData(102, &MqttInfoStr.Relay_State[0], 7);
log_info("> FEED: 开始投喂 单次=%.1fKG, 起始=%.1fKG, 继电器1=开",
(float)once_weight / 10.0f, (float)start_weight / 10.0f);
/* 投喂开始时立即上报一次状态 */
feed_scale_report_weight("iot.prop.post");
}
void feedScaleStop(void)
{
if (feed_state == FEED_IDLE) return;
relaySet(1, 0);
eepromWriteData(102, &MqttInfoStr.Relay_State[0], 7);
feed_state = FEED_DONE;
log_info("> FEED: 停止投喂, 已减少=%.1fKG", (float)reduced_weight / 10.0f);
relayRequestReport();
}
static void feed_scale_finish_error(const char *reason)
{
relaySet(1, 0);
eepromWriteData(102, &MqttInfoStr.Relay_State[0], 7);
feed_state = FEED_ERROR;
log_warn("> FEED: 异常停止, %s", reason);
relayRequestReport();
}
void feedScaleProcess(void)
{
if (!g_feed_scale_on) return;
uint8_t rx_buf[32];
int len;
uint16_t cur_weight;
int32_t diff;
/* 原子取帧在 feed_scale_read_frame 内完成,无需再 Tick */
/* 空闲时 10s 查询一次; 投喂时 1s 查询一次;
*/
uint32_t query_interval = (feed_state == FEED_RUNNING) ? FEED_SCALE_QUERY_RUN_MS : FEED_SCALE_QUERY_IDLE_MS;
/* 首次查询延迟 3s避开上电窗口BLE 配置/网络初始化/首次上报都在抢资源),
* */
if (HAL_GetTick() < 3000u) return;
if (first_query_sent == 0 || (HAL_GetTick() - last_query_tick) >= query_interval) {
last_query_tick = HAL_GetTick();
feed_scale_send_query();
await_reply_tick = HAL_GetTick(); /* 标记等待应答1s 无帧打日志 */
if (first_query_sent == 0) {
first_query_sent = 1;
log_info("> FEED: 首次查询已发送");
}
}
/* 查询发出后 1s 仍无有效帧:打日志(不自动补发) */
if (await_reply_tick && (HAL_GetTick() - await_reply_tick) > 1000u) {
await_reply_tick = 0;
log_warn("> FEED: 查询无应答(秤未接/线序/波特率/站号不对)");
s_last_err = 1;
}
/* 读取 485 返回 */
len = feed_scale_read_frame(rx_buf, sizeof(rx_buf));
if (len > 0) {
int prc = feed_scale_parse_weight(rx_buf, len, &cur_weight);
if (prc == 0) { await_reply_tick = 0; }
if (prc != -7) { /* -7=回声帧:不影响状态显示 */
s_last_err = (prc == 0) ? 0 : (prc == -1 ? 2 : (prc == -2 ? 3 : (prc == -4 ? 4 : (prc == -6 ? 6 : 5))));
}
if (prc == 0) s_last_ok_tick = HAL_GetTick();
if (prc == 0) {
last_raw_weight = cur_weight; /* 保存原始毛重 */
last_weight = cur_weight > zero_weight ? (cur_weight - zero_weight) : 0; /* 净重 */
if (!first_query_reported) {
/* 首次上电: 查询到一次有效重量后上报平台。
* (AT忙)
* 0 */
if (feed_scale_report_weight("iot.prop.post") == 0) {
first_query_reported = 1;
log_info("> FEED: 首次重量已上报");
}
}
if (feed_state == FEED_RUNNING) {
/* 投喂期间:每次有效重量都上报平台,保证实时 */
feed_scale_report_weight("iot.prop.post");
/* 计算减少量,去零后比较 */
diff = (int32_t)start_weight - (int32_t)last_weight;
if (diff < 0) diff = -diff;
reduced_weight = (uint16_t)diff;
log_info("> FEED: 投喂中 起始=%.1f 当前=%.1f 已减=%.1f 单次=%.1f",
(float)start_weight / 10.0f, (float)last_weight / 10.0f,
(float)reduced_weight / 10.0f, (float)once_weight / 10.0f);
/* 条件1: 达到/超过单次投喂量,停止 */
if (reduced_weight >= once_weight) {
log_info("> FEED: 达到目标投喂量, 停止");
feedScaleStop();
return;
}
/* 条件2: 重量反而增加,可能有人在加料,连续超过阈值后结束 */
if (last_weight > start_weight) {
add_weight_count++;
if (add_weight_count >= FEED_ADD_WEIGHT_LIMIT) {
feed_scale_finish_error("重量反增");
return;
}
} else {
add_weight_count = 0;
}
/* 条件3: 投喂超时 5 分钟 */
if (HAL_GetTick() - feed_start_tick > FEED_TIMEOUT_MS) {
feed_scale_finish_error("投喂超时");
return;
}
}
}
}
/* 清理已完成状态,让外部模块在下一循环前处理 */
if (feed_state == FEED_DONE || feed_state == FEED_ERROR) {
/* 状态保持一次,由 relay 状态机上报 */
}
}

View File

@ -1,70 +0,0 @@
#ifndef __FEED_SCALE_H
#define __FEED_SCALE_H
#include "main.h"
#include <stdint.h>
/* 单次投喂量有效范围: 0 ~ 500.0 KG, 内部以 0.1 KG 为单位保存 */
#define FEED_SCALE_ONCEWT_MIN 1 /* 0.1 KG */
#define FEED_SCALE_ONCEWT_MAX 5000 /* 500.0 KG */
#define FEED_SCALE_WEIGHT_MAX 5000 /* 500.0 KG */
/* 产品形态运行时可切换EEPROM(偏移172) 存 0/1空白(0xFF) 时用
* main.h PRODUCT_WITH_FEED_SCALE
* 1=()
* 0=()feeding 1
* SCALE SCALE0/SCALE1 */
extern uint8_t g_feed_scale_on;
void feed_scale_load_mode(void); /* app_init 早期调用,加载产品模式 */
void feed_scale_set_mode(uint8_t on); /* 写 EEPROM 并更新全局(重启后完全生效) */
#define FEED_SCALE_MODE_ADDR 172 /* EEPROM 产品模式字节164~171 为电能累计脉冲) */
/* 485 读秤状态OLED 显示用):
* 0= 1= 2= 3=CRC错 4= 5=/ */
int feedScaleGetLastErr(void);
uint32_t feedScaleGetLastOkTick(void); /* 最后一次有效重量的时刻 */
/* 投喂状态 */
typedef enum {
FEED_IDLE = 0, /* 空闲 */
FEED_RUNNING, /* 投喂中: 继电器吸合正在读485 */
FEED_DONE, /* 投喂完成 */
FEED_ERROR /* 异常结束(485失联/重量反而增加等) */
} FeedScaleState;
/* 喂料秤模块 API */
void feedScaleInit(void);
void feedScaleProcess(void);
/* 触发/停止投喂 */
void feedScaleStart(uint16_t once_wt_100g);
void feedScaleStop(void);
/* 设置/获取单次投喂量(0.1KG 单位),设置后持久化到 EEPROM */
void feedScaleSetOnceWeight(uint16_t once_wt_100g);
uint16_t feedScaleGetOnceWeight(void);
/* 设置秤去皮重量(0.1KG 单位),并持久化到 EEPROM */
void feedScaleSetZero(uint16_t zero_100g);
/* 获取当前/最近一次解析出的重量(0.1KG 单位) */
uint16_t feedScaleGetWeight(void);
/* 获取最近一次 485 原始毛重未去皮0.1KG 单位) */
uint16_t feedScaleGetRawWeight(void);
/* 获取当前去皮重量(0.1KG 单位) */
uint16_t feedScaleGetZero(void);
/* 获取投喂状态 */
FeedScaleState feedScaleGetState(void);
/* 获取已减少重量(0.1KG 单位),投喂结束后有效 */
uint16_t feedScaleGetReduced(void);
/* 485 轮询间隔: 空闲 30s, 投喂中 1s */
#define FEED_SCALE_QUERY_IDLE_MS 30000u
#define FEED_SCALE_QUERY_RUN_MS 1000u
#endif /* __FEED_SCALE_H */

View File

@ -1,241 +0,0 @@
/* hlw8032.c - HLW8032 电量计量芯片驱动UART5 只收4800 8E1
*
* 24 50ms
* [0] State REG 0x55= / 0xAA= / 0xFx=
* [1] Check REG 0x5A
* [2:5]
* [5:8]
* [8:11]
* [11:14]
* [14:17] PowerPar
* [17:20]
* [20] bit7 = PF
* [21:23]PF 16bit bit7
* [23] [2]~[22] 21 8
*
*
* 1 = (1/PowerPar) × (1/(×)) × 10^9 × 3600
* (kWh) = × PowerPar × × / (10^9 × 3600)
* / main.hHLW_U_COEF / HLW_I_COEF
*
* PF 16 bit7 bit7
* +6553650ms vs
*
*/
#include "hlw8032.h"
#include "uart.h"
#include "24c02.h"
#include "log.h"
#include <string.h>
#define HLW_FRAME_LEN 24
#define HLW_OVERFLOW_PULSES 65536u
#define HLW_EEP_ADDR 164 /* EEPROM 164~171: uint64 累计脉冲数132~157 蓝牙缓存, 160 日志等级) */
static uint8_t s_frame[HLW_FRAME_LEN];
static uint8_t s_idx = 0; /* 帧同步状态0=等首字节 1=等 0x5A >=2=收集中 */
static uint64_t s_total_pulses = 0; /* 累计脉冲(含 EEPROM 恢复的历史值) */
static uint64_t s_saved_pulses = 0; /* 上次已写入 EEPROM 的值 */
static uint32_t s_power_par = 0; /* 功率参数寄存器(芯片常数,收到帧后才有) */
static uint32_t s_u_par = 0, s_u_reg = 0; /* 电压参数/电压寄存器(显示电压用) */
static uint32_t s_i_par = 0, s_i_reg = 0; /* 电流参数/电流寄存器 */
static uint32_t s_p_reg = 0; /* 功率寄存器 */
static float s_u_f = 0, s_i_f = 0, s_p_f = 0; /* 电压/电流/功率 EMA 滤波值 */
static uint32_t s_k = 0; /* 本次上电以来 bit7 翻转PF 溢出)次数 */
static uint8_t s_last_bit7 = 0;
static uint32_t s_last_chip = 0; /* 上一帧芯片侧总脉冲 k*65536+n */
static uint8_t s_synced = 0; /* 已确认同步(连续有效帧) */
static uint8_t s_streak = 0; /* 连续有效帧计数(防噪声假同步) */
static uint32_t s_last_valid_tick = 0; /* 上一有效帧时刻(帧间隔校验用) */
static uint32_t s_last_frame_tick = 0;
/*==== 帧校验与电量累计(中断上下文执行,保持轻量)====*/
static void hlw8032_parse(const uint8_t *f)
{
uint8_t sum = 0;
for (uint8_t i = 2; i <= 22; i++) sum += f[i];
if (sum != f[23]) return; /* 校验失败丢帧 */
uint32_t power_par = ((uint32_t)f[14] << 16) | ((uint32_t)f[15] << 8) | f[16];
if (power_par == 0) return; /* 参数寄存器异常,帧不可用 */
s_power_par = power_par;
/* 电压/电流/功率原始寄存器(调试用,每分钟日志显示) */
s_u_par = ((uint32_t)f[2] << 16) | ((uint32_t)f[3] << 8) | f[4];
s_u_reg = ((uint32_t)f[5] << 16) | ((uint32_t)f[6] << 8) | f[7];
s_i_par = ((uint32_t)f[8] << 16) | ((uint32_t)f[9] << 8) | f[10];
s_i_reg = ((uint32_t)f[11] << 16) | ((uint32_t)f[12] << 8) | f[13];
s_p_reg = ((uint32_t)f[17] << 16) | ((uint32_t)f[18] << 8) | f[19];
uint8_t bit7 = f[20] & 0x80;
uint16_t pf = ((uint16_t)f[21] << 8) | f[22];
if (!s_synced) {
/* 首帧只建立基准,不累计增量:
* Wh */
s_last_bit7 = bit7;
s_k = 0;
} else if (bit7 != s_last_bit7) {
s_k++; /* PF 溢出一次 */
s_last_bit7 = bit7;
}
/* 防噪声:真芯片 50ms 一帧稳定到达,要求连续 3 帧且间隔 20~150ms 才算同步。
* PD2 1/256
* 3 50ms */
{
uint32_t now = HAL_GetTick();
uint32_t gap = now - s_last_valid_tick;
s_last_valid_tick = now;
if (gap >= 20 && gap <= 150) {
if (s_streak < 3) s_streak++;
} else {
s_streak = 1;
}
if (s_streak < 3) return; /* 未确认同步:帧内容一律不采用 */
}
/* EMA 滑动平均(系数 1/8约 0.4s 响应):压空载噪声跳变,
* <0.5s使 */
{
float u = s_u_reg ? (float)((double)s_u_par / (double)s_u_reg * HLW_U_COEF) : 0.0f;
float a = s_i_reg ? (float)((double)s_i_par / (double)s_i_reg * HLW_I_COEF) : 0.0f;
float w = s_p_reg ? (float)((double)s_power_par / (double)s_p_reg * HLW_U_COEF * HLW_I_COEF) : 0.0f;
s_u_f += (u - s_u_f) * 0.125f;
s_i_f += (a - s_i_f) * 0.125f;
s_p_f += (w - s_p_f) * 0.125f;
}
uint32_t chip_total = s_k * HLW_OVERFLOW_PULSES + pf;
if (s_synced) {
/* 正常单调递增;变小说明芯片断电重启过(寄存器清零),从当前值重新累计 */
uint32_t delta = (chip_total >= s_last_chip) ? (chip_total - s_last_chip) : chip_total;
s_total_pulses += delta;
}
s_last_chip = chip_total;
if (!s_synced) {
s_synced = 1;
log_info("> 电能: HLW8032 在线, 功率参数=%lu", (unsigned long)s_power_par);
}
s_last_frame_tick = HAL_GetTick();
}
/*==== UART5 中断逐字节喂入:首字节同步 + 0x5A 二次确认 + 收满 24 字节解析 ====*/
void hlw8032_rx_byte(uint8_t b)
{
if (s_idx == 0) {
/* 首字节候选0x55 / 0xAA / 0xFx */
if (b == 0x55 || b == 0xAA || (b & 0xF0) == 0xF0) {
s_frame[0] = b;
s_idx = 1;
}
return;
}
if (s_idx == 1) {
if (b == 0x5A) {
s_frame[1] = b;
s_idx = 2;
} else {
/* 第二字节不是 0x5A伪同步但它本身可能是真首字节 */
s_idx = 0;
if (b == 0x55 || b == 0xAA || (b & 0xF0) == 0xF0) {
s_frame[0] = b;
s_idx = 1;
}
}
return;
}
s_frame[s_idx++] = b;
if (s_idx >= HLW_FRAME_LEN) {
s_idx = 0;
hlw8032_parse(s_frame);
}
}
/*==== 初始化EEPROM 恢复累计 + UART5 起收 ====*/
void hlw8032_init(void)
{
uint64_t v = 0;
eepromReadData(HLW_EEP_ADDR, &v, sizeof(v));
if (v == 0xFFFFFFFFFFFFFFFFULL) v = 0; /* 空白 EEPROM出厂 0xFF */
s_total_pulses = v;
s_saved_pulses = v;
MX_UART5_UART_Init();
UART5_StartRx();
if (s_total_pulses) {
log_info("> 电能: HLW8032 就绪 (UART5 PD2), 历史脉冲 %lu%09lu",
(unsigned long)(s_total_pulses / 1000000000ULL),
(unsigned long)(s_total_pulses % 1000000000ULL));
} else {
log_info("> 电能: HLW8032 就绪 (UART5 PD2)");
}
}
/*==== 累计电量 kWh ====*/
float hlw8032_get_energy_kwh(void)
{
if (!s_power_par) return 0.0f;
return (float)((double)s_total_pulses * (double)s_power_par *
(double)HLW_U_COEF * (double)HLW_I_COEF / 3.6e12);
}
/*==== 电压/电流/功率(手册:有效值 = 参数寄存器/寄存器 × 系数)====*/
float hlw8032_get_voltage_v(void)
{
if (!s_synced) return 0.0f;
return s_u_f;
}
float hlw8032_get_current_a(void)
{
if (!s_synced) return 0.0f;
float a = s_i_f - HLW_I_ZERO_A; /* 减零点偏移 */
return a > 0.0f ? a : 0.0f; /* 钳位到 0 */
}
float hlw8032_get_power_w(void)
{
if (!s_synced) return 0.0f;
if (s_p_f < HLW_P_DEAD_W) return 0.0f; /* 死区:空载噪声底显示 0 */
return s_p_f - HLW_P_ZERO_W;
}
/*==== 在线判断1s 内收到过有效帧 ====*/
uint8_t hlw8032_online(void)
{
return s_synced && (HAL_GetTick() - s_last_frame_tick) < 1000;
}
/*==== 立即写 EEPROM断电预警/计划复位前调用)====*/
void hlw8032_save(void)
{
if (s_total_pulses == s_saved_pulses) return;
uint64_t v = s_total_pulses;
if (eepromWriteData(HLW_EEP_ADDR, &v, sizeof(v)) == 0) {
s_saved_pulses = s_total_pulses;
}
}
/*==== 累计电量清零(蓝牙 ENCLR 命令):清 RAM 与 EEPROM ====*/
void hlw8032_clear(void)
{
s_total_pulses = 0;
s_saved_pulses = 0;
uint64_t v = 0;
eepromWriteData(HLW_EEP_ADDR, &v, sizeof(v));
}
/*==== 1s 周期:每攒够约 0.1 kWh 写一次 EEPROM限制擦写频率 ====*/
void hlw8032_tick(void)
{
if (!s_power_par) return;
double per_01kwh = 3.6e11 / ((double)s_power_par * HLW_U_COEF * HLW_I_COEF);
if (per_01kwh < 1.0) per_01kwh = 1.0;
if (s_total_pulses - s_saved_pulses >= (uint64_t)per_01kwh) {
hlw8032_save();
}
}

View File

@ -1,23 +0,0 @@
#ifndef __HLW8032_H
#define __HLW8032_H
#include "main.h"
/* HLW8032 电量计量模块
* 线 TXD -> PD2 (UART5_RX) RX
* 4800bps 8E1 50ms 24
* PF (kWh)//
* EEPROM(164~171, uint64) + */
void hlw8032_init(void); /* UART5 初始化 + EEPROM 恢复累计app_init 调用) */
void hlw8032_rx_byte(uint8_t b); /* UART5 中断逐字节喂入uart.c 接收回调调用) */
void hlw8032_tick(void); /* 1s 周期:到阈值就把累计脉冲写 EEPROM */
void hlw8032_save(void); /* 立即写 EEPROM断电预警/计划复位前调用) */
void hlw8032_clear(void); /* 累计电量清零并落盘(蓝牙 ENCLR 命令) */
float hlw8032_get_energy_kwh(void); /* 累计电量 kWh未收到帧前为 0 */
float hlw8032_get_voltage_v(void); /* 电压有效值 V调试显示用 */
float hlw8032_get_current_a(void); /* 电流有效值 A */
float hlw8032_get_power_w(void); /* 有功功率 W */
uint8_t hlw8032_online(void); /* 最近 1s 内收到过有效帧 */
#endif /* __HLW8032_H */

View File

@ -1,499 +0,0 @@
/* network.c - 网络层4G/WiFi 双通道抽象与故障切换
*
* (app_loop/relay/feed_scale) NET_*
* EEPROM "上次成功联网方式"
* 4G 4G/ 10s 3 WiFi 4G
* WiFi WiFi 4G WiFi
* WiFi AP CIPSTATUS, 10s
* SSID60s SmartConfig60s
* NET_SaveMode() EEPROM
*/
#include "network.h"
#include "net_wifi.h"
#include "hlw8032.h"
#include "feed_scale.h"
#include "net_wifi_ota.h"
#include "air780e.h"
#include "led.h"
#include "log.h"
#include "main.h"
#include "uart.h"
#include "relay.h"
#include "proto.h"
#include "24c02.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "stdio.h"
#include "string.h"
volatile uint8_t g_net_mqtt_ready = 0;
volatile uint8_t g_net_first_publish_ok = 0;
/* 网络发送互斥标志OTA 下载等 AT 交互期间置位 */
volatile uint8_t g_net_tx_busy = 0;
/* 4G 无 SIM 卡标志:由 air780e.c 在初始化失败时设置 */
volatile uint8_t g_no_sim_detected = 0;
/* 4G 无信号标志:由 air780e.c 在 AT+CSQ 检测时设置 */
volatile uint8_t g_no_signal_detected = 0;
/* 当前激活的网络后端 */
/* WiFi 模块使能脚PA6高电平工作4G 运行时拉低断电省电 */
#define ESP_EN_PORT GPIOA
#define ESP_EN_PIN GPIO_PIN_6
static void esp_power(uint8_t on)
{
HAL_GPIO_WritePin(ESP_EN_PORT, ESP_EN_PIN, on ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
volatile net_ui_phase_t g_net_ui_phase = NET_UI_NONE;
volatile int g_net_ui_countdown = 0;
#define OTA_ID_EEP_ADDR 175 /* EEPROM 175~194OTA 平台任务 id确认后清 0xFF */
static net_backend_t s_backend = NET_BACKEND_4G;
/* 上次成功联网方式EEPROM 偏移 130秤参数占用到 126避开 */
#define EEPROM_NETMODE_ADDR 130
static uint8_t s_last_mode = NET_MODE_4G;
/* 无 SIM 重试多少次后切 WiFi */
#define NET_NO_SIM_SWITCH_COUNT 3
/* WiFi 一轮完整建链(已存AP→固定SSID→SmartConfig)失败后切回 4G */
#define NET_WIFI_FAIL_SWITCH_COUNT 1
/*==== 网络模组 AT 会话互斥RTOS 版)====
* net AT //OTA
* NET_publish_*
* AT
* net net_wifi_init */
static SemaphoreHandle_t s_net_at_mutex = NULL;
void NET_LockInit(void)
{
s_net_at_mutex = xSemaphoreCreateRecursiveMutex();
}
static void net_at_lock(void)
{
if (s_net_at_mutex && xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
xSemaphoreTakeRecursive(s_net_at_mutex, portMAX_DELAY);
}
}
static void net_at_unlock(void)
{
if (s_net_at_mutex && xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
xSemaphoreGiveRecursive(s_net_at_mutex);
}
}
/* 尝试拿锁0=拿到(含调度器未启动的直通),-1=被 AT 会话占用。
* 2s net process
* */
static int net_at_trylock(void)
{
if (s_net_at_mutex && xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
return xSemaphoreTakeRecursive(s_net_at_mutex, pdMS_TO_TICKS(2000)) == pdTRUE ? 0 : -1;
}
return 0;
}
net_backend_t NET_GetActiveBackend(void)
{
return s_backend;
}
uint8_t NET_GetLastMode(void)
{
return s_last_mode;
}
/* 联网方式记忆:仅方式变化时写一次 EEPROM"只存一次" */
void NET_SaveMode(uint8_t mode)
{
if (mode == s_last_mode || mode > NET_MODE_WIFI_SMART) return;
s_last_mode = mode;
eepromWriteData(EEPROM_NETMODE_ADDR, &mode, 1);
log_info("> NET: 联网方式已保存 -> %s",
mode == NET_MODE_4G ? "4G" :
mode == NET_MODE_WIFI_FIXED ? "WiFi(固定热点)" : "WiFi(配网热点)");
}
void NET_init(void)
{
g_net_mqtt_ready = 0;
g_net_first_publish_ok = 0;
g_no_sim_detected = 0;
g_no_signal_detected = 0;
/* 读 EEPROM 记忆的上次成功联网方式:非法值(含 EEPROM 未初始化的 0xFF)按 4G */
uint8_t m = NET_MODE_4G;
eepromReadData(EEPROM_NETMODE_ADDR, &m, 1);
if (m > NET_MODE_WIFI_SMART) m = NET_MODE_4G;
s_last_mode = m;
if (m == NET_MODE_4G) {
s_backend = NET_BACKEND_4G;
esp_power(0); /* 4G 优先:关闭 WiFi 模块省电 */
net_at_lock();
air780e_init();
net_at_unlock();
if (g_no_sim_detected) {
log_warn("> 4G 无 SIM 卡,请插入 SIM 卡");
/* 不立即切 WiFi由 NET_process 重试几次仍无卡后再切换 */
}
g_net_mqtt_ready = g_air780e_mqtt_ready;
} else {
/* 上次 WiFi 成功WiFi 优先NET_process 会立即发起 net_wifi_init */
log_info("> NET: 上次联网方式为 WiFiWiFi 优先");
esp_power(1); /* WiFi 优先:使能 WiFi 模块 */
s_backend = NET_BACKEND_WIFI;
}
}
/* 切到 WiFi 通道:停 4G 接收共享缓冲互斥WiFi 建链由 NET_process 立即发起 */
static void net_switch_to_wifi(void)
{
log_warn("> NET: 切换 4G -> WiFi");
esp_power(1); /* 使能 WiFi 模块 */
USART3_StopRx();
g_net_mqtt_ready = 0;
g_net_first_publish_ok = 0;
s_backend = NET_BACKEND_WIFI;
}
/* 切回 4G 通道:停 WiFi 接收,恢复 4G 接收并重新初始化 */
static void net_switch_to_4g(void)
{
log_warn("> NET: 切换 WiFi -> 4G");
UART4_StopRx();
esp_power(0); /* 关闭 WiFi 模块省电 */
g_net_mqtt_ready = 0;
g_net_first_publish_ok = 0;
g_no_sim_detected = 0;
g_no_signal_detected = 0;
g_air780e_mqtt_ready = 0;
g_air780e_first_publish_ok = 0;
s_backend = NET_BACKEND_4G;
USART3_StartRx();
net_at_lock();
air780e_init();
net_at_unlock();
}
void NET_process(void)
{
static uint32_t retry_tick = 0;
static uint16_t fail_count = 0;
if (s_backend == NET_BACKEND_4G) {
/* 网络未就绪时自动重试 */
if (!g_air780e_mqtt_ready) {
if (g_no_sim_detected || g_no_signal_detected) {
/* 无 SIM 卡或无信号:每 10s 重试一次,连续多次仍不可用则切换到 WiFi 通道 */
if (retry_tick == 0 ||
(HAL_GetTick() - retry_tick) >= 10000) {
retry_tick = HAL_GetTick();
fail_count++;
log_warn("> NET: 4G 不可用 (no_sim=%d, no_signal=%d),重试初始化 %d/%d",
g_no_sim_detected, g_no_signal_detected,
fail_count, NET_NO_SIM_SWITCH_COUNT);
g_no_sim_detected = 0;
g_no_signal_detected = 0;
net_at_lock();
air780e_init();
net_at_unlock();
if (!g_no_sim_detected && !g_no_signal_detected) {
log_info("> NET: 4G SIM卡/信号正常或网络已恢复");
fail_count = 0;
} else if (fail_count >= NET_NO_SIM_SWITCH_COUNT) {
log_warn("> NET: 4G 重试 %d 次仍不可用,回退到 WiFi",
NET_NO_SIM_SWITCH_COUNT);
net_switch_to_wifi();
fail_count = 0;
retry_tick = 0;
}
}
} else {
retry_tick = 0;
fail_count = 0;
}
} else {
retry_tick = 0;
fail_count = 0;
NET_SaveMode(NET_MODE_4G); /* 4G 联网成功:更新方式记忆(未变化则空操作) */
}
net_at_lock();
air780e_process();
net_at_unlock();
g_net_mqtt_ready = g_air780e_mqtt_ready;
g_net_first_publish_ok = g_air780e_first_publish_ok;
} else {
/* WiFi 通道:未就绪时重新建链(首轮立即),一轮失败即切回 4G */
if (!g_wifi_mqtt_ready) {
if (retry_tick == 0 ||
(HAL_GetTick() - retry_tick) >= 10000) {
retry_tick = HAL_GetTick();
fail_count++;
log_warn("> NET: WiFi 连接尝试 %d/%d",
fail_count, NET_WIFI_FAIL_SWITCH_COUNT);
net_at_lock();
int wifi_rc = net_wifi_init();
net_at_unlock();
if (wifi_rc == 0) {
fail_count = 0;
} else if (fail_count >= NET_WIFI_FAIL_SWITCH_COUNT) {
log_warn("> NET: WiFi 连接失败,回退到 4G");
net_switch_to_4g();
fail_count = 0;
retry_tick = 0;
}
}
} else {
retry_tick = 0;
fail_count = 0;
}
net_at_lock();
net_wifi_process();
net_at_unlock();
g_net_mqtt_ready = g_wifi_mqtt_ready;
g_net_first_publish_ok = g_wifi_first_publish_ok;
}
}
int NET_publish_status(int sw1, int feeding, int sw2, int sw3, int sw4,
float weight_kg, float once_wt_kg, float zero_kg)
{
if (g_net_tx_busy) { log_info("> NET: 状态上报被丢弃 (发送忙)"); return -1; }
if (net_at_trylock() != 0) { log_info("> NET: 状态上报被丢弃 (AT 忙)"); return -1; }
/* ver 字段:固件版本号,平台据此可识别设备是否发生了 OTA 回退 */
/* sw1 与 feeding 都映射继电器 1 状态(无秤模式两者等效),
* sw1 feeding */
/* 无秤模式PRODUCT_WITH_FEED_SCALE=0不上报 zero/onceWt 字段 */
if (s_backend == NET_BACKEND_4G) {
if (g_feed_scale_on) {
CAT1_printf("AT+MPUB=\"/iot/data/up/%s\",0,0,\"{\\22header\\22:\\22iot.prop.post\\22,\\22body\\22:{\\22ver\\22:%u,\\22weight\\22:%.1f,\\22onceWt\\22:%.1f,\\22zero\\22:%.1f,\\22sw1\\22:%d,\\22feeding\\22:%d,\\22sw2\\22:%d,\\22sw3\\22:%d,\\22sw4\\22:%d}}\"",
MqttInfoStr.ClientID, (unsigned)MqttInfoStr.Ver, zero_kg, once_wt_kg, weight_kg, sw1, feeding, sw2, sw3, sw4);
} else {
CAT1_printf("AT+MPUB=\"/iot/data/up/%s\",0,0,\"{\\22header\\22:\\22iot.prop.post\\22,\\22body\\22:{\\22ver\\22:%u,\\22weight\\22:%.1f,\\22sw1\\22:%d,\\22feeding\\22:%d,\\22sw2\\22:%d,\\22sw3\\22:%d,\\22sw4\\22:%d,\\22energy\\22:%.2f}}\"",
MqttInfoStr.ClientID, (unsigned)MqttInfoStr.Ver, zero_kg, sw1, feeding, sw2, sw3, sw4, (double)hlw8032_get_energy_kwh());
}
} else {
char json[256];
if (g_feed_scale_on) {
snprintf(json, sizeof(json),
"{\"header\":\"iot.prop.post\",\"body\":{\"ver\":%u,\"weight\":%.1f,\"onceWt\":%.1f,\"zero\":%.1f,\"sw1\":%d,\"feeding\":%d,\"sw2\":%d,\"sw3\":%d,\"sw4\":%d}}",
(unsigned)MqttInfoStr.Ver, zero_kg, once_wt_kg, weight_kg, sw1, feeding, sw2, sw3, sw4);
} else {
snprintf(json, sizeof(json),
"{\"header\":\"iot.prop.post\",\"body\":{\"ver\":%u,\"weight\":%.1f,\"sw1\":%d,\"feeding\":%d,\"sw2\":%d,\"sw3\":%d,\"sw4\":%d,\"energy\":%.2f}}",
(unsigned)MqttInfoStr.Ver, zero_kg, sw1, feeding, sw2, sw3, sw4, (double)hlw8032_get_energy_kwh());
}
net_wifi_publish_up(json);
}
net_at_unlock();
return 0;
}
void NET_publish_response(const char *cmd_id, int ok, const char *msg)
{
if (g_net_tx_busy) { log_info("> NET: 应答上报被丢弃 (发送忙)"); return; }
if (net_at_trylock() != 0) { log_info("> NET: 应答上报被丢弃 (AT 忙)"); return; }
const char *text = msg ? msg : (ok ? "OK" : "command failed");
if (s_backend == NET_BACKEND_4G) {
CAT1_printf("AT+MPUB=\"/iot/data/up/%s\",0,0,\"{\\22id\\22:\\22%s\\22,\\22code\\22:0,\\22message\\22:\\22%s\\22}\"",
MqttInfoStr.ClientID, cmd_id, text);
} else {
char json[200];
snprintf(json, sizeof(json),
"{\"id\":\"%s\",\"code\":0,\"message\":\"%s\"}", cmd_id, text);
net_wifi_publish_up(json);
}
net_at_unlock();
}
/* 上报代码版本字符串: ver 字段,内容即 FIRMWARE_VERSION_STR每次联网成功报一次 */
void NET_publish_version(const char *ver)
{
if (g_net_tx_busy) { log_info("> NET: 版本上报被丢弃 (发送忙)"); return; }
if (net_at_trylock() != 0) { log_info("> NET: 版本上报被丢弃 (AT 忙)"); return; }
if (s_backend == NET_BACKEND_4G) {
CAT1_printf("AT+MPUB=\"/iot/data/up/%s\",0,0,\"{\\22header\\22:\\22iot.prop.post\\22,\\22body\\22:{\\22fwver\\22:\\22%s\\22}}\"",
MqttInfoStr.ClientID, ver);
} else {
char json[96];
snprintf(json, sizeof(json),
"{\"header\":\"iot.prop.post\",\"body\":{\"fwver\":\"%s\"}}", ver);
net_wifi_publish_up(json);
}
net_at_unlock();
}
/* 上报电量状态: on=1 正常, on=0 低电量。返回 0=已发出,-1=通道忙被丢弃 */
int NET_publish_power(int on)
{
if (g_net_tx_busy) { log_info("> NET: 电量上报被丢弃 (发送忙)"); return -1; }
if (net_at_trylock() != 0) { log_info("> NET: 电量上报被丢弃 (AT 忙)"); return -1; }
if (s_backend == NET_BACKEND_4G) {
CAT1_printf("AT+MPUB=\"/iot/data/up/%s\",0,0,\"{\\22header\\22:\\22iot.prop.post\\22,\\22body\\22:{\\22pow\\22:%d}}\"",
MqttInfoStr.ClientID, on ? 1 : 0);
} else {
char json[96];
snprintf(json, sizeof(json),
"{\"header\":\"iot.prop.post\",\"body\":{\"pow\":%d}}", on ? 1 : 0);
net_wifi_publish_up(json);
}
net_at_unlock();
return 0;
}
/* 断电预警专用:发 pow 并等模组回 OK最多 ~1.5s),确认模组已受理。
* 0=-1=/WiFi QoS0 OK 0 */
int NET_publish_power_wait(int on)
{
if (g_net_tx_busy) return -1;
if (net_at_trylock() != 0) return -1;
int rc = 0;
if (s_backend == NET_BACKEND_4G) {
static char cmd[160];
sprintf(cmd, "AT+MPUB=\"/iot/data/up/%s\",0,0,\"{\\22header\\22:\\22iot.prop.post\\22,\\22body\\22:{\\22pow\\22:%d}}\"",
MqttInfoStr.ClientID, on ? 1 : 0);
if (catSendCmd(cmd, "OK", 1, 15) != 0) rc = -1;
} else {
char json[96];
snprintf(json, sizeof(json),
"{\"header\":\"iot.prop.post\",\"body\":{\"pow\":%d}}", on ? 1 : 0);
net_wifi_publish_up(json);
}
net_at_unlock();
return rc;
}
/* 上报累计电量+实时功率: energy(kWh) 数值 + power 字符串("220V,0.21A,46.2W"),同一条报文。
* power // */
void NET_publish_energy(float kwh, float power_w)
{
if (g_net_tx_busy) { log_info("> NET: energy 上报被丢弃 (发送忙)"); return; }
if (net_at_trylock() != 0) { log_info("> NET: energy 上报被丢弃 (AT 忙)"); return; }
char pwr[40];
snprintf(pwr, sizeof(pwr), "%dV,%.2fA,%.1fW",
(int)hlw8032_get_voltage_v(), (double)hlw8032_get_current_a(), (double)power_w);
if (s_backend == NET_BACKEND_4G) {
CAT1_printf("AT+MPUB=\"/iot/data/up/%s\",0,0,\"{\\22header\\22:\\22iot.prop.post\\22,\\22body\\22:{\\22energy\\22:%.2f,\\22power\\22:\\22%s\\22}}\"",
MqttInfoStr.ClientID, kwh, pwr);
} else {
char json[128];
snprintf(json, sizeof(json),
"{\"header\":\"iot.prop.post\",\"body\":{\"energy\":%.2f,\"power\":\"%s\"}}", kwh, pwr);
net_wifi_publish_up(json);
}
log_debug("> NET: energy 已发布 %.2f %s", (double)kwh, pwr);
net_at_unlock();
}
int NET_mqtt_report(const char *header, const char *ota_id, int val)
{
if (g_net_tx_busy) { log_info("> NET: 进度/结果上报被丢弃 (发送忙)"); return -1; }
if (net_at_trylock() != 0) { log_info("> NET: 进度/结果上报被丢弃 (AT 忙)"); return -1; }
if (s_backend == NET_BACKEND_4G) {
int rc = air780e_mqtt_report(header, ota_id, val);
net_at_unlock();
return rc;
}
if (!header || !ota_id || !ota_id[0]) { net_at_unlock(); return -1; }
char json[200];
if (strstr(header, "progress")) {
snprintf(json, sizeof(json),
"{\"header\":\"%s\",\"body\":{\"id\":\"%.16s\",\"progress\":%d}}",
header, ota_id, val);
} else {
snprintf(json, sizeof(json),
"{\"header\":\"%s\",\"body\":{\"id\":\"%.16s\",\"success\":%s}}",
header, ota_id, val ? "true" : "false");
}
net_wifi_publish_up(json);
net_at_unlock();
return 0;
}
int NET_ota_confirm(void)
{
/* 两通道复用同一确认流程:无挂起 OTA 时内部直接返回;
* NET_mqtt_report */
return air780e_ota_confirm();
}
/*==== 下行消息统一分发(从 air780e.c 上提,与模组无关)====
* msg +MSUB URC4G JSONWiFi */
void net_dispatch_message(const char *msg)
{
/* dedup by message id */
static char last_id[20] = {0};
char cur_id[20] = {0};
proto_get_id((char*)msg, cur_id, sizeof(cur_id));
if (cur_id[0] && strcmp(cur_id, last_id) == 0) {
log_info("> MQTT: 重复消息跳过id=%.16s", cur_id);
return;
}
strncpy(last_id, cur_id, sizeof(last_id)-1);
/* relay control */
if (strstr(msg, "iot.prop.set")) { relay_action((uint8_t *)msg); }
/* OTA upgrade */
if (strstr(msg, "iot.ota.upgrade.post")) {
log_info("> OTA 升级命令!");
char host[30]={0}, path[80]={0}, ota_id[20]={0}; int port = 81;
uint16_t new_ver = 0;
uint16_t crc16 = 0;
/* 解析版本号与 crc16 */
{ uint32_t v = 0; if (proto_get_u32((char*)msg, "ver", &v)) new_ver = (uint16_t)v; }
proto_get_hex16((char*)msg, "crc16", &crc16);
log_info("> OTA 版本: 当前=%u, 新版本=%u, crc16=0x%04X", MqttInfoStr.Ver, new_ver, crc16);
/* 版本号 ≤ 当前版本 → 跳过下载 (防止重复或降级) */
if (new_ver > 0 && new_ver <= MqttInfoStr.Ver) {
proto_get_id((char*)msg, ota_id, sizeof(ota_id));
log_info("> OTA: 版本相同或更低,跳过 (当前=%u, 新版本=%u)", MqttInfoStr.Ver, new_ver);
NET_mqtt_report("iot.ota.progress.post", ota_id, 10);
} else {
strncpy(host, MqttInfoStr.ServerIP, sizeof(host)-1); strcpy(path, "/firmware.bin");
proto_get_id((char*)msg, ota_id, sizeof(ota_id));
{ char fu[100]={0};
if (proto_get_str((char*)msg, "url", fu, sizeof(fu))) {
log_info("> OTA 下载地址: %s", fu); char *p=fu;
if(strncmp(p,"http://",7)==0)p+=7;
char *sl=strchr(p,'/'),*co=strchr(p,':');
if(co&&(!sl||co<sl)){ int hl=co-p; memcpy(host,p,hl); host[hl]=0;
co++; port=0; while(*co>='0'&&*co<='9'){port=port*10+(*co-'0');co++;}
if(sl)strncpy(path,sl,sizeof(path)-1); }
else if(sl){ int hl=sl-p; memcpy(host,p,hl); host[hl]=0; strncpy(path,sl,sizeof(path)-1); }
else strncpy(host,p,sizeof(host)-1);
}}
log_info("> OTA: %s:%d%s", host, port, path);
/* 平台任务 id 存 EEPROM偏移 175重启确认时补发 result.post 用。
* W25Q64 OTA BL/APP BL
* BLEEPROM APP */
if (ota_id[0]) {
char id20[20] = {0};
strncpy(id20, ota_id, sizeof(id20) - 1);
eepromWriteData(OTA_ID_EEP_ADDR, id20, sizeof(id20));
}
/* 两条通道共用 W25Q64 双槽 + CRC + BootLoader 回退体系,仅下载传输不同 */
if (s_backend == NET_BACKEND_WIFI) {
net_wifi_ota(host, port, path, ota_id, new_ver, crc16);
} else {
air780e_trigger_ota(host, port, path, ota_id, new_ver, crc16);
}
}
}
}

View File

@ -1,67 +0,0 @@
#ifndef NETWORK_H
#define NETWORK_H
#include "main.h"
#include <stdint.h>
/*===== 网络后端(通道)=====*/
/* 4G 与 WiFi 互斥运行:任一时刻只有一个后端激活,
* Rx_Buf/U2_CopyBuff stm32f1xx_it.c IDLE */
typedef enum {
NET_BACKEND_4G = 0, /* Air780E, USART3, 模组内置MQTT(AT+MPUB) */
NET_BACKEND_WIFI /* ESP-01S, UART4, TCP透传+软件MQTT */
} net_backend_t;
/*===== 网络状态 =====*/
extern volatile uint8_t g_net_mqtt_ready;
extern volatile uint8_t g_net_first_publish_ok;
extern volatile uint8_t g_no_sim_detected;
extern volatile uint8_t g_no_signal_detected; /* 4G 无信号标志air780e.c 在 AT+CSQ 检测失败时设置 */
/*===== 上次成功联网方式EEPROM 持久化,仅方式变化时写一次)=====*/
#define NET_MODE_4G 0 /* 4G */
#define NET_MODE_WIFI_FIXED 1 /* WiFi - 代码里固定的热点 */
#define NET_MODE_WIFI_SMART 2 /* WiFi - SmartConfig 配网的热点 */
uint8_t NET_GetLastMode(void); /* 读当前记忆的方式 */
void NET_SaveMode(uint8_t mode); /* 方式变化时写 EEPROM未变化则空操作 */
/* 网络发送互斥:置位期间 NET_publish_* 直接丢弃OTA 下载等 AT 交互期间使用,
* AT USART3 */
extern volatile uint8_t g_net_tx_busy;
/*===== OLED 联网进度显示net 任务各阶段更新sys_task 屏幕读取)=====*/
typedef enum {
NET_UI_NONE = 0, /* 无进度/初始化中 */
NET_UI_4G_RESET, /* 4G 模块复位 */
NET_UI_4G_AT, /* AT 指令检测通过 */
NET_UI_4G_SIM, /* SIM 卡就绪 */
NET_UI_4G_ATTACH, /* 附着/激活网络成功 */
NET_UI_4G_MQTT, /* MQTT 连接中 */
NET_UI_4G_NOSIM, /* 无 SIM 卡 */
NET_UI_WIFI_FIXED, /* WiFi 固定热点搜索中countdown 有效) */
NET_UI_WIFI_SMART, /* SmartConfig 配网中countdown 有效) */
} net_ui_phase_t;
extern volatile net_ui_phase_t g_net_ui_phase;
extern volatile int g_net_ui_countdown; /* WiFi 连接/配网剩余秒数 */
/*===== API =====*/
void NET_init(void);
void NET_process(void);
void NET_LockInit(void); /* 网络 AT 会话互斥锁初始化(调度器启动前调用一次) */
net_backend_t NET_GetActiveBackend(void);
int NET_publish_status(int sw1, int feeding, int sw2, int sw3, int sw4,
float weight_kg, float once_wt_kg, float zero_kg); /* 返回 0=已发出 */
void NET_publish_response(const char *cmd_id, int ok, const char *msg);
int NET_publish_power(int on);
int NET_publish_power_wait(int on); /* 断电预警专用:等模组 OK0=已受理 */
void NET_publish_version(const char *ver); /* 上报代码版本字符串(FIRMWARE_VERSION_STR) */ /* 返回 0=已发出,-1=通道忙被丢弃 */
void NET_publish_energy(float kwh, float power_w); /* 上报累计电量,标识符 energy单位 kWh */
int NET_mqtt_report(const char *header, const char *ota_id, int val); /* 返回 0=送达 */
int NET_ota_confirm(void); /* 0=确认通知已送达 1=无挂起 -1=未送达(需重试) */
/* 下行消息统一分发与模组无关id去重 → iot.prop.set继电器控制 → iot.ota升级。
* 4G +MSUB WiFi MQTT PUBLISH payload
* msg +MSUB URC JSON */
void net_dispatch_message(const char *msg);
#endif /* NETWORK_H */

View File

@ -1,329 +0,0 @@
/* oled.c - 128x64 OLED + 晶联讯 GB2312 字库 IC 驱动GPIO 模拟 SPI
* 485CRC oledisp.c
* CLK=PC12 MOSI=PC9 DC=PC8 CS1=PC7 FSO=PC6 CS2=PB15 */
#include "oled.h"
#include <string.h>
#define OLED_CLK_PORT GPIOC
#define OLED_CLK_PIN GPIO_PIN_12
#define OLED_MOSI_PORT GPIOC
#define OLED_MOSI_PIN GPIO_PIN_9
#define OLED_DC_PORT GPIOC
#define OLED_DC_PIN GPIO_PIN_8
#define OLED_CS1_PORT GPIOC
#define OLED_CS1_PIN GPIO_PIN_7
#define OLED_FSO_PORT GPIOC
#define OLED_FSO_PIN GPIO_PIN_6
#define OLED_CS2_PORT GPIOB
#define OLED_CS2_PIN GPIO_PIN_15
#define OLED_RST_PORT GPIOB
#define OLED_RST_PIN GPIO_PIN_14 /* 复位脚,低电平复位(板内上拉,默认拉高即可) */
#define lcd_cs1(a) HAL_GPIO_WritePin(OLED_CS1_PORT, OLED_CS1_PIN, (GPIO_PinState)(a))
#define lcd_dc(a) HAL_GPIO_WritePin(OLED_DC_PORT, OLED_DC_PIN, (GPIO_PinState)(a))
#define lcd_mosi(a) HAL_GPIO_WritePin(OLED_MOSI_PORT, OLED_MOSI_PIN, (GPIO_PinState)(a))
#define lcd_clk(a) HAL_GPIO_WritePin(OLED_CLK_PORT, OLED_CLK_PIN, (GPIO_PinState)(a))
#define rom_cs2(a) HAL_GPIO_WritePin(OLED_CS2_PORT, OLED_CS2_PIN, (GPIO_PinState)(a))
#define ROM_OUT HAL_GPIO_ReadPin(OLED_FSO_PORT, OLED_FSO_PIN)
static uint8_t s_row = 0; /* 当前页地址16px 行 × 2 */
static uint8_t s_col = 0; /* 当前列(像素) */
/*==== FSO 脚输入/输出切换(读字库时为输入)====*/
static void fso_input(void)
{
GPIO_InitTypeDef g = {0};
g.Pin = OLED_FSO_PIN;
g.Mode = GPIO_MODE_INPUT;
g.Pull = GPIO_NOPULL;
HAL_GPIO_Init(OLED_FSO_PORT, &g);
}
static void fso_output(void)
{
GPIO_InitTypeDef g = {0};
g.Pin = OLED_FSO_PIN;
g.Mode = GPIO_MODE_OUTPUT_PP;
g.Pull = GPIO_PULLUP;
g.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(OLED_FSO_PORT, &g);
}
/*==== GPIO 初始化 ====*/
static void oled_gpio_init(void)
{
GPIO_InitTypeDef g = {0};
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
/* 先置好空闲电平再开推挽,避免毛刺 */
HAL_GPIO_WritePin(OLED_CLK_PORT, OLED_CLK_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(OLED_MOSI_PORT, OLED_MOSI_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(OLED_DC_PORT, OLED_DC_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(OLED_CS1_PORT, OLED_CS1_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(OLED_CS2_PORT, OLED_CS2_PIN, GPIO_PIN_SET);
g.Mode = GPIO_MODE_OUTPUT_PP;
g.Pull = GPIO_PULLUP;
g.Speed = GPIO_SPEED_FREQ_LOW;
g.Pin = OLED_CLK_PIN; HAL_GPIO_Init(OLED_CLK_PORT, &g);
g.Pin = OLED_MOSI_PIN; HAL_GPIO_Init(OLED_MOSI_PORT, &g);
g.Pin = OLED_DC_PIN; HAL_GPIO_Init(OLED_DC_PORT, &g);
g.Pin = OLED_CS1_PIN; HAL_GPIO_Init(OLED_CS1_PORT, &g);
g.Pin = OLED_CS2_PIN; HAL_GPIO_Init(OLED_CS2_PORT, &g);
/* RST(PB14):推挽输出,默认高(板内上拉,这里也主动拉高) */
HAL_GPIO_WritePin(OLED_RST_PORT, OLED_RST_PIN, GPIO_PIN_SET);
g.Pin = OLED_RST_PIN;
g.Mode = GPIO_MODE_OUTPUT_PP;
g.Pull = GPIO_NOPULL;
g.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(OLED_RST_PORT, &g);
fso_output();
}
/*==== 写字节到屏幕DC=0 指令 / DC=1 数据)====*/
static void oled_write(uint8_t dc, uint8_t dat)
{
lcd_dc(dc);
lcd_cs1(0);
for (int i = 0; i < 8; i++) {
lcd_clk(0);
lcd_mosi(dat & 0x80);
lcd_clk(1);
dat <<= 1;
}
lcd_dc(1);
lcd_cs1(1);
}
/*==== 设置页/列地址 ====*/
static void lcd_address(uint8_t page, uint8_t column)
{
oled_write(0, 0xb0 + column); /* 页地址 */
oled_write(0, ((page & 0xf0) >> 4) | 0x10); /* 列地址高 4 位 */
oled_write(0, (page & 0x0f)); /* 列地址低 4 位 */
}
/*==== 关显示(黑屏,断电预警时调用)====*/
void oled_off(void)
{
oled_write(0, 0xAE); /* display off */
}
/*==== 全屏清屏 ====*/
void oled_clear(void)
{
lcd_cs1(0);
rom_cs2(1);
for (uint8_t i = 0; i < 8; i++) {
oled_write(0, 0xb0 + i);
oled_write(0, 0x00);
oled_write(0, 0x10);
for (uint8_t j = 0; j < 128; j++) oled_write(1, 0x00);
}
lcd_cs1(1);
}
/*==== 屏幕初始化SSD1306 兼容序列)====*/
void oled_init(void)
{
HAL_Delay(400); /* 等屏幕/字库上电稳定 */
oled_gpio_init();
/* 硬件复位一次屏幕RST 低 10ms保证从确定状态开始初始化 */
HAL_GPIO_WritePin(OLED_RST_PORT, OLED_RST_PIN, GPIO_PIN_RESET);
HAL_Delay(10);
HAL_GPIO_WritePin(OLED_RST_PORT, OLED_RST_PIN, GPIO_PIN_SET);
HAL_Delay(120);
lcd_cs1(0);
rom_cs2(1);
oled_write(0, 0xAE); /* display off */
oled_write(0, 0x20); /* 寻址模式 */
oled_write(0, 0x10); /* 水平寻址 */
oled_write(0, 0xb0);
oled_write(0, 0xc8); /* COM 扫描方向 */
oled_write(0, 0x00);
oled_write(0, 0x10);
oled_write(0, 0x40); /* 起始行 */
oled_write(0, 0x81); /* 对比度 */
oled_write(0, 0xFF);
oled_write(0, 0xa1); /* 段重映射 */
oled_write(0, 0xa6); /* 正常显示 */
oled_write(0, 0xa8); /* 复用率 */
oled_write(0, 0x3F);
oled_write(0, 0xa4);
oled_write(0, 0xd3); /* 显示偏移 */
oled_write(0, 0x00);
oled_write(0, 0xd5); /* 时钟分频 */
oled_write(0, 0xf0);
oled_write(0, 0xd9); /* 预充电 */
oled_write(0, 0x22);
oled_write(0, 0xda); /* COM 硬件配置 */
oled_write(0, 0x12);
oled_write(0, 0xdb); /* VCOMH */
oled_write(0, 0x20);
oled_write(0, 0x8d); /* DC-DC */
oled_write(0, 0x14);
oled_write(0, 0xaf); /* display on */
lcd_cs1(1);
oled_clear();
}
/*==== 显示 16x16 点阵(汉字)====*/
static void display_graphic_16x16(uint8_t page, uint8_t column, const uint8_t *dp)
{
lcd_cs1(0);
rom_cs2(1);
for (uint8_t j = 2; j > 0; j--) {
lcd_address(column, page);
for (uint8_t i = 0; i < 16; i++) oled_write(1, *dp++);
page++;
}
lcd_cs1(1);
}
/*==== 显示 8x16 点阵ASCII====*/
static void display_graphic_8x16(uint8_t page, uint8_t column, const uint8_t *dp)
{
lcd_cs1(0);
for (uint8_t j = 2; j > 0; j--) {
lcd_address(column, page);
for (uint8_t i = 0; i < 8; i++) oled_write(1, *dp++);
page++;
}
lcd_cs1(1);
}
/*==== 字库 IC 写字节(指令/地址)====*/
static void rom_write_byte(uint8_t dat)
{
for (int i = 0; i < 8; i++) {
lcd_mosi(dat & 0x80);
dat <<= 1;
lcd_clk(0);
lcd_clk(1);
}
}
/*==== 字库 IC 读一字节 ====*/
static uint8_t rom_read_byte(void)
{
uint8_t ret = 0;
lcd_clk(1);
fso_input();
for (int i = 0; i < 8; i++) {
lcd_clk(0);
ret <<= 1;
if (ROM_OUT) ret |= 1;
lcd_clk(1);
}
fso_output();
return ret;
}
/*==== 从字库 IC 连续读 DataLen 字节 ====*/
static void rom_read_n(uint8_t ah, uint8_t am, uint8_t al, uint8_t *buf, uint8_t len)
{
rom_cs2(0);
lcd_cs1(1);
lcd_clk(0);
rom_write_byte(0x03); /* 读指令 */
rom_write_byte(ah);
rom_write_byte(am);
rom_write_byte(al);
for (uint8_t i = 0; i < len; i++) buf[i] = rom_read_byte();
rom_cs2(1);
}
/*==== 定位row 0~316px 行col8 为 8 像素单位 ====*/
void oled_locate(uint8_t row, uint8_t col8)
{
s_row = row * 2;
s_col = col8 * 8;
}
/*==== 显示 GBK 字符串16x16 汉字 / 8x16 ASCII编码换算见晶联讯字库手册====*/
void oled_print(const char *s)
{
uint8_t fontbuf[32];
uint8_t y = s_row;
uint8_t x = s_col;
const uint8_t *text = (const uint8_t *)s;
uint32_t fontaddr;
s_col += strlen(s) * 8; /* 位置自加(按 8px/字符计) */
uint8_t i = 0;
while (text[i] > 0x00) {
if ((text[i] >= 0xb0 && text[i] <= 0xf7) && text[i+1] >= 0xa1) {
/* GB2312 汉字Address = ((MSB-0xB0)*94 + (LSB-0xA1) + 846) * 32 */
fontaddr = (text[i] - 0xb0) * 94;
fontaddr += (text[i+1] - 0xa1) + 846;
fontaddr *= 32;
rom_read_n((uint8_t)(fontaddr >> 16), (uint8_t)(fontaddr >> 8),
(uint8_t)fontaddr, fontbuf, 32);
display_graphic_16x16(y, x, fontbuf);
i += 2;
x += 16;
}
else if ((text[i] >= 0xa1 && text[i] <= 0xa3) && text[i+1] >= 0xa1) {
/* GB2312 全角符号Address = ((MSB-0xA1)*94 + (LSB-0xA1)) * 32 */
fontaddr = (text[i] - 0xa1) * 94;
fontaddr += (text[i+1] - 0xa1);
fontaddr *= 32;
rom_read_n((uint8_t)(fontaddr >> 16), (uint8_t)(fontaddr >> 8),
(uint8_t)fontaddr, fontbuf, 32);
display_graphic_16x16(y, x, fontbuf);
i += 2;
x += 16;
}
else if (text[i] >= 0x20 && text[i] <= 0x7e) {
/* ASCII 8x16Address = (char-0x20)*16 + 0x3CF80 */
fontaddr = (text[i] - 0x20) * 16 + 0x3cf80;
rom_read_n((uint8_t)(fontaddr >> 16), (uint8_t)(fontaddr >> 8),
(uint8_t)fontaddr, fontbuf, 16);
display_graphic_8x16(y, x, fontbuf);
i += 1;
x += 8;
}
else {
i++;
}
}
}
/*==== 当前列右移 px 像素 ====*/
void oled_xoff(uint8_t px)
{
s_col += px;
}
/*==== 文本像素宽度:汉字/全角 16pxASCII 8px ====*/
uint8_t oled_text_w(const char *s)
{
const uint8_t *p = (const uint8_t *)s;
uint32_t w = 0;
while (*p) {
if (p[0] >= 0xa1 && p[1] >= 0xa1) { w += 16; p += 2; }
else { w += 8; p += 1; }
}
return (uint8_t)(w > 128 ? 128 : w);
}
/*==== 整行显示:计算像素宽度,不足 128px 用空格补齐(覆盖上次内容)====*/
void oled_print_line(const char *s)
{
const uint8_t *p = (const uint8_t *)s;
uint32_t w = 0;
while (*p) {
if (p[0] >= 0xa1 && p[1] >= 0xa1) { w += 16; p += 2; } /* 汉字/全角 */
else { w += 8; p += 1; } /* ASCII */
}
oled_print(s);
while (w < 128) { oled_print(" "); w += 8; }
}

View File

@ -1,21 +0,0 @@
#ifndef __OLED_H
#define __OLED_H
#include "main.h"
/* 128x64 OLEDSSD1306 兼容指令)+ 晶联讯 GB2312 字库 IC全部 GPIO 模拟 SPI。
* 线CLK=PC12 MOSI=PC9 DC=PC8 CS1=PC7() FSO=PC6(,MCU输入)
* CS2=PB15()
* GB2312/GBK GBK
* 128x6416 4 row 0~3 8 16 ASCII */
void oled_init(void); /* GPIO + 屏幕初始化app_init 调用) */
void oled_clear(void); /* 全屏清屏 */
void oled_off(void); /* 关显示0xAE黑屏断电前调用 */
void oled_locate(uint8_t row, uint8_t col8); /* 定位row 0~3col8 为 8 像素单位 0~15 */
void oled_xoff(uint8_t px);
uint8_t oled_text_w(const char *gbk_str); /* 文本像素宽度汉字16/ASCII8 */ /* 在当前列基础上右移 px 像素(防烧屏偏移用) */
void oled_print(const char *gbk_str); /* 从当前位置显示 GBK 字符串 */
void oled_print_line(const char *gbk_str); /* 整行显示:不足 128px 用空格补齐覆盖旧内容 */
#endif /* __OLED_H */

View File

@ -1,333 +0,0 @@
#include "relay.h"
#include "main.h"
#include "network.h"
#include "24c02.h"
#include "feed_scale.h"
#include "proto.h"
#include <string.h>
/* 命令执行后状态上报标志 */
volatile uint8_t relay_report_pending = 0;
volatile uint32_t relay_report_tick = 0;
/* 继电器命令响应阶段控制:
0:
1: ,
2: , response status,
*/
volatile uint8_t relay_cmd_stage = 0;
volatile uint32_t relay_cmd_tick = 0;
volatile uint8_t relay_cmd_ch = 0;
volatile GPIO_PinState relay_cmd_fb_before = GPIO_PIN_RESET;
char relay_cmd_id[20] = {0};
/* 运行继电器命令响应状态机,由主循环每轮调用 */
void relayCmdProcess(void)
{
GPIO_PinState fb_after;
uint8_t ch = relay_cmd_ch;
if (relay_cmd_stage == 0) return;
if (relay_cmd_stage == 1) {
/* 等待继电器反馈电路稳定 */
if (HAL_GetTick() - relay_cmd_tick < 100) return;
fb_after = relayReadFb(ch);
if (relayIsManualMode()) {
log_info("> 继电器: SW%d 动作正常(手动模式)", ch);
NET_publish_response(relay_cmd_id, 1, "OK");
} else if (RELAY_FB_ENABLE) {
if (MqttInfoStr.Relay_State[ch]) {
if (relay_cmd_fb_before == FB_INACTIVE && fb_after == FB_ACTIVE) {
log_info("> 继电器: SW%d 动作正常", ch);
NET_publish_response(relay_cmd_id, 1, "OK");
} else {
log_warn("> 继电器: SW%d 未接负载或继电器断开", ch);
NET_publish_response(relay_cmd_id, 0, "Load unpowered / relay failure !");
}
} else {
if (relay_cmd_fb_before == FB_ACTIVE && fb_after == FB_INACTIVE) {
log_info("> 继电器: SW%d 动作正常", ch);
NET_publish_response(relay_cmd_id, 1, "OK");
} else {
log_warn("> 继电器: SW%d 未接负载或继电器断开", ch);
NET_publish_response(relay_cmd_id, 0, "Load unpowered / relay failure !");
}
}
} else {
log_info("> 继电器: SW%d 动作正常", ch);
NET_publish_response(relay_cmd_id, 1, "OK");
}
/* 参考 APP1: 命令响应后立即上报一次全状态, 让平台刷新开关状态 */
relayStatueUpdata();
relay_cmd_stage = 0;
return;
}
}
void relayInit(void)
{
GPIO_InitTypeDef g = {0};
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
/* 4 路继电器控制引脚初始化为推挽输出, 默认低电平释放 */
g.Mode = GPIO_MODE_OUTPUT_PP;
g.Pull = GPIO_NOPULL;
g.Speed = GPIO_SPEED_FREQ_LOW;
g.Pin = Relay1_Pin; HAL_GPIO_Init(Relay1_Port, &g);
g.Pin = Relay2_Pin; HAL_GPIO_Init(Relay2_Port, &g);
g.Pin = Relay3_Pin; HAL_GPIO_Init(Relay3_Port, &g);
g.Pin = Relay4_Pin; HAL_GPIO_Init(Relay4_Port, &g);
/* 4 路反馈输入引脚, 上拉 */
g.Mode = GPIO_MODE_INPUT;
g.Pull = GPIO_PULLUP;
g.Pin = Relay1_FB_Pin; HAL_GPIO_Init(Relay1_FB_Port, &g);
g.Pin = Relay2_FB_Pin; HAL_GPIO_Init(Relay2_FB_Port, &g);
g.Pin = Relay3_FB_Pin; HAL_GPIO_Init(Relay3_FB_Port, &g);
g.Pin = Relay4_FB_Pin; HAL_GPIO_Init(Relay4_FB_Port, &g);
/* PA8 手动模式检测, 下拉, 默认低电平=正常模式 */
__HAL_RCC_GPIOA_CLK_ENABLE();
g.Pin = MANUAL_MODE_Pin;
g.Mode = GPIO_MODE_INPUT;
g.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(MANUAL_MODE_Port, &g);
/* 控制 GPIO 默认全部 OFF */
Relay1_OFF; log_info("SW1:关");
Relay2_OFF; log_info("SW2:关");
Relay3_OFF; log_info("SW3:关");
Relay4_OFF; log_info("SW4:关");
}
/* 网络连接成功后, 从 EEPROM 恢复继电器上一次状态(断电记忆)。
* ():
* : 1 */
void relayRestoreState(void)
{
if (!g_feed_scale_on) {
if (MqttInfoStr.Relay_State[1]) { Relay1_ON; log_info("SW1:开(恢复)"); }
else { Relay1_OFF; log_info("SW1:关(恢复)"); }
} else {
MqttInfoStr.Relay_State[1] = 0;
Relay1_OFF; log_info("SW1:关(不恢复,投喂安全保护)");
}
if (MqttInfoStr.Relay_State[2]) { Relay2_ON; log_info("SW2:开(恢复)"); }
else { Relay2_OFF; log_info("SW2:关(恢复)"); }
if (MqttInfoStr.Relay_State[3]) { Relay3_ON; log_info("SW3:开(恢复)"); }
else { Relay3_OFF; log_info("SW3:关(恢复)"); }
if (MqttInfoStr.Relay_State[4]) { Relay4_ON; log_info("SW4:开(恢复)"); }
else { Relay4_OFF; log_info("SW4:关(恢复)"); }
}
/* 读取指定继电器反馈引脚原始电平 */
GPIO_PinState relayReadFb(uint8_t ch)
{
switch (ch) {
case 1: return HAL_GPIO_ReadPin(Relay1_FB_Port, Relay1_FB_Pin);
case 2: return HAL_GPIO_ReadPin(Relay2_FB_Port, Relay2_FB_Pin);
case 3: return HAL_GPIO_ReadPin(Relay3_FB_Port, Relay3_FB_Pin);
case 4: return HAL_GPIO_ReadPin(Relay4_FB_Port, Relay4_FB_Pin);
default: return GPIO_PIN_RESET;
}
}
/* 读取 PA8 手动模式输入: 1=手动模式, 0=正常模式 */
int relayIsManualMode(void)
{
return (HAL_GPIO_ReadPin(MANUAL_MODE_Port, MANUAL_MODE_Pin) == MANUAL_MODE_ACTIVE) ? 1 : 0;
}
/* 设置指定继电器开关状态: 只写 GPIO 和 Relay_State, 不碰 EEPROM/网络 */
void relaySet(uint8_t ch, uint8_t on)
{
if (ch < 1 || ch > 4) return;
MqttInfoStr.Relay_State[ch] = on ? 1 : 0;
switch (ch) {
case 1: if (on) Relay1_ON; else Relay1_OFF; break;
case 2: if (on) Relay2_ON; else Relay2_OFF; break;
case 3: if (on) Relay3_ON; else Relay3_OFF; break;
case 4: if (on) Relay4_ON; else Relay4_OFF; break;
}
}
/* 请求一次状态上报: 由主循环检测到 relay_report_pending 后延时统一上报 */
void relayRequestReport(void)
{
relay_report_tick = HAL_GetTick();
relay_report_pending = 1;
}
/* 上报继电器当前状态到MQTT平台 */
void relayStatueUpdata(void)
{
/* 云平台物模型字段weight=剩余料重(净重)zero=空载重量(去皮)onceWt=单次投料量 */
/* sw1 与 feeding 均上报继电器 1 状态,平台两个开关都能同步刷新 */
NET_publish_status(
MqttInfoStr.Relay_State[1] ? 1 : 0,
MqttInfoStr.Relay_State[1] ? 1 : 0,
MqttInfoStr.Relay_State[2] ? 1 : 0,
MqttInfoStr.Relay_State[3] ? 1 : 0,
MqttInfoStr.Relay_State[4] ? 1 : 0,
(float)feedScaleGetZero() / 10.0f,
(float)feedScaleGetOnceWeight() / 10.0f,
(float)feedScaleGetWeight() / 10.0f);
}
/* 从 JSON 报文中解析控制指令并执行(取值统一走 proto 模块,按键名定位) */
void relayAction(uint8_t *data)
{
char *p;
char CmdId[20] = {0};
uint8_t ch = 0;
GPIO_PinState fb_before;
log_info("> 继电器命令: %.80s", data);
/* 支持两种 id 格式:"id":"xxx" 或 "id":xxx */
if (!proto_get_id((char *)data, CmdId, sizeof(CmdId))) {
log_warn("> 继电器: id 解析失败");
return;
}
if (strstr((char *)data, "\"sw")) {
char swkey[4];
int on = 0;
p = strstr((char *)data, "\"sw");
ch = p[3] - '0';
/* sw1 与 feeding 指令等效, 均控制继电器 1 */
if (ch < 1 || ch > 4) { log_warn("> 继电器: sw 编号无效,仅支持 sw1~sw4"); return; }
swkey[0] = 's'; swkey[1] = 'w'; swkey[2] = (char)('0' + ch); swkey[3] = '\0';
if (!proto_get_bool((char *)data, swkey, &on)) {
log_warn("> 继电器: sw%d 值无效", ch);
NET_publish_response(CmdId, 0, "bad sw value");
return;
}
/* 控制继电器前先读取反馈电平 */
fb_before = relayReadFb(ch);
if (g_feed_scale_on) {
/* 称重自动投喂模式下继电器 1 仅由 feeding 指令控制, sw1 直接忽略 */
if (ch == 1) {
log_warn("> 继电器: 称重投喂模式下忽略 sw1请用 feeding 指令");
NET_publish_response(CmdId, 0, "sw1 disabled, use feeding");
return;
}
}
relaySet(ch, (uint8_t)on);
log_info("> SW%d %s", ch, on ? "" : "");
} else if (strstr((char *)data, "\"feeding\"")) {
/* 喂料/投喂指令: body.feeding.value=true 启动, value=false 停止 */
int value_true = 0;
if (!proto_get_bool((char *)data, "feeding", &value_true)) {
log_warn("> 继电器: feeding 值无效");
NET_publish_response(CmdId, 0, "bad feeding value");
return;
}
ch = 1;
fb_before = relayReadFb(ch);
if (!g_feed_scale_on) {
if (value_true) {
relaySet(1, 1);
log_info("> 继电器: 手动投喂开(无秤)");
} else {
relaySet(1, 0);
log_info("> 继电器: 手动投喂关(无秤)");
}
} else {
if (value_true) {
/* 首次上电查询到有效重量前不允许启动投喂 */
if (feedScaleGetWeight() == 0) {
log_warn("> 继电器: 拒绝启动投喂,重量未就绪");
NET_publish_response(CmdId, 0, "weight not ready");
return;
}
/* 优先使用本次指令中携带的 onceWt, 否则使用 EEPROM 保存的值 */
uint16_t once_wt_100g = 0;
proto_get_kg100((char *)data, "onceWt", &once_wt_100g);
if (once_wt_100g == 0) {
once_wt_100g = feedScaleGetOnceWeight();
} else {
feedScaleSetOnceWeight(once_wt_100g);
}
if (once_wt_100g == 0) {
log_warn("> 继电器: 无 onceWt 单次投喂量,已忽略");
NET_publish_response(CmdId, 0, "no onceWt");
return;
}
log_info("> 继电器: 开始投喂onceWt=%.1fKG", (float)once_wt_100g / 10.0f);
feedScaleStart(once_wt_100g);
} else {
log_info("> 继电器: 停止投喂");
feedScaleStop();
}
}
} else if (proto_locate((char *)data, "onceWt")) {
if (!g_feed_scale_on) {
/* 无秤模式:无单次投喂量概念,拒绝设置 */
log_warn("> 继电器: 拒绝设置 onceWt无秤");
NET_publish_response(CmdId, 0, "no scale on this device");
return;
} else {
/* 单独设置单次投喂量, 持久化到 EEPROM */
uint16_t once_wt_100g = 0;
proto_get_kg100((char *)data, "onceWt", &once_wt_100g);
if (once_wt_100g == 0 || once_wt_100g > 5000) {
log_warn("> 继电器: onceWt 无效");
NET_publish_response(CmdId, 0, "invalid onceWt");
return;
}
feedScaleSetOnceWeight(once_wt_100g);
NET_publish_response(CmdId, 1, "OK");
relayStatueUpdata();
return;
}
} else if (proto_locate((char *)data, "zero")) {
if (!g_feed_scale_on) {
/* 无秤模式:无去皮/空载重量概念,拒绝设置 */
log_warn("> 继电器: 拒绝设置 zero无秤");
NET_publish_response(CmdId, 0, "no scale on this device");
return;
} else {
/* 平台下发空载重量/去皮值,直接解析 value 并保存 */
uint16_t zero_100g = 0;
proto_get_kg100((char *)data, "zero", &zero_100g);
if (zero_100g == 0 || zero_100g > 5000) {
log_warn("> 继电器: zero 无效");
NET_publish_response(CmdId, 0, "invalid zero");
return;
}
feedScaleSetZero(zero_100g);
NET_publish_response(CmdId, 1, "OK");
relayStatueUpdata();
return;
}
} else {
log_warn("> 继电器: 未知指令(无 sw/feeding/zero");
return;
}
/* 保存继电器状态到EEPROM, 掉电后恢复 */
eepromWriteData(102, &MqttInfoStr.Relay_State[0], 7);
/* 启动非阻塞反馈检测与响应流程 */
relay_cmd_stage = 1;
relay_cmd_tick = HAL_GetTick();
relay_cmd_ch = ch;
relay_cmd_fb_before = fb_before;
memset(relay_cmd_id, 0, sizeof(relay_cmd_id));
memcpy(relay_cmd_id, CmdId, sizeof(relay_cmd_id) - 1);
}

View File

@ -1,485 +0,0 @@
/* app_loop.c - FreeRTOS 任务调度封装
*
*
* net_task(3, 3KB) - NET_process4G/WiFi OTA
* app_task(2, 2KB) - /OTA
* sys_task(1, 1KB) - LED
* idle - IWDG
* CPU idle 饿
*
*
* g_net_at_mutex - NET_publish_* AT /
* s_storage_mutex - EEPROM(I2C) / W25Q64(SPI) 访storage_lock
* log - log.c
*/
#include "app_loop.h"
#include "app_common.h"
#include "user_cmd.h"
#include "relay.h"
#include "network.h"
#include "led.h"
#include "adc.h"
#include "feed_scale.h"
#include "iic.h"
#include "24c02.h"
#include "uart.h"
#include "log_cn.h"
#include "ble_at.h"
#include "hlw8032.h"
#include "oled.h"
#include "reset_log.h"
#include "../W25Q64/w25q64.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/*==== 存储总线互斥锁EEPROM/W25Q64 共用一把递归锁)====*/
static SemaphoreHandle_t s_storage_mutex = NULL;
void storage_lock_init(void)
{
s_storage_mutex = xSemaphoreCreateRecursiveMutex();
}
void storage_lock(void)
{
if (s_storage_mutex && xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
xSemaphoreTakeRecursive(s_storage_mutex, portMAX_DELAY);
}
}
void storage_unlock(void)
{
if (s_storage_mutex && xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
xSemaphoreGiveRecursive(s_storage_mutex);
}
}
/*==== 业务模块初始化(调度器启动前调用,与裸机版一致)====*/
void app_init(void)
{
log_info("");
log_info("* * * * * * * * * * * * * * * * * *");
log_info("* 版本: %s (RTOS) *", FIRMWARE_VERSION_STR);
log_info("* * * * * * * By Helei * * * *");
/* EEPROM 中的 MQTT/版本配置 */
iicInit();
eepromReadInfo();
log_load_level(); /* 恢复保存的日志等级 */
log_info("> 服务器: %s:%d", MqttInfoStr.ServerIP, MqttInfoStr.ServerPort);
log_info("> 客户端ID: %s", MqttInfoStr.ClientID);
log_info("> 用户名: %s", MqttInfoStr.Username);
log_info("> 版本序号: %u", MqttInfoStr.Ver);
/* W25Q64 外部 Flash用于 OTA 固件暂存 */
W25Q64_Init();
/* 生成本次重启记录(原因+上轮运行时长)并打印最近 10 条 */
reset_log_boot();
/* 继电器 GPIO控制输出 + 反馈输入) */
relayInit();
/* 产品模式(带秤/无秤EEPROM 可切,默认跟编译宏) */
feed_scale_load_mode();
gps_load_mode(); /* GPS 定位开关EEPROM 173蓝牙 GPS0/1 切换) */
/* RS485 喂料秤控制模块 */
feedScaleInit();
/* LEDLED2 心跳同时兼任外部看门狗 TPL5010 喂狗) */
ledInit();
/* ADC 电量检测 */
adcInit();
update_batter();
/* HLW8032 电能计量UART5 PD2只累计电量 */
hlw8032_init();
/* OLED 屏幕128x64 + GB2312 字库 IC */
oled_init();
if (g_feed_scale_on) { oled_locate(1, 1); oled_print("智能水产投料机"); }
else { oled_locate(1, 1); oled_print("继电器控制终端"); }
oled_locate(2, 2); oled_print("V" FIRMWARE_VERSION_STR " RTOS");
log_info("> OLED: 初始化完成");
/* 网络模块初始化:按 EEPROM 记忆的上次成功方式优先4G 或 WiFi */
NET_init();
/* 启动各路串口接收 */
USART2_StartRx(); /* RS485 喂料秤 */
USART3_StartRx(); /* 4G MQTT 消息DMA+IDLE */
USART1_StartRx(); /* 蓝牙调试命令 */
/* 上电把 HLK-B40 蓝牙名称设置为 MQTT ClientID多台设备可区分 */
ble_at_init();
ble_setup_name();
}
/*==== net_task网络状态机4G/WiFi 建链、收发、OTA====*/
static void net_task(void *arg)
{
(void)arg;
for (;;) {
NET_process();
vTaskDelay(5);
}
}
/*==== app_task业务逻辑继电器/秤/上报/OTA 确认)====*/
static void app_task(void *arg)
{
(void)arg;
/* 主循环状态变量 */
static uint8_t ota_confirmed = 0;
static uint8_t ota_simulate_fail = 0; /* =1测试模拟 OTA 确认失败(验证回退) */
static uint8_t ota_fail_logged = 0;
static uint32_t ota_confirm_tick = 0;
static uint8_t ota_report_pending = 0;
static uint32_t ota_report_tick = 0;
static uint32_t mqtt_ready_tick = 0;
static uint8_t mqtt_ready_recorded = 0;
static uint8_t mqtt_first_status_reported = 0;
for (;;) {
/* 记录 MQTT 首次就绪时刻 */
if (!mqtt_ready_recorded && g_net_mqtt_ready) {
mqtt_ready_recorded = 1;
mqtt_ready_tick = HAL_GetTick();
}
/* MQTT 首次就绪后:先按 EEPROM 恢复继电器上次状态(断电记忆),
* */
if (g_net_mqtt_ready && !mqtt_first_status_reported) {
mqtt_first_status_reported = 1;
relayRestoreState();
relayStatueUpdata();
{ /* 每次重启联网后上报一次代码版本:"1.5.0:15"(字符串版本 + EEPROM 版本序号) */
char verbuf[24];
snprintf(verbuf, sizeof(verbuf), "%s:%u",
FIRMWARE_VERSION_STR, (unsigned)MqttInfoStr.Ver);
NET_publish_version(verbuf);
}
log_info("> Main: MQTT Ready 后首次状态上报");
ota_confirm_tick = HAL_GetTick();
}
/* 首次发布成功后延迟 9s 再 OTA 确认避开开机上报风暴pow/ICCID/状态/电能) */
if (!ota_confirmed && g_net_mqtt_ready && g_net_first_publish_ok &&
(HAL_GetTick() - ota_confirm_tick) > 9000) {
if (ota_simulate_fail) {
if (!ota_fail_logged) {
ota_fail_logged = 1;
log_info("> OTA 确认跳过(模拟失败,用于回退测试)");
}
/* 不置位 ota_confirmed让 1 分钟健康超时触发复位 */
} else {
ota_confirmed = 1;
if (NET_ota_confirm() < 0) { /* <0 才是未送达1=无挂起 OTA 属正常 */
ota_report_pending = 1; /* 通知未送达30s 后重试 */
ota_report_tick = HAL_GetTick();
log_warn("> OTA 确认通知未送达,稍后重试");
} else {
log_info("> OTA 已确认MQTT Ready + 首次发布成功 + 延迟9s");
}
}
}
/* OTA 确认通知未送达:每 30s 重试直到成功(本地确认标志已落盘,仅补平台通知) */
if (ota_report_pending && g_net_mqtt_ready &&
(HAL_GetTick() - ota_report_tick) > 30000) {
ota_report_tick = HAL_GetTick();
if (NET_ota_confirm() >= 0) {
ota_report_pending = 0;
log_info("> OTA 确认通知重试成功");
}
}
/* MQTT 就绪后 1 分钟仍未首次 MPUB主动复位 */
if (!ota_confirmed && mqtt_ready_recorded &&
(HAL_GetTick() - mqtt_ready_tick) > OTA_HEALTH_TIMEOUT_MS) {
log_warn("> OTA 健康超时: MQTT Ready 但 1 分钟内无 MPUB复位");
reset_log_mark(RSN_OTA_HEALTH);
W25Q64_OTA_IncTrial(); /* 软件复位 BL 不计次,这里自行消耗一次试错 */
HAL_Delay(100);
NVIC_SystemReset();
}
/* 继电器命令响应状态机 */
relayCmdProcess();
/* 延迟状态上报 */
if (relay_report_pending && (HAL_GetTick() - relay_report_tick) > 100) {
relay_report_pending = 0;
relayStatueUpdata();
}
/* 喂料秤轮询与投喂保护 */
feedScaleProcess();
vTaskDelay(10);
}
}
/*==== OLED 界面刷新1s 一次)
* ///+
* g_net_ui_phase 4G/WiFi WiFi
* 10s 0/2/4px ====*/
static void oled_home_refresh(uint32_t up_ms);
/* 调度器启动前的阻塞建链阶段app_init 里的 4G 初始化),
* sys_task bg_delay 500ms */
void app_oled_presched_tick(void)
{
static uint32_t last = 0;
if (HAL_GetTick() - last < 500) return;
last = HAL_GetTick();
oled_home_refresh(0);
}
/* 写一整行(带防烧屏偏移):先写空格清掉左移腾出的列,再从偏移处写内容 */
static void oled_row(uint8_t row, uint8_t xoff, const char *s)
{
/* 按内容宽度钳位:内容宽就少移,保证不超出右边界 */
uint8_t w = oled_text_w(s);
if (xoff > 0 && (uint16_t)xoff + w > 128) xoff = 128 - w;
if (xoff) {
oled_locate(row, 0);
for (uint8_t c = 0; c < xoff; c += 8) oled_print(" "); /* 清空左侧腾出的列,防残留 */
oled_locate(row, 0);
}
oled_locate(row, 0);
oled_xoff(xoff);
oled_print_line(s);
}
static void oled_home_refresh(uint32_t up_ms)
{
char buf[24];
/* 防烧屏:每 10s 换一档左右往返0→24→0按内容宽度自动钳位 */
uint8_t phase = (uint8_t)((HAL_GetTick() / 10000u) % 8u);
uint8_t xoff = (phase <= 4u) ? phase * 6u : (8u - phase) * 6u;
if (g_net_mqtt_ready) {
/*---- 已联网界面 ----*/
/* R0: 网络 + 运行时间(天:时:分) */
uint32_t mins = up_ms / 60000u;
const char *nt = (NET_GetActiveBackend() == NET_BACKEND_WIFI) ? "WiFi" : "4G";
snprintf(buf, sizeof(buf), "%s %02u:%02u:%02u", nt,
(unsigned)(mins / 1440u),
(unsigned)((mins % 1440u) / 60u),
(unsigned)(mins % 60u));
oled_row(0, xoff, buf);
/* R1/R2: 电能参数 */
if (hlw8032_online()) {
snprintf(buf, sizeof(buf), "%.1fV %.2fA",
(double)hlw8032_get_voltage_v(), (double)hlw8032_get_current_a());
oled_row(1, xoff, buf);
snprintf(buf, sizeof(buf), "%.1fW %.2fkWh",
(double)hlw8032_get_power_w(), (double)hlw8032_get_energy_kwh());
oled_row(2, xoff, buf);
} else {
oled_row(1, xoff, "计量芯片离线");
oled_row(2, xoff, "");
}
/* R3: 带秤模式显示重量(读不到显示原因),无秤模式显示开关状态 */
if (g_feed_scale_on) {
if (feedScaleGetLastErr() == 0 &&
(HAL_GetTick() - feedScaleGetLastOkTick()) < 40000u) {
snprintf(buf, sizeof(buf), "重量:%.1fKG",
(double)(feedScaleGetWeight() / 10.0f));
} else {
const char *reason;
switch (feedScaleGetLastErr()) {
case 2: reason = "帧短"; break;
case 3: reason = "CRC错"; break;
case 4: reason = "秤异常帧"; break;
case 5: reason = "站号不符"; break;
default: reason = "无应答"; break;
}
snprintf(buf, sizeof(buf), "重量:%s", reason);
}
oled_row(3, xoff, buf);
} else {
snprintf(buf, sizeof(buf), "开关:%s%s%s%s",
MqttInfoStr.Relay_State[1] ? "" : "",
MqttInfoStr.Relay_State[2] ? "" : "",
MqttInfoStr.Relay_State[3] ? "" : "",
MqttInfoStr.Relay_State[4] ? "" : "");
oled_row(3, xoff, buf);
}
} else {
/*---- 未联网界面:联网进度 ----*/
net_ui_phase_t ph = g_net_ui_phase;
oled_row(0, xoff, "断网");
if (ph == NET_UI_WIFI_FIXED || ph == NET_UI_WIFI_SMART ||
NET_GetActiveBackend() == NET_BACKEND_WIFI) {
if (ph == NET_UI_WIFI_FIXED) {
oled_row(1, xoff, "WiFi固定模式");
oled_row(2, xoff, "正在搜索热点");
} else if (ph == NET_UI_WIFI_SMART) {
oled_row(1, xoff, "智能配网");
oled_row(2, xoff, "请打开APP广播");
} else {
oled_row(1, xoff, "WiFi模式");
oled_row(2, xoff, "初始化中...");
}
snprintf(buf, sizeof(buf), "剩余 %ds", g_net_ui_countdown);
oled_row(3, xoff, g_net_ui_countdown > 0 ? buf : "");
} else {
/* 4G 进度:显示最近完成的阶段 */
const char *step = "初始化中";
switch (ph) {
case NET_UI_4G_RESET: step = "模块复位成功"; break;
case NET_UI_4G_AT: step = "AT指令成功"; break;
case NET_UI_4G_SIM: step = "SIM卡检测成功"; break;
case NET_UI_4G_ATTACH: step = "附着网络成功"; break;
case NET_UI_4G_MQTT: step = "连接服务器中"; break;
case NET_UI_4G_NOSIM: step = "无SIM卡!"; break;
default: break;
}
oled_row(1, xoff, "正在等待4G联网");
oled_row(2, xoff, step);
oled_row(3, xoff, "");
}
}
}
/* 电能上报策略:无论功率多少,每 3 分钟上报一次energy+power 同一条报文)。
* 10s */
#define ENERGY_REPORT_INTERVAL_MS 180000u
/*==== sys_task系统辅助LED/电量/蓝牙命令/运行时长)====*/
static void sys_task(void *arg)
{
(void)arg;
uint32_t batt_tick = HAL_GetTick();
uint32_t batt_fast_tick = HAL_GetTick();
uint32_t up_last = HAL_GetTick();
uint32_t up_print = HAL_GetTick();
uint32_t en_print = HAL_GetTick();
uint64_t up_ms = 0;
for (;;) {
/* 蓝牙调试命令 */
process_usart1_command();
/* 联网指示连接中闪烁连上常亮led.c activeEvents 据此控制 PA1 */
g_net_led_connected = g_net_mqtt_ready ? 1 : 0;
/* LED 心跳/联网指示 */
activeEvents();
/* 断电检测 50ms 快轮询PA0 掉电要抢在法拉电容耗尽前发消息) */
if (HAL_GetTick() - batt_fast_tick >= 50) {
batt_fast_tick = HAL_GetTick();
update_batter();
}
/* 运行秒数 1s 节流更新 */
if (HAL_GetTick() - batt_tick >= 1000) {
batt_tick = HAL_GetTick();
hlw8032_tick(); /* 攒够约 0.1kWh 写一次 EEPROM */
oled_home_refresh(up_ms); /* OLED 界面 1s 刷新 */
reset_log_tick((uint32_t)(up_ms / 1000)); /* 共享 RAM 更新运行秒数,热复位不丢 */
}
/* 运行时长心跳:联网后每 60s 串口输出一次(天/时/分);
* /
* 64 HAL_GetTick 49 */
{
uint32_t now = HAL_GetTick();
up_ms += (uint32_t)(now - up_last); /* 32位差值天然容忍回绕 */
up_last = now;
if (now - up_print >= 60000) {
up_print = now;
if (g_net_mqtt_ready) {
uint32_t mins = (uint32_t)(up_ms / 60000);
log_info(FMT_UPTIME,
(unsigned)(mins / 1440),
(unsigned)((mins % 1440) / 60),
(unsigned)(mins % 60));
}
}
}
/* 电能:串口日志固定 10s 一条;上报为联网后首次一条,之后固定每 3 分钟一条 */
if (g_net_mqtt_ready && hlw8032_online() &&
(HAL_GetTick() - en_print) >= 10000u) {
en_print = HAL_GetTick();
static uint8_t energy_first_reported = 0;
static uint32_t energy_report_tick = 0;
float kwh = hlw8032_get_energy_kwh();
float pw = hlw8032_get_power_w();
uint8_t report = 0;
if (!energy_first_reported) {
report = 1;
energy_first_reported = 1;
} else if ((HAL_GetTick() - energy_report_tick) >= ENERGY_REPORT_INTERVAL_MS) {
report = 1;
}
if (report) {
energy_report_tick = HAL_GetTick();
NET_publish_energy(kwh, pw);
}
log_info(FMT_ENERGY, (double)kwh,
(double)hlw8032_get_voltage_v(),
(double)hlw8032_get_current_a(),
(double)pw);
}
vTaskDelay(20);
}
}
/*==== FreeRTOS 钩子 ====*/
/* idle 钩子:喂独立看门狗。有任务死循环占 CPU 时 idle 饿死 → IWDG 复位 */
void vApplicationIdleHook(void)
{
IWDG_Feed();
}
void vApplicationMallocFailedHook(void)
{
log_error("> RTOS: malloc 失败(堆耗尽)");
taskDISABLE_INTERRUPTS();
for (;;) { }
}
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
{
(void)xTask;
log_error("> RTOS: 任务 %s 栈溢出", pcTaskName);
taskDISABLE_INTERRUPTS();
for (;;) { }
}
/*==== 任务创建与调度器启动main 调用,不再返回)====*/
void app_rtos_start(void)
{
/* 互斥锁先于任务创建 */
log_lock_init();
storage_lock_init();
NET_LockInit();
xTaskCreate(net_task, "net", 768, NULL, 3, NULL); /* 栈 3KBAT/OTA 有大的 sprintf 缓冲 */
xTaskCreate(app_task, "app", 512, NULL, 2, NULL); /* 栈 2KB */
xTaskCreate(sys_task, "sys", 512, NULL, 1, NULL); /* 栈 2KB电能日志一行 4 个浮点格式化很吃栈 */
vTaskStartScheduler();
/* 不会走到这里(堆不足创建失败才会) */
log_error("> RTOS: 调度器启动失败");
for (;;) { }
}

View File

@ -1,14 +0,0 @@
#ifndef __APP_LOOP_H
#define __APP_LOOP_H
/* FreeRTOS 任务调度封装 */
void app_init(void); /* 业务模块初始化(横幅/外设/网络/串口接收) */
void app_rtos_start(void); /* 创建任务并启动调度器(不再返回) */
void app_oled_presched_tick(void); /* 调度器启动前阻塞建链期间的屏幕进度刷新bg_delay 调用500ms 节流) */
/* 存储总线互斥EEPROM 软件I2C 与 W25Q64 软件SPI 跨任务共用) */
void storage_lock_init(void);
void storage_lock(void);
void storage_unlock(void);
#endif /* __APP_LOOP_H */

View File

@ -1,226 +0,0 @@
/* reset_log.c - 重启记录(原因 + 上次运行时长)
*
* W25Q64 0x100000 4KB 10
* 寿
* BOOT_SHARE RAM/
* RAM
* PA0 <10% 12V 30s
* RCC->CSR BL BOOT_SHARE APPBL
* reset_log_mark()
*/
#include "reset_log.h"
#include "boot_share.h"
#include "log.h"
#include "main.h"
#include "app_loop.h"
#include "hlw8032.h"
#include "../W25Q64/w25q64.h"
#include "string.h"
#define RST_LOG_ADDR 0x100000u /* 避开 OTA 区(0~0x81FFF) */
#define RST_LOG_SLOTS 10
#define RST_REC_MAGIC 0x5253544Cu /* 'RSTL' */
#define RST_REC_SIZE 32u
/* 每条记录独占一个 4KB 扇区NOR Flash 只能 1->0 翻位,同扇区覆写必须先整擦,
* 10 9
* "第 11 条只覆盖最旧那条"W25Q64 8MB 10 */
typedef struct {
uint32_t magic; /* RST_REC_MAGIC */
uint32_t seq; /* 递增序号,大者新 */
uint8_t reason; /* RSN_x */
uint8_t boot_seen; /* 0xFF=新记录;断电预警记录被下次开机消费后置 0x00位翻转免擦除 */
uint8_t rsvd[2];
uint32_t uptime_sec; /* 上次运行时长0xFFFFFFFF=未知(冷启动 RAM 丢失) */
uint16_t crc16; /* 前 14 字节累加和 */
uint8_t pad[16];
} rst_rec_t;
static const char *reason_str(uint8_t r)
{
switch (r) {
case RSN_POWER_ON: return "上电/意外掉电";
case RSN_PIN_RESET: return "按键复位";
case RSN_WATCHDOG: return "卡死看门狗溢出";
case RSN_KICKED: return "被平台踢下线";
case RSN_PING_TIMEOUT: return "心跳超时(服务器无响应)";
case RSN_NO_SERVICE: return "4G无网络服务";
case RSN_NO_SIM: return "无SIM卡";
case RSN_OTA_UPDATE: return "OTA升级完成";
case RSN_OTA_HEALTH: return "OTA健康检查超时";
case RSN_4G_FAIL: return "4G初始化失败";
case RSN_CMD_RESET: return "串口RESET命令";
case RSN_CMD_OTA: return "串口OTA命令";
case RSN_CMD_RECOVERY: return "串口RECOVERY命令";
case RSN_POWER_LOSS: return "外部断电(法拉电容记录)";
case RSN_4G_MODULE: return "4G模组异常自重启";
case RSN_OTA_FAIL: return "OTA失败中止";
case RSN_SOFT_GENERIC: return "软件复位(未标记)";
default: return "未知";
}
}
static uint16_t rec_sum(const rst_rec_t *r)
{
const uint8_t *p = (const uint8_t *)r;
uint16_t s = 0;
for (int i = 0; i < 14; i++) s += p[i]; /* magic+seq+reason+boot_seen+rsvd+uptime */
return s;
}
static int rec_valid(const rst_rec_t *r)
{
return r->magic == RST_REC_MAGIC && r->crc16 == rec_sum(r);
}
static void rec_read(uint8_t slot, rst_rec_t *r)
{
W25Q64_Read(RST_LOG_ADDR + (uint32_t)slot * W25Q64_SECTOR_SIZE, (uint8_t *)r, RST_REC_SIZE);
}
static void rec_write(uint8_t slot, rst_rec_t *r)
{
r->crc16 = rec_sum(r);
/* 每条独占一扇区覆写前先擦除该扇区NOR 只能 1->0 翻位) */
W25Q64_EraseRegion(RST_LOG_ADDR + (uint32_t)slot * W25Q64_SECTOR_SIZE, W25Q64_SECTOR_SIZE);
W25Q64_WriteBuffer(RST_LOG_ADDR + (uint32_t)slot * W25Q64_SECTOR_SIZE, (const uint8_t *)r, RST_REC_SIZE);
}
/* 找最新记录:返回槽位;无有效记录返回 -1 */
static int find_newest(uint32_t *seq_out)
{
rst_rec_t r;
int best = -1;
uint32_t best_seq = 0;
for (uint8_t i = 0; i < RST_LOG_SLOTS; i++) {
rec_read(i, &r);
if (rec_valid(&r) && (best < 0 || (int32_t)(r.seq - best_seq) > 0)) {
best = i;
best_seq = r.seq;
}
}
if (seq_out) *seq_out = best_seq;
return best;
}
/* 追加一条记录(自动环形覆盖最旧) */
static void append_record(uint8_t reason, uint32_t uptime_sec)
{
uint32_t seq = 0;
int newest = find_newest(&seq);
int slot = (newest < 0) ? 0 : (newest + 1) % RST_LOG_SLOTS;
rst_rec_t r;
memset(&r, 0xFF, sizeof(r));
r.magic = RST_REC_MAGIC;
r.seq = seq + 1;
r.reason = reason;
r.boot_seen = 0xFF;
r.uptime_sec = uptime_sec;
rec_write((uint8_t)slot, &r);
}
/* 主动复位前标记细分原因(共享 RAM热复位后 APP 开机读取) */
void reset_log_mark(uint8_t reason)
{
hlw8032_save(); /* 计划内复位前把累计电量落 EEPROM无变化时空操作 */
BOOT_SHARE->pending_reason = reason;
boot_share_store();
}
/* 每秒更新运行时长到共享 RAM热复位不丢 */
void reset_log_tick(uint32_t uptime_s)
{
BOOT_SHARE->uptime_sec = uptime_s;
boot_share_store();
}
/* 外部断电预警PA0 低电量 = 12V 断开,法拉电容续航中):立即写断电记录 */
void reset_log_power_loss(void)
{
uint32_t up = boot_share_valid() ? BOOT_SHARE->uptime_sec : 0xFFFFFFFFu;
uint32_t m = up / 60;
log_warn("> 断电预警:外部 12V 断开,记录运行时间 %u天%02u小时%02u分%02u秒",
(unsigned)(m / 1440), (unsigned)((m % 1440) / 60), (unsigned)(m % 60), (unsigned)(up % 60));
append_record(RSN_POWER_LOSS, up);
}
/*==== 开机:根据 BL 传来的 CSR + 预留原因码生成本次记录,并打印最近 10 条 ====*/
void reset_log_boot(void)
{
uint8_t reason = RSN_UNKNOWN;
uint32_t up = 0xFFFFFFFFu;
int skip_new = 0;
if (boot_share_valid()) {
uint32_t csr = BOOT_SHARE->csr_flags;
up = BOOT_SHARE->uptime_sec; /* 热复位时这就是上轮运行时长 */
if ((csr & RCC_CSR_SFTRSTF) && BOOT_SHARE->pending_reason != 0) {
reason = (uint8_t)BOOT_SHARE->pending_reason;
} else if (csr & RCC_CSR_SFTRSTF) {
reason = RSN_SOFT_GENERIC;
} else if (csr & RCC_CSR_IWDGRSTF) {
reason = RSN_WATCHDOG;
} else if (csr & RCC_CSR_WWDGRSTF) {
reason = RSN_WATCHDOG;
} else if (csr & RCC_CSR_PINRSTF) {
reason = RSN_PIN_RESET;
} else if (csr & RCC_CSR_PORRSTF) {
reason = RSN_POWER_ON;
}
}
/* 断电预警记录消费(不限复位类型):断电时已预写记录,本次开机直接消费它,
* +BL */
{
uint32_t seq;
int newest = find_newest(&seq);
if (newest >= 0) {
rst_rec_t last;
rec_read((uint8_t)newest, &last);
if (last.reason == RSN_POWER_LOSS && last.boot_seen == 0xFF) {
last.boot_seen = 0x00;
rec_write((uint8_t)newest, &last); /* 位翻转 1->0 免擦除 */
skip_new = 1;
log_info("> 上次为断电关机(已记录)");
}
}
}
if (!skip_new) {
append_record(reason, up);
}
/* 本次开机初始化共享 RAM 状态(清预留原因,运行时长重新计) */
BOOT_SHARE->pending_reason = 0;
BOOT_SHARE->uptime_sec = 0;
BOOT_SHARE->magic = BOOT_SHARE_MAGIC;
boot_share_store();
/* 打印最近 10 条(新→旧),显示序号固定 1~10[1] 最新,写满后覆盖最旧) */
log_info("> 历史重启记录(新→旧):");
uint32_t newest_seq;
int newest = find_newest(&newest_seq);
if (newest < 0) {
log_info(" (无记录)");
return;
}
for (int i = 0; i < RST_LOG_SLOTS; i++) {
int slot = (newest - i + RST_LOG_SLOTS) % RST_LOG_SLOTS;
rst_rec_t r;
rec_read((uint8_t)slot, &r);
if (!rec_valid(&r)) continue;
if (r.uptime_sec == 0xFFFFFFFFu) {
log_info(" [%d] 重启原因: %s 运行时间: 未知",
i + 1, reason_str(r.reason));
} else {
uint32_t m = r.uptime_sec / 60;
log_info(" [%d] 重启原因: %s 运行时间: %u天%02u小时%02u分",
i + 1, reason_str(r.reason),
(unsigned)(m / 1440), (unsigned)((m % 1440) / 60), (unsigned)(m % 60));
}
}
}

View File

@ -1,125 +0,0 @@
#include "system.h"
#include "uart.h"
#include "relay.h"
DMA_HandleTypeDef hdma_usart3_rx;
DMA_HandleTypeDef hdma_uart4_rx;
static IWDG_HandleTypeDef hiwdg; /* 独立看门狗句柄,本模块内私有 */
/* LSI(~40kHz) / 256分频 / 重载4095 → 超时约26sLSI 漂移下最短约17s */
static void MX_IWDG_Init(void)
{
hiwdg.Instance = IWDG;
hiwdg.Init.Prescaler = IWDG_PRESCALER_256;
hiwdg.Init.Reload = 4095;
if (HAL_IWDG_Init(&hiwdg) != HAL_OK) Error_Handler();
}
/* 喂狗:主循环和 app_bg_service长阻塞等待间隙都必须调用 */
void IWDG_Feed(void)
{
HAL_IWDG_Refresh(&hiwdg);
}
/**
* @brief HAL//GPIO/DMA/UART/
* @note BootLoader IWDG
* NET_init bg_delay app_bg_service
*/
void Board_Init(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init(); /* RS485 喂料秤 */
MX_USART3_UART_Init(); /* 4G Air780E */
MX_UART4_UART_Init(); /* WiFi ESP-01S */
MX_IWDG_Init();
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) Error_Handler();
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) Error_Handler();
}
void MX_DMA_Init(void)
{
__HAL_RCC_DMA1_CLK_ENABLE();
__HAL_RCC_DMA2_CLK_ENABLE();
HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);
HAL_NVIC_SetPriority(DMA2_Channel3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Channel3_IRQn);
}
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef g = {0};
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
/* 继电器为高电平吸合,上电瞬间必须立即置低,防止浮空导致误吸合 */
HAL_GPIO_WritePin(Relay1_Port, Relay1_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(Relay2_Port, Relay2_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(Relay3_Port, Relay3_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(Relay4_Port, Relay4_Pin, GPIO_PIN_RESET);
g.Mode = GPIO_MODE_OUTPUT_PP;
g.Pull = GPIO_NOPULL;
g.Speed = GPIO_SPEED_FREQ_LOW;
g.Pin = Relay1_Pin; HAL_GPIO_Init(Relay1_Port, &g);
g.Pin = Relay2_Pin; HAL_GPIO_Init(Relay2_Port, &g);
g.Pin = Relay3_Pin; HAL_GPIO_Init(Relay3_Port, &g);
g.Pin = Relay4_Pin; HAL_GPIO_Init(Relay4_Port, &g);
/* CAT_PWR(PC4): 4G 模块开机脚,开漏输出,拉低=模块开机(上电直接拉低即可) */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, GPIO_PIN_RESET);
g.Mode = GPIO_MODE_OUTPUT_OD;
g.Pin = GPIO_PIN_4;
HAL_GPIO_Init(GPIOC, &g);
/* ESP_EN(PA6): WiFi 模块使能,高电平有效。默认关闭省电,
* network.c 4G WiFi */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET);
g.Mode = GPIO_MODE_OUTPUT_PP;
g.Pin = GPIO_PIN_6;
HAL_GPIO_Init(GPIOA, &g);
/* BT_RST(PA7): 蓝牙模块复位脚,低电平=正常工作,高电平=复位,推挽输出默认拉低 */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET);
g.Pin = GPIO_PIN_7;
HAL_GPIO_Init(GPIOA, &g);
}
void Error_Handler(void)
{
/* 初始化失败:停在这里,若 IWDG 已启动则由看门狗复位 */
__disable_irq();
while (1)
{
}
}

View File

@ -1,459 +0,0 @@
#include "uart.h"
#include "network.h"
#include "log.h"
#include "hlw8032.h"
#include <string.h>
#include <ctype.h>
UART_HandleTypeDef huart1;
UART_HandleTypeDef huart2;
UART_HandleTypeDef huart3;
UART_HandleTypeDef huart4;
UART_HandleTypeDef huart5;
DMA_HandleTypeDef hdma_usart2_rx;
uint16_t Rx_Len = 0;
uint8_t Rx_Buf[Rx_Max] = {0};
volatile uint8_t uart1_rx_ready = 0;
uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE] = {0};
/* 诊断计数UART4 高吞吐接收健康状况OTA 排障用) */
volatile uint32_t g_uart4_rxcplt_cnt = 0; /* DMA 填满重启次数 */
volatile uint32_t g_uart4_ore_cnt = 0; /* 溢出错误次数 */
volatile uint8_t uart2_rx_ready = 0;
uint8_t uart2_rx_buf[UART2_RX_BUF_SIZE] = {0};
static uint8_t uart1_rx_byte = 0;
static uint8_t uart1_cmd_buf[UART1_RX_BUF_SIZE] = {0};
static uint8_t uart1_cmd_index = 0;
static uint32_t uart1_last_rx_tick = 0;
static uint8_t uart5_rx_byte = 0;
static uint8_t uart2_cmd_buf[UART2_RX_BUF_SIZE] = {0};
static uint8_t uart2_cmd_index = 0;
static uint32_t uart2_last_rx_tick = 0;
/* USART2 DMA 接收485 秤DMA 填 dma_buf线路静默产生 IDLE 中断即为一帧 */
uint8_t uart2_dma_buf[64];
uint8_t uart2_frame_buf[64];
volatile uint16_t uart2_frame_len = 0;
volatile uint8_t uart2_frame_ready = 0;
#define UART1_CMD_TIMEOUT_MS 100u /* 无换行时,字符间隔超过该时间视为命令结束 */
#define UART2_CMD_TIMEOUT_MS 20u /* 485 返回帧较短20ms 视为帧结束 */
void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK) Error_Handler();
}
void MX_USART2_UART_Init(void)
{
huart2.Instance = USART2;
huart2.Init.BaudRate = 9600;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart2) != HAL_OK) Error_Handler();
}
void MX_USART3_UART_Init(void)
{
huart3.Instance = USART3;
huart3.Init.BaudRate = 115200; /* 上电默认 115200安全档cat_connect_network 里再自适应切 921600 */
huart3.Init.WordLength = UART_WORDLENGTH_8B;
huart3.Init.StopBits = UART_STOPBITS_1;
huart3.Init.Parity = UART_PARITY_NONE;
huart3.Init.Mode = UART_MODE_TX_RX;
huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart3.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart3) != HAL_OK) Error_Handler();
}
/* UART4: WiFi ESP-01S 模块 (PC10=TX -> ESP_RXD, PC11=RX <- ESP_TXD)
* 115200ESP-01S AT
* esp_reset AT+UART_DEF WIFI_BAUD_TARGET */
void MX_UART4_UART_Init(void)
{
huart4.Instance = UART4;
huart4.Init.BaudRate = 115200;
huart4.Init.WordLength = UART_WORDLENGTH_8B;
huart4.Init.StopBits = UART_STOPBITS_1;
huart4.Init.Parity = UART_PARITY_NONE;
huart4.Init.Mode = UART_MODE_TX_RX;
huart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart4.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart4) != HAL_OK) Error_Handler();
}
/* UART5: HLW8032 计量芯片 (PD2=RX <- 芯片 TXD)4800 8E1。
* TX(PC12) 8 + F1 9 */
void MX_UART5_UART_Init(void)
{
huart5.Instance = UART5;
huart5.Init.BaudRate = 4800;
huart5.Init.WordLength = UART_WORDLENGTH_9B;
huart5.Init.StopBits = UART_STOPBITS_1;
huart5.Init.Parity = UART_PARITY_EVEN;
huart5.Init.Mode = UART_MODE_RX;
huart5.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart5.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart5) != HAL_OK) Error_Handler();
}
void UART5_StartRx(void)
{
HAL_UART_Receive_IT(&huart5, &uart5_rx_byte, 1);
}
void USART3_StartRx(void)
{
/* 避免重复启动 DMA: 若已在接收则先停止再启动 */
if (huart3.RxState != HAL_UART_STATE_READY) {
HAL_UART_DMAStop(&huart3);
}
HAL_UART_Receive_DMA(&huart3, Rx_Buf, Rx_Max);
__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
}
/* 停止 USART3 DMA 接收WiFi/4G 互斥复用 Rx_Buf切 WiFi 通道时必须先停 4G 接收 */
void USART3_StopRx(void)
{
__HAL_UART_DISABLE_IT(&huart3, UART_IT_IDLE);
HAL_UART_DMAStop(&huart3);
}
/* 启动 UART4 DMA+IDLE 接收:与 USART3 复用同一块 Rx_Buf两通道互斥靠 StartRx/StopRx 保证) */
void UART4_StartRx(void)
{
if (huart4.RxState != HAL_UART_STATE_READY) {
HAL_UART_DMAStop(&huart4);
}
HAL_UART_Receive_DMA(&huart4, Rx_Buf, Rx_Max);
__HAL_UART_ENABLE_IT(&huart4, UART_IT_IDLE);
}
void UART4_StopRx(void)
{
__HAL_UART_DISABLE_IT(&huart4, UART_IT_IDLE);
HAL_UART_DMAStop(&huart4);
}
/* 运行中切换 UART4 波特率:只重写 BRR 寄存器,不动 GPIO/NVIC/DMA 配置。
* UART4 APB1(36MHz) */
void UART4_SetBaudRate(uint32_t baud)
{
if (huart4.RxState != HAL_UART_STATE_READY) {
HAL_UART_DMAStop(&huart4);
}
huart4.Init.BaudRate = baud;
UART4->BRR = UART_BRR_SAMPLING16(36000000u, baud);
__HAL_UART_CLEAR_PEFLAG(&huart4);
__HAL_UART_CLEAR_FEFLAG(&huart4);
__HAL_UART_CLEAR_NEFLAG(&huart4);
__HAL_UART_CLEAR_OREFLAG(&huart4);
__HAL_UART_CLEAR_IDLEFLAG(&huart4);
HAL_UART_Receive_DMA(&huart4, Rx_Buf, Rx_Max);
__HAL_UART_ENABLE_IT(&huart4, UART_IT_IDLE);
}
/* UART 错误回调ORE(溢出) 等错误会让 HAL 中止整个 DMA 接收
* (stm32f1xx_hal_uart.c: UART_EndRxTransfer + HAL_DMA_Abort_IT)
*
* 921600 IDLE DMA
* ORE */
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == UART4) {
g_uart4_ore_cnt++;
__HAL_UART_CLEAR_OREFLAG(huart);
__HAL_UART_CLEAR_PEFLAG(huart);
__HAL_UART_CLEAR_FEFLAG(huart);
__HAL_UART_CLEAR_NEFLAG(huart);
__HAL_UART_CLEAR_IDLEFLAG(huart);
huart->ErrorCode = HAL_UART_ERROR_NONE;
if (NET_GetActiveBackend() == NET_BACKEND_WIFI) {
HAL_UART_Receive_DMA(&huart4, Rx_Buf, Rx_Max);
__HAL_UART_ENABLE_IT(&huart4, UART_IT_IDLE);
}
}
else if (huart->Instance == USART1) {
__HAL_UART_CLEAR_OREFLAG(huart);
__HAL_UART_CLEAR_PEFLAG(huart);
__HAL_UART_CLEAR_FEFLAG(huart);
__HAL_UART_CLEAR_NEFLAG(huart);
__HAL_UART_CLEAR_IDLEFLAG(huart);
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart1.RxState = HAL_UART_STATE_READY; /* 关键HAL 错误处理后 RxState 常卡 BUSY_RXReceive_IT 会静默失败,接收永久停摆 */
HAL_UART_Receive_IT(&huart1, &uart1_rx_byte, 1); /* 蓝牙/调试口被 ORE 弄挂后立即恢复接收 */
}
else if (huart->Instance == USART2) {
__HAL_UART_CLEAR_OREFLAG(huart);
__HAL_UART_CLEAR_PEFLAG(huart);
__HAL_UART_CLEAR_FEFLAG(huart);
__HAL_UART_CLEAR_NEFLAG(huart);
__HAL_UART_CLEAR_IDLEFLAG(huart);
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart2.RxState = HAL_UART_STATE_READY;
HAL_UART_Receive_DMA(&huart2, uart2_dma_buf, sizeof(uart2_dma_buf)); /* RS485 秤口 DMA 自愈 */
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
}
else if (huart->Instance == UART5) {
__HAL_UART_CLEAR_OREFLAG(huart);
__HAL_UART_CLEAR_PEFLAG(huart);
__HAL_UART_CLEAR_FEFLAG(huart);
__HAL_UART_CLEAR_NEFLAG(huart);
__HAL_UART_CLEAR_IDLEFLAG(huart);
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart5.RxState = HAL_UART_STATE_READY; /* 关键HAL 错误处理后 RxState 常卡 BUSY_RXReceive_IT 会静默失败,接收永久停摆 */
HAL_UART_Receive_IT(&huart5, &uart5_rx_byte, 1); /* HLW8032 口同样自愈 */
}
else if (huart->Instance == USART3) {
__HAL_UART_CLEAR_OREFLAG(huart);
__HAL_UART_CLEAR_PEFLAG(huart);
__HAL_UART_CLEAR_FEFLAG(huart);
__HAL_UART_CLEAR_NEFLAG(huart);
__HAL_UART_CLEAR_IDLEFLAG(huart);
huart->ErrorCode = HAL_UART_ERROR_NONE;
if (NET_GetActiveBackend() == NET_BACKEND_4G) {
HAL_UART_Receive_DMA(&huart3, Rx_Buf, Rx_Max);
__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
}
}
}
/* 运行中切换 USART3 波特率:只重写 BRR 寄存器,不动 GPIO/NVIC/DMA 配置。
* USART3 APB1(36MHz) */
void USART3_SetBaudRate(uint32_t baud)
{
if (huart3.RxState != HAL_UART_STATE_READY) {
HAL_UART_DMAStop(&huart3);
}
huart3.Init.BaudRate = baud;
USART3->BRR = UART_BRR_SAMPLING16(36000000u, baud);
__HAL_UART_CLEAR_PEFLAG(&huart3);
__HAL_UART_CLEAR_FEFLAG(&huart3);
__HAL_UART_CLEAR_NEFLAG(&huart3);
__HAL_UART_CLEAR_OREFLAG(&huart3);
__HAL_UART_CLEAR_IDLEFLAG(&huart3);
HAL_UART_Receive_DMA(&huart3, Rx_Buf, Rx_Max);
__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
}
void USART1_StartRx(void)
{
uart1_rx_ready = 0;
uart1_cmd_index = 0;
uart1_last_rx_tick = 0;
memset(uart1_cmd_buf, 0, sizeof(uart1_cmd_buf));
memset(uart1_rx_buf, 0, sizeof(uart1_rx_buf));
HAL_UART_Receive_IT(&huart1, &uart1_rx_byte, 1);
}
void USART2_StartRx(void)
{
uart2_rx_ready = 0;
uart2_cmd_index = 0;
uart2_last_rx_tick = 0;
memset(uart2_cmd_buf, 0, sizeof(uart2_cmd_buf));
memset(uart2_rx_buf, 0, sizeof(uart2_rx_buf));
uart2_frame_ready = 0;
uart2_frame_len = 0;
HAL_UART_Receive_DMA(&huart2, uart2_dma_buf, sizeof(uart2_dma_buf));
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
}
/* 清 USART2 软件接收缓冲(不动硬件)。
* 485 /
* AbortReceive/
* ISR + ORE */
uint16_t USART2_GetRxIndex(void) { return uart2_cmd_index; } /* 排障用 */
void USART2_FlushRxBuf(void)
{
if (uart2_cmd_index > 0) {
char hex[3 * 24 + 1] = {0};
uint16_t n = uart2_cmd_index < 24 ? uart2_cmd_index : 24;
for (uint16_t i = 0; i < n; i++) snprintf(hex + i * 3, 4, "%02X ", uart2_cmd_buf[i]);
log_warn("> UART2: 清缓冲丢弃 %d 字节: %s", (int)uart2_cmd_index, hex);
}
uart2_rx_ready = 0;
uart2_cmd_index = 0;
/* 兜底接收中断意外停摆时ORE 后状态卡 BUSY_RX拉起来。不 Abort不动正常状态 */
if (!(USART2->CR1 & USART_CR1_RXNEIE)) {
__HAL_UART_CLEAR_OREFLAG(&huart2);
huart2.ErrorCode = HAL_UART_ERROR_NONE;
huart2.RxState = HAL_UART_STATE_READY;
HAL_UART_Receive_DMA(&huart2, uart2_dma_buf, sizeof(uart2_dma_buf));
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
}
}
/* 从 USART2 接收缓冲区取出一帧20ms 无新字节结束返回帧长度0 表示暂无完整帧 */
uint16_t USART2_GetFrame(uint8_t *out, uint16_t out_size)
{
if (uart2_rx_ready) {
uart2_rx_ready = 0;
uint16_t len = uart2_cmd_index < out_size ? uart2_cmd_index : out_size - 1;
memcpy(out, uart2_cmd_buf, len);
out[len] = '\0';
uart2_cmd_index = 0;
return len;
}
return 0;
}
/* 中断接收回调:把单个字符追加到命令缓冲区,回车换行结束;
* UART1_CMD_TIMEOUT_MS */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1) {
uint8_t ch = uart1_rx_byte;
if (ch == '\r' || ch == '\n') {
if (uart1_cmd_index > 0) {
uart1_cmd_buf[uart1_cmd_index] = '\0';
memcpy(uart1_rx_buf, uart1_cmd_buf, uart1_cmd_index + 1);
uart1_rx_ready = 1;
uart1_cmd_index = 0;
}
} else if (uart1_cmd_index < UART1_RX_BUF_SIZE - 1) {
uart1_cmd_buf[uart1_cmd_index++] = ch;
uart1_last_rx_tick = HAL_GetTick();
}
HAL_UART_Receive_IT(&huart1, &uart1_rx_byte, 1);
}
else if (huart->Instance == USART2) {
/* DMA(NORMAL) 填满 64B垃圾洪泛场景把内容当一帧提交并重启接收 */
HAL_UART_DMAStop(&huart2);
if (!uart2_frame_ready) {
memcpy(uart2_frame_buf, uart2_dma_buf, sizeof(uart2_dma_buf));
uart2_frame_len = sizeof(uart2_dma_buf);
uart2_frame_ready = 1;
}
HAL_UART_Receive_DMA(&huart2, uart2_dma_buf, sizeof(uart2_dma_buf));
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
}
else if (huart->Instance == UART5) {
hlw8032_rx_byte(uart5_rx_byte);
HAL_UART_Receive_IT(&huart5, &uart5_rx_byte, 1);
}
else if (huart->Instance == UART4) {
/* DMA(NORMAL) 填满 Rx_Buf连续数据流没有空闲间隙、IDLE 不触发,
* WiFi OTA */
g_uart4_rxcplt_cnt++;
if (NET_GetActiveBackend() == NET_BACKEND_WIFI &&
(U2_CopyIndex + Rx_Max) < (U2_COPY_SIZE - 1)) {
memcpy(U2_CopyBuff + U2_CopyIndex, Rx_Buf, Rx_Max);
U2_CopyIndex += Rx_Max;
U2_CopyBuff[U2_CopyIndex] = '\0';
U2_CopyFlag = 1;
}
__HAL_UART_CLEAR_IDLEFLAG(&huart4);
HAL_UART_Receive_DMA(&huart4, Rx_Buf, Rx_Max);
__HAL_UART_ENABLE_IT(&huart4, UART_IT_IDLE);
}
else if (huart->Instance == USART3) {
/* 同理4G 侧理论上也可能遇到连续无间隙数据流 */
if (NET_GetActiveBackend() == NET_BACKEND_4G &&
(U2_CopyIndex + Rx_Max) < (U2_COPY_SIZE - 1)) {
memcpy(U2_CopyBuff + U2_CopyIndex, Rx_Buf, Rx_Max);
U2_CopyIndex += Rx_Max;
U2_CopyBuff[U2_CopyIndex] = '\0';
U2_CopyFlag = 1;
}
__HAL_UART_CLEAR_IDLEFLAG(&huart3);
HAL_UART_Receive_DMA(&huart3, Rx_Buf, Rx_Max);
__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
}
}
/* 原子取帧20ms 静默判帧完整 → 直接拷出并清缓冲,一步完成。
* Tick()+GetFrame() /
* 485 */
uint16_t USART2_PollFrame(uint8_t *out, uint16_t out_size)
{
if (uart2_frame_ready) {
/* 排障:帧提交时打印内容 */
char hex[3 * 24 + 1] = {0};
uint16_t n0 = uart2_frame_len < 24 ? uart2_frame_len : 24;
for (uint16_t k = 0; k < n0; k++) snprintf(hex + k * 3, 4, "%02X ", uart2_frame_buf[k]);
log_info("> UART2: 帧提交 %d 字节: %s", (int)uart2_frame_len, hex);
uint16_t len = uart2_frame_len < out_size ? uart2_frame_len : out_size - 1;
memcpy(out, uart2_frame_buf, len);
out[len] = 0;
uart2_frame_ready = 0;
return len;
}
return 0;
}
/* 在 USART2_GetFrame 被轮询前20ms 无新字节自动提交帧 */
void USART2_Tick(void)
{
if (uart2_cmd_index > 0 &&
(HAL_GetTick() - uart2_last_rx_tick) > UART2_CMD_TIMEOUT_MS) {
/* 排障帧提交时打印内容485 丢帧定位) */
char hex[3 * 24 + 1] = {0};
uint16_t n = uart2_cmd_index < 24 ? uart2_cmd_index : 24;
for (uint16_t i = 0; i < n; i++) snprintf(hex + i * 3, 4, "%02X ", uart2_cmd_buf[i]);
log_info("> UART2: 帧提交 %d 字节: %s", (int)uart2_cmd_index, hex);
uart2_rx_ready = 1;
}
}
/* 不区分大小写的字符串比较 */
static int uart_strcasecmp(const char *a, const char *b)
{
while (*a && *b) {
char ca = (char)toupper((unsigned char)*a);
char cb = (char)toupper((unsigned char)*b);
if (ca != cb) return ca - cb;
a++;
b++;
}
return (char)toupper((unsigned char)*a) - (char)toupper((unsigned char)*b);
}
/* 检查 USART1 是否收到指定命令(不区分大小写),检查后清除 ready */
int USART1_CheckCommand(const char *cmd)
{
const char *got = USART1_GetCommand();
if (!got) return 0;
return (uart_strcasecmp(got, cmd) == 0);
}
/* 获取当前接收到的命令字符串(用于自定义解析)。
* \r/\n 100ms */
const char *USART1_GetCommand(void)
{
if (uart1_rx_ready) {
uart1_rx_ready = 0;
return (const char *)uart1_rx_buf;
}
if (uart1_cmd_index > 0 &&
(HAL_GetTick() - uart1_last_rx_tick) > UART1_CMD_TIMEOUT_MS) {
uart1_cmd_buf[uart1_cmd_index] = '\0';
memcpy(uart1_rx_buf, uart1_cmd_buf, uart1_cmd_index + 1);
uart1_cmd_index = 0;
return (const char *)uart1_rx_buf;
}
return NULL;
}

View File

@ -1,52 +0,0 @@
#ifndef __UART_H
#define __UART_H
#include "main.h"
#define UART1_RX_BUF_SIZE 64
#define UART2_RX_BUF_SIZE 64
extern UART_HandleTypeDef huart1;
extern UART_HandleTypeDef huart2; /* RS485 喂料秤 */
extern UART_HandleTypeDef huart3; /* 4G Air780E */
extern UART_HandleTypeDef huart4; /* WiFi ESP-01S (PC10=TX, PC11=RX) */
extern UART_HandleTypeDef huart5; /* HLW8032 计量芯片 (PD2=RX, 只收) */
extern volatile uint8_t uart1_rx_ready;
extern uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE];
extern volatile uint8_t uart2_rx_ready;
extern uint8_t uart2_rx_buf[UART2_RX_BUF_SIZE];
extern volatile uint32_t g_uart4_rxcplt_cnt; /* DMA 填满重启次数(诊断) */
extern volatile uint32_t g_uart4_ore_cnt; /* 溢出错误次数(诊断) */
void MX_USART1_UART_Init(void);
void MX_USART2_UART_Init(void);
void MX_USART3_UART_Init(void);
void MX_UART4_UART_Init(void);
void MX_UART5_UART_Init(void);
void USART3_StartRx(void); /* 启动 USART3 DMA+IDLE 接收 */
void USART3_StopRx(void); /* 停止 USART3 DMA 接收(切 WiFi 通道时调用,释放共享 Rx_Buf */
void USART3_SetBaudRate(uint32_t baud); /* 运行中切换 USART3 波特率 */
void UART4_StartRx(void); /* 启动 UART4 DMA+IDLE 接收(与 USART3 复用 Rx_Buf互斥使用 */
void UART4_StopRx(void); /* 停止 UART4 DMA 接收 */
void UART4_SetBaudRate(uint32_t baud); /* 运行中切换 UART4 波特率 */
void USART1_StartRx(void); /* 启动 USART1 中断接收 */
void USART2_StartRx(void); /* 启动 USART2 中断接收 */
void UART5_StartRx(void); /* 启动 UART5 中断接收HLW8032 */
void USART2_Tick(void); /* 触发 USART2 帧超时自动提交 */
uint16_t USART2_GetFrame(uint8_t *out, uint16_t out_size);
uint16_t USART2_PollFrame(uint8_t *out, uint16_t out_size); /* 原子取帧(判静默+拷出一步完成) */
void USART2_FlushRxBuf(void);
extern volatile uint32_t g_uart2_rx_byte_cnt;
uint16_t USART2_GetRxIndex(void); /* USART2 收字节计数485 排障) */ /* 清 USART2 软件接收缓冲485 查询前调用,不动硬件) */ /* 从 USART2 取一帧原始字节 */
int USART1_CheckCommand(const char *cmd); /* 检查是否收到指定命令(不区分大小写) */
const char *USART1_GetCommand(void); /* 获取当前接收到的命令字符串 */
extern volatile uint32_t g_wp_pc;
extern uint32_t g_wp_ring[16];
extern uint8_t g_wp_ring_idx;
void dbg_watchpoint_uart2_idx(void);
void dbg_wp_rearm(void);
#endif

View File

@ -1,206 +0,0 @@
/* ble_at.c - HLK-B40 蓝牙透传模块 AT 配置
*
* 线 PC5() PB0
* AT PB0 1s 0.5~3s
* AT AT+TS=1
*
*/
#include "ble_at.h"
#include "uart.h"
#include "log.h"
#include "main.h"
#include "system.h"
#include "24c02.h"
#include "stdio.h"
#include "string.h"
#define BLE_PC5_PORT GPIOB
#define BLE_PC5_PIN GPIO_PIN_0
/* BT_RST(PA7)高电平复位模块默认低电平工作system.c 已初始化为推挽低) */
#define BLE_RST_PORT GPIOA
#define BLE_RST_PIN GPIO_PIN_7
#define BLE_RST_HIGH() HAL_GPIO_WritePin(BLE_RST_PORT, BLE_RST_PIN, GPIO_PIN_SET)
#define BLE_RST_LOW() HAL_GPIO_WritePin(BLE_RST_PORT, BLE_RST_PIN, GPIO_PIN_RESET)
#define BLE_PC5_LOW() HAL_GPIO_WritePin(BLE_PC5_PORT, BLE_PC5_PIN, GPIO_PIN_RESET)
#define BLE_PC5_HIGH() HAL_GPIO_WritePin(BLE_PC5_PORT, BLE_PC5_PIN, GPIO_PIN_SET)
/* EEPROM 缓存区已配置好的蓝牙名132~15726B避开其它占用 */
#define BLE_CACHE_ADDR 132
#define BLE_POWER_ADDR 158 /* EEPROM已配置的发射功率132~157 名称缓存, 160 日志等级) */
#define BLE_CACHE_LEN 26
/* PB0 开漏输出:释放时由模块 PC5 内部上拉保持高电平 */
void ble_at_init(void)
{
GPIO_InitTypeDef g = {0};
__HAL_RCC_GPIOB_CLK_ENABLE();
BLE_PC5_HIGH();
g.Pin = BLE_PC5_PIN;
g.Mode = GPIO_MODE_OUTPUT_OD;
g.Pull = GPIO_NOPULL;
g.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(BLE_PC5_PORT, &g);
BLE_PC5_HIGH();
}
static void ble_delay(uint32_t ms)
{
while (ms >= 100) {
HAL_Delay(100);
IWDG_Feed();
ms -= 100;
}
if (ms) HAL_Delay(ms);
}
/* 清空 USART1 待处理行 */
static void ble_flush_rx(void)
{
while (USART1_GetCommand() != NULL) { }
}
/* 发送 AT 命令并等待期望应答按行匹配子串0=成功 */
static int ble_at_cmd(const char *cmd, const char *expect, int timeout_ms)
{
char line[64];
int n = snprintf(line, sizeof(line), "%s\r\n", cmd);
ble_flush_rx();
HAL_UART_Transmit(&huart1, (uint8_t *)line, n, 500);
int t = timeout_ms / 50;
while (t-- > 0) {
ble_delay(50);
const char *resp = USART1_GetCommand();
if (resp) {
if (strstr(resp, expect)) {
log_info("> BLE AT: %s", resp);
return 0;
}
if (strstr(resp, "ERROR")) {
log_warn("> BLE AT 应答错误: %s", resp);
return -1;
}
}
}
log_warn("> BLE AT 超时: %s", cmd);
return -1;
}
/* 上电执行一次:进 AT 模式 → 蓝牙名称查询/设置为 BLE_NAME → 需要时重启模块生效。
* AT APP AT */
void ble_setup_name(void)
{
char cmd[48];
/* EEPROM 缓存命中(名称+功率都对)则跳过整个 AT 流程(零窗口,蓝牙 APP 不断联) */
char cache[BLE_CACHE_LEN] = {0};
eepromReadData(BLE_CACHE_ADDR, cache, BLE_CACHE_LEN);
cache[BLE_CACHE_LEN - 1] = 0;
uint8_t pwr_cache = 0xFF;
eepromReadData(BLE_POWER_ADDR, &pwr_cache, 1);
if (strcmp(cache, BLE_NAME) == 0 && pwr_cache == BLE_TX_POWER) {
log_info("> BLE: 配置已缓存(%s/功率%d),跳过设置", BLE_NAME, BLE_TX_POWER);
return;
}
/* 先硬复位一次模块PA7 拉高 100ms防止模块状态异常时 PC5 进不了 AT 模式。
* */
BLE_RST_HIGH();
ble_delay(100);
BLE_RST_LOW();
ble_delay(1500); /* 等模块重启完成 */
/* AT 窗口内静音日志UART1 与模块共用,日志字节会污染模块的 AT 命令解析 */
log_mute(1);
/* 进 AT 模式并查询当前名称:无应答则重新拉 PC5 再试,最多 3 轮 */
const char *cur = NULL;
for (int round = 0; round < 3 && !cur; round++) {
BLE_PC5_LOW();
ble_delay(1100); /* 拉低 1s 进 AT 模式 */
BLE_PC5_HIGH();
ble_delay(600); /* 等模块切换到 AT 模式 */
ble_flush_rx();
HAL_UART_Transmit(&huart1, (uint8_t *)"AT+NAME=?\r\n", 12, 500);
int t = 30;
while (t-- > 0 && !cur) {
ble_delay(50);
cur = USART1_GetCommand();
}
}
if (!cur) {
log_mute(0);
log_warn("> BLE: 3 轮 AT 均无应答(模块未接/被手机占用/PC5 未拉低)");
return;
}
if (strstr(cur, BLE_NAME) && pwr_cache == BLE_TX_POWER) {
/* 名称与功率都正确:直接回透传,不动任何设置 */
ble_at_cmd("AT+TS=1", "OK", 1500);
log_mute(0);
log_info("> BLE: 名称已是 %s无需设置", BLE_NAME);
return;
}
if (strstr(cur, BLE_NAME)) {
/* 名称对但功率变了:只重设发射功率 */
snprintf(cmd, sizeof(cmd), "AT+RFPOWER=%d", BLE_TX_POWER);
ble_at_cmd(cmd, "OK", 1500);
ble_at_cmd("AT+REBOOT=1", "OK", 1500);
uint8_t p = BLE_TX_POWER;
eepromWriteData(BLE_POWER_ADDR, &p, 1);
log_mute(0);
log_info("> BLE: 发射功率已设置为 %d", BLE_TX_POWER);
return;
}
/* 需要设置:发 AT+NAME最多重试 3 次)。
*
* */
int ok = -1;
for (int i = 0; i < 3 && ok != 0; i++) {
snprintf(cmd, sizeof(cmd), "AT+NAME=%s", BLE_NAME);
ok = ble_at_cmd(cmd, "OK", 1500);
}
if (ok == 0) {
#if BLE_UUID_CUSTOM
/* 顺带设置透传 GATT UUIDFFE0/FFE1通用 BLE 串口 APP 可连 */
snprintf(cmd, sizeof(cmd), "AT+UUIDS=%s", BLE_UUID_SERVICE);
ble_at_cmd(cmd, "OK", 1500);
snprintf(cmd, sizeof(cmd), "AT+UUIDR=%s", BLE_UUID_TX);
ble_at_cmd(cmd, "OK", 1500);
snprintf(cmd, sizeof(cmd), "AT+UUIDW=%s", BLE_UUID_RX);
ble_at_cmd(cmd, "OK", 1500);
#endif
/* 顺带设置发射功率 */
snprintf(cmd, sizeof(cmd), "AT+RFPOWER=%d", BLE_TX_POWER);
ble_at_cmd(cmd, "OK", 1500);
/* 手册说明:设置类参数重启后才生效,重启模块使新名称生效;
* AT+TS=1 */
for (int i = 0; i < 2; i++) {
if (ble_at_cmd("AT+REBOOT=1", "OK", 1500) == 0) break;
}
eepromWriteData(BLE_CACHE_ADDR, (void *)BLE_NAME, strlen(BLE_NAME) + 1);
{
uint8_t p = BLE_TX_POWER;
eepromWriteData(BLE_POWER_ADDR, &p, 1);
}
log_mute(0);
log_info("> BLE: 名称/UUID/功率已设置完成");
} else {
ble_at_cmd("AT+TS=1", "OK", 1500); /* 确保回到透传 */
log_mute(0);
log_warn("> BLE: 设置名称失败(模块未接或应答异常)");
}
}
/* 强制重新执行蓝牙设置(蓝牙 BLESET 命令触发):清缓存后重跑 */
void ble_force_setup(void)
{
eepromWriteData(BLE_CACHE_ADDR, "", 1);
ble_setup_name();
}

View File

@ -1,179 +0,0 @@
/* user_cmd.c - 蓝牙串口调试命令处理
*
* USART1 :
* RESET/OTA -
* RECOVERY - WiFi模块 AT+RESTORE (SmartConfig) +
* INFO - MQTT
* STATUS -
* R1ON ~ R4OFF -
* ALLON / ALLOFF -
* HELP -
*/
#include "user_cmd.h"
#include "ble_at.h"
#include "app_common.h"
#include "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();
}
void userCmdInit(void)
{
/* 无需额外初始化 */
}
/* 处理 USART1 蓝牙调试命令 */
void process_usart1_command(void)
{
const char *cmd = USART1_GetCommand();
if (!cmd) 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_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, "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] ? "" : "");
}
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, "RECOVERY")) {
/* WiFi 模块 AT+RESTORE 恢复出厂(忘掉已保存的热点),用于测试 SmartConfig
* WiFi UART4 4G
* 4G UART4 4G */
if (NET_GetActiveBackend() == NET_BACKEND_WIFI) {
log_warn("> BT RECOVERY: 已安排 WiFi 恢复出厂设置"); g_wifi_restore_req = 1;
} else {
log_warn("> BT RECOVERY: 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, "HELP")) {
log_info("BT CMD: RESET, INFO, STATUS, R1ON/R1OFF~R4ON/R4OFF, ALLON, ALLOFF, OTA, RECOVERY, BLESET, SCALE, SCALE0, SCALE1, GPS, GPS0, GPS1, ENCLR, LOG, LOGTRACE, LOGDEBUG, LOGINFO, LOGWARN, LOGERROR, LOGFATAL, HELP");
}
else {
log_warn("> BT: 未知命令 %s", cmd);
}
}

View File

@ -1,404 +0,0 @@
/* esp8266.c - ESP-01S (AT固件) 驱动
*
* 4WIFI和4G V3.4 WIFI无回弹HARDWARE/WIFI/wifi.c
* HAL USART2 UART4(PC10/PC11) PA5
*
* 4G U2_CopyBuffWiFi UART4 IDLE
* stm32f1xx_it.c UART4_IRQHandler network.c
* StopRx/StartRx
*/
#include "esp8266.h"
#include "air780e.h" /* U2_CopyBuff / bg_delay */
#include "network.h"
#include "uart.h"
#include "log.h"
#include "led.h"
#include "system.h"
#include "stdio.h"
#include "string.h"
#include <stdarg.h>
/*==== 接收缓冲操作 ====*/
void esp_rx_clear(void)
{
U2_CopyIndex = 0;
U2_CopyBuff[0] = 0;
U2_CopyFlag = 0;
}
/* 二进制安全的子串查找strstr 遇 0x00 会失明) */
static uint8_t *esp_memfind(uint8_t *hay, uint16_t hay_len, const char *needle)
{
uint16_t nlen = strlen(needle);
if (hay_len < nlen) return NULL;
for (uint16_t i = 0; i <= hay_len - nlen; i++) {
if (memcmp(hay + i, needle, nlen) == 0) return hay + i;
}
return NULL;
}
/* 等待接收缓冲中出现关键字,期间切片喂狗/跑后台任务。
* "ERROR" 0= */
static int esp_wait(const char *expect, int timeout_ms)
{
int t = timeout_ms / 100;
while (t-- > 0) {
bg_delay(100);
if (U2_CopyFlag) {
if (esp_memfind(U2_CopyBuff, U2_CopyIndex, expect)) return 0;
if (expect[0] != 'E' && esp_memfind(U2_CopyBuff, U2_CopyIndex, "ERROR")) return -1;
}
}
return -1;
}
/* 带倒计时日志的长等待:每秒打印一次剩余秒数,便于观察进度。
* "ERROR"/"FAIL" 0= */
static int esp_wait_countdown(const char *expect, int timeout_s)
{
int remain = timeout_s * 10; /* 100ms 计数 */
int sec = timeout_s;
while (remain-- > 0) {
bg_delay(100);
if (U2_CopyFlag) {
if (esp_memfind(U2_CopyBuff, U2_CopyIndex, expect)) {
log_info("> WiFi: 等待完成 (剩 %ds)", sec);
return 0;
}
if (esp_memfind(U2_CopyBuff, U2_CopyIndex, "ERROR") ||
esp_memfind(U2_CopyBuff, U2_CopyIndex, "FAIL")) {
return -1;
}
}
if (remain % 10 == 0 && sec > 0) {
g_net_ui_countdown = sec; /* OLED 显示用 */
log_info("> WiFi: 倒计时 %ds", sec--);
}
}
return -1;
}
/*==== 透传/AT 数据发送UART4末尾自动加\r\n ====*/
static void ESP_printf(const char *fmt, ...)
{
static uint32_t last_tx_tick = 0;
char buf[256];
va_list ap;
/* 任意两条 AT 指令之间至少间隔 30ms与 4G 侧一致 */
while (HAL_GetTick() - last_tx_tick < 30) {
HAL_Delay(1);
}
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
HAL_UART_Transmit(&huart4, (uint8_t *)buf, strlen(buf), 500);
HAL_UART_Transmit(&huart4, (uint8_t *)"\r\n", 2, 500);
last_tx_tick = HAL_GetTick();
}
/* 透传模式原始数据发送MQTT 二进制报文,不加\r\n */
void esp_send(const uint8_t *data, uint16_t len)
{
HAL_UART_Transmit(&huart4, (uint8_t *)data, len, 1000);
}
/*==== GPIO ====*/
void esp_gpio_init(void)
{
GPIO_InitTypeDef g = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
ESP_RST_HIGH(); /* 默认拉高,模块正常运行 */
g.Pin = ESP_RST_PIN;
g.Mode = GPIO_MODE_OUTPUT_PP;
g.Pull = GPIO_NOPULL;
g.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(ESP_RST_PORT, &g);
ESP_RST_HIGH();
}
/*==== 硬件复位 + 波特率同步 ====
* 500ms 115200 /RESTORE
* AT+UART_DEF WIFI_BAUD_TARGET
* 使 */
int esp_reset(void)
{
log_info("> WiFi: ESP 复位...");
/* 关闭中断/DMA防止模块复位期间乱码触发中断风暴同 4G cat_reset 思路) */
HAL_NVIC_DisableIRQ(UART4_IRQn);
HAL_NVIC_DisableIRQ(DMA2_Channel3_IRQn);
HAL_UART_DMAStop(&huart4);
DMA2_Channel3->CCR &= ~DMA_CCR_EN;
while (DMA2_Channel3->CCR & DMA_CCR_EN);
DMA2->IFCR = DMA_IFCR_CGIF3;
__HAL_UART_CLEAR_IDLEFLAG(&huart4);
__HAL_UART_CLEAR_OREFLAG(&huart4);
__HAL_UART_CLEAR_PEFLAG(&huart4);
__HAL_UART_CLEAR_FEFLAG(&huart4);
__HAL_UART_CLEAR_NEFLAG(&huart4);
esp_rx_clear();
ESP_RST_LOW();
bg_delay(500);
ESP_RST_HIGH();
bg_delay(1500); /* 等模块完成启动boot log 输出完毕) */
/* 复位完成后重新启动 DMA+IDLE 接收 */
esp_rx_clear();
UART4_SetBaudRate(WIFI_BAUD_DEFAULT);
HAL_NVIC_EnableIRQ(DMA2_Channel3_IRQn);
HAL_NVIC_EnableIRQ(UART4_IRQn);
/* 先按 115200 探测 */
if (esp_send_cmd("AT", "OK", 1500) == 0) {
/* 通了固定为目标波特率AT+UART_DEF 掉电保存) */
if (esp_send_cmd("AT+UART_DEF=921600,8,1,0,0", "OK", 3000) == 0) {
UART4_SetBaudRate(WIFI_BAUD_TARGET);
bg_delay(200);
if (esp_send_cmd("AT", "OK", 1500) == 0) {
log_info("> WiFi: 波特率 -> 921600");
return 0;
}
/* 目标波特率通信失败:回滚为 115200维持可用 */
log_warn("> WiFi: 921600 不可用, 回退到 115200");
UART4_SetBaudRate(WIFI_BAUD_DEFAULT);
bg_delay(100);
esp_send_cmd("AT+UART_DEF=115200,8,1,0,0", "OK", 2000);
return 0;
}
/* 固件不支持 UART_DEF 等:维持 115200 也能用 */
log_warn("> WiFi: UART_DEF 失败, 保持 115200");
return 0;
}
/* 115200 不通:模块可能已是目标波特率 */
UART4_SetBaudRate(WIFI_BAUD_TARGET);
if (esp_send_cmd("AT", "OK", 1500) == 0) {
log_info("> WiFi: 波特率已是 921600");
return 0;
}
log_warn("> WiFi: 所有波特率均无 AT 应答");
UART4_SetBaudRate(WIFI_BAUD_DEFAULT);
return -1;
}
/*==== 通用 AT 指令:发送→清缓冲→等关键字 ====*/
/* 接收缓冲十六进制转储诊断用strstr 遇 0x00 会失明,
* */
/* 接收缓冲内容转储(诊断用):按文本打印,不可打印字节显示为 '.' */
static void esp_dump_rx(const char *tag)
{
char text[65];
uint16_t len = U2_CopyIndex < 64 ? U2_CopyIndex : 64;
for (uint16_t i = 0; i < len; i++) {
uint8_t c = U2_CopyBuff[i];
text[i] = (c >= 32 && c <= 126) ? (char)c : '.';
}
text[len] = '\0';
log_warn("> WiFi 接收[%s] idx=%u: %s", tag, U2_CopyIndex, len ? text : "(empty)");
}
/*==== 通用 AT 指令:发送→清缓冲→等关键字 ====
* CWJAP
* "WIFI GOT IP" OK esp_wait
* "假成功" AT+CIPMODE=1 OK
* DMA+IDLE
* Rx_Buf IDLE U2_CopyBuff */
int esp_send_cmd(const char *cmd, const char *expect, int timeout_ms)
{
ESP_printf("%s", cmd);
esp_rx_clear();
if (esp_wait(expect, timeout_ms) != 0) {
log_warn("> WiFi AT 超时: %s", cmd);
esp_dump_rx(cmd);
return -1;
}
return 0;
}
/*==== 查询模块是否已自动连上保存的热点SmartConfig 配过的 AP====
* AT+CIPSTATUSSTATUS:2= IP
* AP 2~3s "WIFI GOT IP"
* */
int esp_saved_ap_connected(int timeout_s)
{
log_info("> WiFi: 检查已保存热点的自动连接 (%ds)", timeout_s);
for (int i = 0; i < timeout_s; i++) {
ESP_printf("AT+CIPSTATUS");
esp_rx_clear();
if (esp_wait("STATUS:2", 1000) == 0) {
log_info("> WiFi: 已连接保存的热点");
return 0;
}
}
log_warn("> WiFi: 无已保存的热点");
return -1;
}
/*==== 连接固定 WiFi代码里保存的 SSID/PASS超时见 WIFI_JOIN_TIMEOUT_S ====*/
int esp_join_fixed_ap(void)
{
char cmd[96];
g_net_ui_phase = NET_UI_WIFI_FIXED;
log_info("> WiFi: 连接固定热点 '%s' (%ds)", WIFI_SSID, WIFI_JOIN_TIMEOUT_S);
/* 保持 CWAUTOCONN=1默认连接成功的AP会被模块保存
* esp_wait_got_ip */
snprintf(cmd, sizeof(cmd), "AT+CWJAP=\"%s\",\"%s\"", WIFI_SSID, WIFI_PASS);
ESP_printf("%s", cmd);
esp_rx_clear();
if (esp_wait_countdown("WIFI GOT IP", WIFI_JOIN_TIMEOUT_S) != 0) {
/* 提前结束 = 模块回了 FAIL/ERROR密码错误等数满倒计时 = 真超时(热点不在) */
log_warn("> WiFi: 固定热点连接失败或超时");
esp_dump_rx("CWJAP");
return -1;
}
log_info("> WiFi: 固定热点已连接");
return 0;
}
/*==== Smart Config (ESP-Touch) 智能配网60s 倒计时 ====
* APP(ESP-Touch) 广 WiFi AP
* AT+CWJAP */
int esp_smartconfig(void)
{
log_info("> WiFi: SmartConfig 开始 (%ds), 请用 ESP-Touch APP", WIFI_SMART_TIMEOUT_S);
g_net_ui_phase = NET_UI_WIFI_SMART;
g_net_led_smartconfig = 1; /* NET_LED 100ms 快闪提示配网模式 */
if (esp_send_cmd("AT+CWSTARTSMART", "OK", 5000) != 0) { g_net_led_smartconfig = 0; return -1; }
esp_rx_clear();
if (esp_wait_countdown("connected", WIFI_SMART_TIMEOUT_S) != 0) {
log_warn("> WiFi: SmartConfig 超时");
esp_send_cmd("AT+CWSTOPSMART", "OK", 2000);
g_net_led_smartconfig = 0;
return -1;
}
log_info("> WiFi: SmartConfig 已获取热点");
esp_send_cmd("AT+CWSTOPSMART", "OK", 2000);
g_net_led_smartconfig = 0;
/* 配网成功后模块自动连接热点。主动查 CIPSTATUS(STATUS:2=已拿到IP)
* "WIFI GOT IP" CWSTOPSMART */
if (esp_saved_ap_connected(10) != 0) {
log_warn("> WiFi: SmartConfig 未获取到 IP");
return -1;
}
log_info("> WiFi: SmartConfig 已连接热点");
return 0;
}
/*==== 退出透传模式OTA 换连接前用)====
* "+++" 1s guard time \r\n
* TCP esp_reset */
int esp_exit_transparent(void)
{
bg_delay(1100);
esp_send((uint8_t *)"+++", 3);
bg_delay(1100);
if (esp_send_cmd("AT", "OK", 2000) != 0) {
log_warn("> WiFi: 退出透传无 AT 应答");
return -1;
}
esp_send_cmd("AT+CIPCLOSE", "OK", 2000);
log_info("> WiFi: 退出透传成功");
return 0;
}
/*==== 恢复出厂设置AT+RESTORE模块忘掉已保存的热点用于测试 SmartConfig====
* 1RESTORE 115200
* UART4 115200
* 2WiFi TCP AT TCP
* "+++" 退 */
int esp_factory_restore(void)
{
log_info("> WiFi: AT+RESTORE (忘记已保存热点)");
esp_exit_transparent(); /* 透传中先退回命令态;非透传时调用无害 */
esp_rx_clear();
ESP_printf("AT+RESTORE");
bg_delay(2500); /* 等模块执行 RESTORE 并完成重启 */
UART4_SetBaudRate(WIFI_BAUD_DEFAULT);
if (esp_send_cmd("AT", "OK", 2000) != 0) {
log_warn("> WiFi: 波特率复位后 RESTORE 无应答");
return -1;
}
log_info("> WiFi: RESTORE 完成, 已保存热点已清除");
return 0;
}
/*==== 连接 TCP 服务器并进入透传模式 ====*/
int esp_enter_transparent(const char *ip, uint16_t port)
{
char cmd[96];
log_info("> WiFi: 进入透传 %s:%u", ip, (unsigned)port);
/* 整体可重试一次CIPSEND 收到 ERROR 多为 CIPMODE 未生效或连接状态残留 */
for (int attempt = 0; attempt < 2; attempt++) {
if (attempt > 0) {
log_warn("> WiFi: 透传重试");
bg_delay(1000);
}
/* 先清掉可能残留的旧 TCP 连接(上一轮失败没关的连接会让
* CIPMODE/CIPMUX "link is builded" ERROR
* CIPCLOSE ERROR */
esp_send_cmd("AT+CIPCLOSE", "OK", 1500);
/* CIPMUX=0 必须在 CIPMODE=1 之前:透传模式只在单连接下可设置 */
if (esp_send_cmd("AT+CIPMUX=0", "OK", 2000) != 0) continue;
if (esp_send_cmd("AT+CIPMODE=1", "OK", 2000) != 0) continue;
snprintf(cmd, sizeof(cmd), "AT+CIPSTART=\"TCP\",\"%s\",%u", ip, (unsigned)port);
ESP_printf("%s", cmd);
esp_rx_clear();
if (esp_wait("CONNECT", WIFI_TCP_TIMEOUT_MS) != 0) {
log_warn("> WiFi: TCP 连接失败");
esp_dump_rx("CIPSTART");
continue;
}
log_info("> WiFi: TCP 已连接, 接收: %.100s", U2_CopyBuff);
/* 等 CIPSTART 的 OK 收尾(先匹配到 CONNECT 时 OK 可能还没回来),
* busy CIPSEND */
esp_wait("OK", 2000);
bg_delay(300);
/* 进透传:等 ">" */
ESP_printf("AT+CIPSEND");
esp_rx_clear();
if (esp_wait(">", 10000) == 0) {
esp_rx_clear();
log_info("> WiFi: 透传模式成功");
return 0;
}
log_warn("> WiFi: CIPSEND 无 '>'");
esp_dump_rx("CIPSEND");
/* 发 AT 探测:有 OK=仍在命令态;无应答=固件已自动进透传 */
ESP_printf("AT");
esp_rx_clear();
if (esp_wait("OK", 2000) != 0) {
log_warn("> WiFi: 无 AT 应答, 假定已进入透传");
esp_rx_clear();
return 0;
}
/* 仍在命令态:查 CIPMODE 实际值做诊断 */
if (esp_send_cmd("AT+CIPMODE?", "+CIPMODE:1", 2000) == 0) {
log_warn("> WiFi: CIPMODE 仍为 1, 连接可能被服务器关闭");
} else {
log_warn("> WiFi: CIPMODE 已还原或从未设置成功");
}
}
log_warn("> WiFi: 进入透传失败");
return -1;
}

View File

@ -1,50 +0,0 @@
#ifndef ESP8266_H
#define ESP8266_H
#include "main.h"
#include <stdint.h>
/*===== 用户配置区 =====*/
/* 固定 WiFi 凭据:优先尝试连接的热点;连不上(60s)则进 Smart Config 智能配网 */
#define WIFI_SSID "OrayBox-6B0E" /* TODO: 填入固定 WiFi 名称 */
#define WIFI_PASS "nky20192006" /* TODO: 填入固定 WiFi 密码 */
//#define WIFI_SSID "TP_Link-01" /* TODO: 填入固定 WiFi 名称 */
//#define WIFI_PASS "zxcvbnmm112" /* TODO: 填入固定 WiFi 密码 */
//#define WIFI_SSID "TP_Link" /* TODO: 填入固定 WiFi 名称 */
//#define WIFI_PASS "zxcvbnm888" /* TODO: 填入固定 WiFi 密码 */
/* 超时配置 */
#define WIFI_JOIN_TIMEOUT_S 40 /* 固定 WiFi 连接超时(秒) */
#define WIFI_SMART_TIMEOUT_S 100 /* Smart Config 配网超时(秒) */
#define WIFI_TCP_TIMEOUT_MS 10000 /* TCP 连接服务器超时 */
/* 波特率配置:模块出厂/RESTORE 后为 115200
* WIFI_BAUD_TARGETAT+UART_DEF
* WiFi OTA 115200 */
#define WIFI_BAUD_DEFAULT 115200u
#define WIFI_BAUD_TARGET 921600u
/*===== ESP-01S 复位脚 (ESP_RST -> PA5) =====*/
#define ESP_RST_PIN GPIO_PIN_5
#define ESP_RST_PORT GPIOA
#define ESP_RST_LOW() HAL_GPIO_WritePin(ESP_RST_PORT, ESP_RST_PIN, GPIO_PIN_RESET)
#define ESP_RST_HIGH() HAL_GPIO_WritePin(ESP_RST_PORT, ESP_RST_PIN, GPIO_PIN_SET)
/*===== API =====*/
void esp_gpio_init(void); /* PA5 推挽输出初始化(默认拉高) */
int esp_reset(void); /* 硬件复位+波特率同步(->921600), 0=成功 */
int esp_send_cmd(const char *cmd, const char *expect, int timeout_ms); /* 发AT等关键字, 0=成功 */
int esp_join_fixed_ap(void); /* 连固定SSID(60s), 0=成功 */
int esp_saved_ap_connected(int timeout_s); /* CIPSTATUS查已存AP自动连接, 0=已连 */
int esp_smartconfig(void); /* ESP-Touch智能配网(60s), 0=成功 */
int esp_enter_transparent(const char *ip, uint16_t port); /* 连TCP并进透传, 0=成功 */
int esp_exit_transparent(void); /* "+++" 退透传+CIPCLOSE, 0=成功 */
int esp_factory_restore(void); /* AT+RESTORE 恢复出厂(忘掉已存AP), 0=成功 */
void esp_send(const uint8_t *data, uint16_t len); /* 透传模式原始数据发送 */
void esp_rx_clear(void); /* 清空共享接收缓冲 */
#endif /* ESP8266_H */

View File

@ -1,138 +0,0 @@
#ifndef __MAIN_H
#define __MAIN_H
#ifdef __cplusplus
extern "C" {
#endif
#include "stm32f1xx_hal.h"
/* USER CODE BEGIN Includes */
#include "air780e.h"
#include "log.h"
/* USER CODE END Includes */
void Error_Handler(void);
/* USER CODE BEGIN Private defines */
#define Rx_Max 8192 /* USART3 DMA 接收缓冲OTA 分块 3KB + 头部/尾部突发约 3.1KB,留足余量 */
extern uint16_t Rx_Len;
extern uint8_t Rx_Buf[Rx_Max];
extern UART_HandleTypeDef huart3;
/* EEPROM配置魔数: 自动检测EEPROM是否已初始化 */
#define EEPROM_MAGIC 0xDEADBEEF// 0xDE123456//0xDEADBEEF
/*===== 产品型号配置 =====
*
* 1 = RS485 feeding onceWt
* /sw1 feeding
* 0 = feeding/sw1 1
*
*/
/*===== 固件版本号(开机横幅打印用,改版本只动这里) =====*/
#define FIRMWARE_VERSION_STR "1.5.11"
#define PRODUCT_WITH_FEED_SCALE 1 /* 默认带秤EEPROM 172 可改,蓝牙 SCALE0/1 切换) */
/*===== GPS 定位配置 =====
* GNSS
* 1 = /GPS 4G Air780EG
* 0 = Air780E GNSS GPS AT
*/
#define PRODUCT_WITH_GPS 1
/*===== MQTT 默认配置 =====*/
////锦鲤塘增氧投料机-02
//#define MQTT_CLIENT_ID "3eb3d1e51846f296"
//#define MQTT_USERNAME "766990788193702677"
//#define MQTT_PASSWORD "a0207984565267a5"
////测试平台的ID - 01
//#define MQTT_CLIENT_ID "4fbaf68ca378b72b"
//#define MQTT_USERNAME "698227263545354343"
//#define MQTT_PASSWORD "b612c2839c135934"
//RTOS版-投料机-无蠕变磅秤-01
#define MQTT_CLIENT_ID "46c080c385c3652e"
#define MQTT_USERNAME "766990788193702677"
#define MQTT_PASSWORD "6ae9622b7034326b"
////RTOS版-投料机-无蠕变磅秤-03
//#define MQTT_CLIENT_ID "5791a8579d459290"
//#define MQTT_USERNAME "766990788193702677"
//#define MQTT_PASSWORD "5f6cd7748ec9db35"
#define MQTT_SERVER_IP "58.17.14.95"
#define MQTT_SERVER_PORT 1880
#define MQTT_TOPIC "/iot/data/down/"
/*===== 蓝牙名称配置 =====
* AT HLK-B40
* 10 */
#define BLE_NAME "NKY-BT-01"
/*===== 蓝牙发射功率AT+RFPOWER =====
* 1~18 18 8 1~3
* */
#define BLE_TX_POWER 1
/*===== HLW8032 电量计量系数UART5 PD2 接收,只上报累计电量) =====
* : ZMPT107-1(2mA:2mA) 220V 100K 49.9R
* -> = (100K/49.9)/1000 = 2.004
* : CT 100A/100mA(1000:1) 2R -> 0.002R
* -> = 1/(0.002*1000) = 0.5
* / HLW8032 */
#define HLW_U_COEF 2.004f
#define HLW_I_COEF 0.5f
/* 电流零点偏移(A):空载时电流通道的零漂读数,显示时减去并钳位到 0。
* / CT */
#define HLW_I_ZERO_A 0.02f
/* 功率零漂处理:空载噪声底约 0.4~2W 且会跳动,单纯减偏移压不住尖峰。
* 0 */
#define HLW_P_DEAD_W 2.0f /* 功率死区(W):低于此值显示 0 */
#define HLW_P_ZERO_W 0.5f /* 功率偏移(W):超过死区后减去 */
/*===== 蓝牙 GATT UUID 配置(透传服务)=====
* BLE_UUID_CUSTOM = 1 UUID
* = 0 UUID(fff0/fff1/fff2) APP
* FFE0/FFE1HM-10/JDY-31 FFE1
* BLE APP Nordic NUS 6e400001/6e400003/6e400002 */
#define BLE_UUID_CUSTOM 1
#define BLE_UUID_SERVICE "0000ffe000001000800000805f9b34fb" /* FFE0 服务 */
#define BLE_UUID_TX "0000ffe100001000800000805f9b34fb" /* FFE1 模块->手机 */
#define BLE_UUID_RX "0000ffe100001000800000805f9b34fb" /* FFE1 手机->模块(同特征双向) */
/* MQTT配置结构体 (存储在AT24C02 EEPROM中) */
typedef struct {
char ClientID[20];
char Username[20];
char Passward[20];
char ServerIP[20];
char Topic[20];
uint16_t ServerPort;
uint8_t Relay_State[7]; /* [1..4]继电器状态, [0]保留 */
uint32_t Magic; /* 魔数, 检测EEPROM是否已初始化 */
uint16_t Ver; /* OTA固件版本号, 断电保持 */
} MqttInfo_Str;
#define MQTT_STRUCT_LEN sizeof(MqttInfo_Str)
/*===== W25Q64 外部 Flash 引脚(软件 SPI =====*/
#define W25Q64_CS_PIN GPIO_PIN_4
#define W25Q64_CS_PORT GPIOB
#define W25Q64_CLK_PIN GPIO_PIN_3
#define W25Q64_CLK_PORT GPIOB
#define W25Q64_DO_PIN GPIO_PIN_5 /* Flash DO -> MCU 输入 */
#define W25Q64_DO_PORT GPIOB
#define W25Q64_DI_PIN GPIO_PIN_15 /* MCU DI -> Flash 输入 */
#define W25Q64_DI_PORT GPIOA
extern MqttInfo_Str MqttInfoStr;
/* USER CODE END Private defines */
#ifdef __cplusplus
}
#endif
#endif /* __MAIN_H */

View File

@ -1,984 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<ProjectOpt xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="project_optx.xsd">
<SchemaVersion>1.0</SchemaVersion>
<Header>### uVision Project, (C) Keil Software</Header>
<Extensions>
<cExt>*.c</cExt>
<aExt>*.s*; *.src; *.a*</aExt>
<oExt>*.obj; *.o</oExt>
<lExt>*.lib</lExt>
<tExt>*.txt; *.h; *.inc</tExt>
<pExt>*.plm</pExt>
<CppX>*.cpp</CppX>
<nMigrate>0</nMigrate>
</Extensions>
<DaveTm>
<dwLowDateTime>0</dwLowDateTime>
<dwHighDateTime>0</dwHighDateTime>
</DaveTm>
<Target>
<TargetName>STM32F103rb_App1</TargetName>
<ToolsetNumber>0x4</ToolsetNumber>
<ToolsetName>ARM-ADS</ToolsetName>
<TargetOption>
<CLKADS>12000000</CLKADS>
<OPTTT>
<gFlags>1</gFlags>
<BeepAtEnd>1</BeepAtEnd>
<RunSim>0</RunSim>
<RunTarget>1</RunTarget>
<RunAbUc>0</RunAbUc>
</OPTTT>
<OPTHX>
<HexSelection>1</HexSelection>
<FlashByte>65535</FlashByte>
<HexRangeLowAddress>0</HexRangeLowAddress>
<HexRangeHighAddress>0</HexRangeHighAddress>
<HexOffset>0</HexOffset>
</OPTHX>
<OPTLEX>
<PageWidth>79</PageWidth>
<PageLength>66</PageLength>
<TabStop>8</TabStop>
<ListingPath></ListingPath>
</OPTLEX>
<ListingPage>
<CreateCListing>1</CreateCListing>
<CreateAListing>1</CreateAListing>
<CreateLListing>1</CreateLListing>
<CreateIListing>0</CreateIListing>
<AsmCond>1</AsmCond>
<AsmSymb>1</AsmSymb>
<AsmXref>0</AsmXref>
<CCond>1</CCond>
<CCode>0</CCode>
<CListInc>0</CListInc>
<CSymb>0</CSymb>
<LinkerCodeListing>0</LinkerCodeListing>
</ListingPage>
<OPTXL>
<LMap>1</LMap>
<LComments>1</LComments>
<LGenerateSymbols>1</LGenerateSymbols>
<LLibSym>1</LLibSym>
<LLines>1</LLines>
<LLocSym>1</LLocSym>
<LPubSym>1</LPubSym>
<LXref>0</LXref>
<LExpSel>0</LExpSel>
</OPTXL>
<OPTFL>
<tvExp>1</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<IsCurrentTarget>1</IsCurrentTarget>
</OPTFL>
<CpuCode>18</CpuCode>
<Books>
<Book>
<Number>0</Number>
<Title>Base Board Schematics (MCBSTM32E)</Title>
<Path>C:\Keil_v5\ARM\PACK\Keil\STM32F1xx_DFP\1.0.5\Documents\mcbstm32e-base-board-schematics.pdf</Path>
</Book>
<Book>
<Number>1</Number>
<Title>Display Board Schematics (MCBSTM32E)</Title>
<Path>C:\Keil_v5\ARM\PACK\Keil\STM32F1xx_DFP\1.0.5\Documents\mcbstm32e-display-board-schematics.pdf</Path>
</Book>
<Book>
<Number>2</Number>
<Title>User Manual (MCBSTM32E)</Title>
<Path>C:\Keil_v5\ARM\PACK\Keil\STM32F1xx_DFP\1.0.5\Documents\mcbstm32e.chm</Path>
</Book>
<Book>
<Number>3</Number>
<Title>MCBSTM32E Evaluation Board Web Page (MCBSTM32E)</Title>
<Path>http://www.keil.com/mcbstm32e/</Path>
</Book>
</Books>
<DebugOpt>
<uSim>0</uSim>
<uTrg>1</uTrg>
<sLdApp>1</sLdApp>
<sGomain>1</sGomain>
<sRbreak>1</sRbreak>
<sRwatch>1</sRwatch>
<sRmem>1</sRmem>
<sRfunc>1</sRfunc>
<sRbox>1</sRbox>
<tLdApp>1</tLdApp>
<tGomain>1</tGomain>
<tRbreak>1</tRbreak>
<tRwatch>1</tRwatch>
<tRmem>1</tRmem>
<tRfunc>1</tRfunc>
<tRbox>1</tRbox>
<tRtrace>1</tRtrace>
<sRSysVw>1</sRSysVw>
<tRSysVw>1</tRSysVw>
<sRunDeb>0</sRunDeb>
<sLrtime>0</sLrtime>
<nTsel>6</nTsel>
<sDll></sDll>
<sDllPa></sDllPa>
<sDlgDll></sDlgDll>
<sDlgPa></sDlgPa>
<sIfile></sIfile>
<tDll></tDll>
<tDllPa></tDllPa>
<tDlgDll></tDlgDll>
<tDlgPa></tDlgPa>
<tIfile></tIfile>
<pMon>Segger\JL2CM3.dll</pMon>
</DebugOpt>
<TargetDriverDllRegistry>
<SetRegEntry>
<Number>0</Number>
<Key>JL2CM3</Key>
<Name>-U20090928 -O78 -S0 -A0 -C0 -JU1 -JI127.0.0.1 -JP0 -RST0 -N00("ARM CoreSight SW-DP") -D00(1BA01477) -L00(0) -TO18 -TC10000000 -TP21 -TDS8007 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -TB1 -TFE0 -FO15 -FD20000000 -FC1000 -FN1 -FF0STM32F10x_512.FLM -FS08000000 -FL080000 -FP0($$Device:STM32F103RC$Flash\STM32F10x_512.FLM)</Name>
</SetRegEntry>
<SetRegEntry>
<Number>0</Number>
<Key>CMSIS_AGDI</Key>
<Name>-X"ATK CMSIS-DAP-Debugger" -UATK-05022018 -O206 -S0 -C0 -P00 -N00("ARM CoreSight SW-DP") -D00(1BA01477) -L00(0) -FO15 -FD20000000 -FC1000 -FN1 -FF0STM32F10x_512 -FS08000000 -FL080000 -FP0($$Device:STM32F103RC$Flash\STM32F10x_512.FLM)</Name>
</SetRegEntry>
<SetRegEntry>
<Number>0</Number>
<Key>ARMRTXEVENTFLAGS</Key>
<Name>-L70 -Z18 -C0 -M0 -T1</Name>
</SetRegEntry>
<SetRegEntry>
<Number>0</Number>
<Key>DLGTARM</Key>
<Name>(1010=-1,-1,-1,-1,0)(1007=-1,-1,-1,-1,0)(1008=-1,-1,-1,-1,0)(1009=-1,-1,-1,-1,0)</Name>
</SetRegEntry>
<SetRegEntry>
<Number>0</Number>
<Key>ARMDBGFLAGS</Key>
<Name></Name>
</SetRegEntry>
<SetRegEntry>
<Number>0</Number>
<Key>DLGUARM</Key>
<Name>(105=-1,-1,-1,-1,0)</Name>
</SetRegEntry>
<SetRegEntry>
<Number>0</Number>
<Key>UL2CM3</Key>
<Name>UL2CM3(-S0 -C0 -P0 ) -FN1 -FC1000 -FD20000000 -FF0STM32F10x_512 -FL080000 -FS08000000 -FP0($$Device:STM32F103RC$Flash\STM32F10x_512.FLM)</Name>
</SetRegEntry>
<SetRegEntry>
<Number>0</Number>
<Key>ST-LINKIII-KEIL_SWO</Key>
<Name>-U066CFF373436424E43133952 -O2254 -SF4000 -C0 -A0 -I0 -HNlocalhost -HP7184 -P1 -N00("ARM CoreSight SW-DP") -D00(1BA01477) -L00(0) -TO18 -TC10000000 -TP21 -TDS8004 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -FO15 -FD20000000 -FC800 -FN1 -FF0STM32F10x_128 -FS08000000 -FL020000</Name>
</SetRegEntry>
</TargetDriverDllRegistry>
<Breakpoint/>
<WatchWindow1>
<Ww>
<count>0</count>
<WinNumber>1</WinNumber>
<ItemText>Rx_Buf</ItemText>
</Ww>
<Ww>
<count>1</count>
<WinNumber>1</WinNumber>
<ItemText>temp_buf</ItemText>
</Ww>
</WatchWindow1>
<MemoryWindow2>
<Mm>
<WinNumber>2</WinNumber>
<SubType>0</SubType>
<ItemText>0x0800F000</ItemText>
<AccSizeX>1</AccSizeX>
</Mm>
</MemoryWindow2>
<Tracepoint>
<THDelay>0</THDelay>
</Tracepoint>
<DebugFlag>
<trace>0</trace>
<periodic>1</periodic>
<aLwin>0</aLwin>
<aCover>0</aCover>
<aSer1>0</aSer1>
<aSer2>0</aSer2>
<aPa>0</aPa>
<viewmode>1</viewmode>
<vrSel>0</vrSel>
<aSym>0</aSym>
<aTbox>0</aTbox>
<AscS1>0</AscS1>
<AscS2>0</AscS2>
<AscS3>0</AscS3>
<aSer3>0</aSer3>
<eProf>0</eProf>
<aLa>0</aLa>
<aPa1>0</aPa1>
<AscS4>0</AscS4>
<aSer4>0</aSer4>
<StkLoc>0</StkLoc>
<TrcWin>0</TrcWin>
<newCpu>0</newCpu>
<uProt>0</uProt>
</DebugFlag>
<LintExecutable></LintExecutable>
<LintConfigFile></LintConfigFile>
<bLintAuto>0</bLintAuto>
</TargetOption>
</Target>
<Group>
<GroupName>Application/MDK-ARM</GroupName>
<tvExp>1</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel>
<RteFlg>0</RteFlg>
<File>
<GroupNumber>1</GroupNumber>
<FileNumber>1</FileNumber>
<FileType>2</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>.\RTE\Device\STM32F103RC\startup_stm32f10x_hd.s</PathWithFileName>
<FilenameWithoutPath>startup_stm32f10x_hd.s</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
</Group>
<Group>
<GroupName>Application/User</GroupName>
<tvExp>1</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel>
<RteFlg>0</RteFlg>
<File>
<GroupNumber>2</GroupNumber>
<FileNumber>2</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Src/main.c</PathWithFileName>
<FilenameWithoutPath>main.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>2</GroupNumber>
<FileNumber>3</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Src/stm32f1xx_it.c</PathWithFileName>
<FilenameWithoutPath>stm32f1xx_it.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>2</GroupNumber>
<FileNumber>4</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Src/stm32f1xx_hal_msp.c</PathWithFileName>
<FilenameWithoutPath>stm32f1xx_hal_msp.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>2</GroupNumber>
<FileNumber>5</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Src/stm32f1xx_hal_timebase_tim.c</PathWithFileName>
<FilenameWithoutPath>stm32f1xx_hal_timebase_tim.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
</Group>
<Group>
<GroupName>Middlewares/FreeRTOS</GroupName>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel>
<RteFlg>0</RteFlg>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>6</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Middlewares/FreeRTOS/Source/tasks.c</PathWithFileName>
<FilenameWithoutPath>tasks.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>7</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Middlewares/FreeRTOS/Source/queue.c</PathWithFileName>
<FilenameWithoutPath>queue.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>8</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Middlewares/FreeRTOS/Source/list.c</PathWithFileName>
<FilenameWithoutPath>list.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>9</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Middlewares/FreeRTOS/Source/portable/RVDS/ARM_CM3/port.c</PathWithFileName>
<FilenameWithoutPath>port.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>3</GroupNumber>
<FileNumber>10</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Middlewares/FreeRTOS/Source/portable/MemMang/heap_4.c</PathWithFileName>
<FilenameWithoutPath>heap_4.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
</Group>
<Group>
<GroupName>Drivers/STM32F1xx_HAL_Driver</GroupName>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel>
<RteFlg>0</RteFlg>
<File>
<GroupNumber>4</GroupNumber>
<FileNumber>11</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_gpio_ex.c</PathWithFileName>
<FilenameWithoutPath>stm32f1xx_hal_gpio_ex.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>4</GroupNumber>
<FileNumber>12</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_tim.c</PathWithFileName>
<FilenameWithoutPath>stm32f1xx_hal_tim.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>4</GroupNumber>
<FileNumber>13</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_tim_ex.c</PathWithFileName>
<FilenameWithoutPath>stm32f1xx_hal_tim_ex.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>4</GroupNumber>
<FileNumber>14</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_uart.c</PathWithFileName>
<FilenameWithoutPath>stm32f1xx_hal_uart.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>4</GroupNumber>
<FileNumber>15</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal.c</PathWithFileName>
<FilenameWithoutPath>stm32f1xx_hal.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>4</GroupNumber>
<FileNumber>16</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_rcc.c</PathWithFileName>
<FilenameWithoutPath>stm32f1xx_hal_rcc.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>4</GroupNumber>
<FileNumber>17</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_rcc_ex.c</PathWithFileName>
<FilenameWithoutPath>stm32f1xx_hal_rcc_ex.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>4</GroupNumber>
<FileNumber>18</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_gpio.c</PathWithFileName>
<FilenameWithoutPath>stm32f1xx_hal_gpio.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>4</GroupNumber>
<FileNumber>19</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_dma.c</PathWithFileName>
<FilenameWithoutPath>stm32f1xx_hal_dma.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>4</GroupNumber>
<FileNumber>20</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_cortex.c</PathWithFileName>
<FilenameWithoutPath>stm32f1xx_hal_cortex.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>4</GroupNumber>
<FileNumber>21</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_pwr.c</PathWithFileName>
<FilenameWithoutPath>stm32f1xx_hal_pwr.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>4</GroupNumber>
<FileNumber>22</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_flash.c</PathWithFileName>
<FilenameWithoutPath>stm32f1xx_hal_flash.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>4</GroupNumber>
<FileNumber>23</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_flash_ex.c</PathWithFileName>
<FilenameWithoutPath>stm32f1xx_hal_flash_ex.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>4</GroupNumber>
<FileNumber>24</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_exti.c</PathWithFileName>
<FilenameWithoutPath>stm32f1xx_hal_exti.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>4</GroupNumber>
<FileNumber>25</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_iwdg.c</PathWithFileName>
<FilenameWithoutPath>stm32f1xx_hal_iwdg.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>4</GroupNumber>
<FileNumber>26</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_adc.c</PathWithFileName>
<FilenameWithoutPath>stm32f1xx_hal_adc.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
</Group>
<Group>
<GroupName>Drivers/CMSIS</GroupName>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel>
<RteFlg>0</RteFlg>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>27</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>../Src/system_stm32f1xx.c</PathWithFileName>
<FilenameWithoutPath>system_stm32f1xx.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
</Group>
<Group>
<GroupName>m_app</GroupName>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel>
<RteFlg>0</RteFlg>
<File>
<GroupNumber>6</GroupNumber>
<FileNumber>28</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\m_app\proto.c</PathWithFileName>
<FilenameWithoutPath>proto.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
</Group>
<Group>
<GroupName>HardWare</GroupName>
<tvExp>1</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel>
<RteFlg>0</RteFlg>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>29</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\CAR\air780e.c</PathWithFileName>
<FilenameWithoutPath>air780e.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>30</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\NETWORK\network.c</PathWithFileName>
<FilenameWithoutPath>network.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>31</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\NETWORK\net_wifi.c</PathWithFileName>
<FilenameWithoutPath>net_wifi.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>32</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\NETWORK\net_wifi_ota.c</PathWithFileName>
<FilenameWithoutPath>net_wifi_ota.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>33</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\WIFI\esp8266.c</PathWithFileName>
<FilenameWithoutPath>esp8266.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>34</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\WIFI\mqtt_client.c</PathWithFileName>
<FilenameWithoutPath>mqtt_client.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>35</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\RELAY\relay.c</PathWithFileName>
<FilenameWithoutPath>relay.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>36</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\LOG\log.c</PathWithFileName>
<FilenameWithoutPath>log.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>37</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\LOG\log_cn.c</PathWithFileName>
<FilenameWithoutPath>log_cn.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>38</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\24C02\24c02.c</PathWithFileName>
<FilenameWithoutPath>24c02.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>39</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\IIC\iic.c</PathWithFileName>
<FilenameWithoutPath>iic.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>40</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\ADC\adc.c</PathWithFileName>
<FilenameWithoutPath>adc.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>41</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\LED\led.c</PathWithFileName>
<FilenameWithoutPath>led.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>42</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\UART\uart.c</PathWithFileName>
<FilenameWithoutPath>uart.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>43</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\FEED_SCALE\feed_scale.c</PathWithFileName>
<FilenameWithoutPath>feed_scale.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>44</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\USER_CMD\user_cmd.c</PathWithFileName>
<FilenameWithoutPath>user_cmd.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>45</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\USER_CMD\ble_at.c</PathWithFileName>
<FilenameWithoutPath>ble_at.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>46</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\SYSINIT\system.c</PathWithFileName>
<FilenameWithoutPath>system.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>47</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\SYSINIT\reset_log.c</PathWithFileName>
<FilenameWithoutPath>reset_log.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>48</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\SYSINIT\app_loop.c</PathWithFileName>
<FilenameWithoutPath>app_loop.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>49</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\W25Q64\w25q64.c</PathWithFileName>
<FilenameWithoutPath>w25q64.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>50</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\HLW8032\hlw8032.c</PathWithFileName>
<FilenameWithoutPath>hlw8032.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>7</GroupNumber>
<FileNumber>51</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\HardWare\OLED\oled.c</PathWithFileName>
<FilenameWithoutPath>oled.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
</Group>
<Group>
<GroupName>Doc</GroupName>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel>
<RteFlg>0</RteFlg>
<File>
<GroupNumber>8</GroupNumber>
<FileNumber>52</FileNumber>
<FileType>5</FileType>
<tvExp>0</tvExp>
<Focus>0</Focus>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\README.md</PathWithFileName>
<FilenameWithoutPath>README.md</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
</Group>
<Group>
<GroupName>::CMSIS</GroupName>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel>
<RteFlg>1</RteFlg>
</Group>
</ProjectOpt>

View File

@ -1,751 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<Project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="project_projx.xsd">
<SchemaVersion>2.1</SchemaVersion>
<Header>### uVision Project, (C) Keil Software</Header>
<Targets>
<Target>
<TargetName>STM32F103rb_App1</TargetName>
<ToolsetNumber>0x4</ToolsetNumber>
<ToolsetName>ARM-ADS</ToolsetName>
<TargetOption>
<TargetCommonOption>
<Device>STM32F103RC</Device>
<Vendor>STMicroelectronics</Vendor>
<PackID>Keil.STM32F1xx_DFP.1.0.5</PackID>
<PackURL>http://www.keil.com/pack/</PackURL>
<Cpu>IROM(0x08008000,0x38000) IRAM(0x20000000,0xC000) CPUTYPE("Cortex-M3") CLOCK(12000000) ELITTLE</Cpu>
<FlashUtilSpec></FlashUtilSpec>
<StartupFile></StartupFile>
<FlashDriverDll>UL2CM3(-S0 -C0 -P0 -FD20000000 -FC1000 -FN1 -FF0STM32F10x_512 -FS08000000 -FL080000 -FP0($$Device:STM32F103RC$Flash\STM32F10x_512.FLM))</FlashDriverDll>
<DeviceId>0</DeviceId>
<RegisterFile>$$Device:STM32F103RC$Device\Include\stm32f10x.h</RegisterFile>
<MemoryEnv></MemoryEnv>
<Cmp></Cmp>
<Asm></Asm>
<Linker></Linker>
<OHString></OHString>
<InfinionOptionDll></InfinionOptionDll>
<SLE66CMisc></SLE66CMisc>
<SLE66AMisc></SLE66AMisc>
<SLE66LinkerMisc></SLE66LinkerMisc>
<SFDFile>$$Device:STM32F103RC$SVD\STM32F103xx.svd</SFDFile>
<bCustSvd>0</bCustSvd>
<UseEnv>0</UseEnv>
<BinPath></BinPath>
<IncludePath></IncludePath>
<LibPath></LibPath>
<RegisterFilePath></RegisterFilePath>
<DBRegisterFilePath></DBRegisterFilePath>
<TargetStatus>
<Error>0</Error>
<ExitCodeStop>0</ExitCodeStop>
<ButtonStop>0</ButtonStop>
<NotGenerated>0</NotGenerated>
<InvalidFlash>1</InvalidFlash>
</TargetStatus>
<OutputDirectory>..\Object\</OutputDirectory>
<OutputName>STM32F103rb_App1</OutputName>
<CreateExecutable>1</CreateExecutable>
<CreateLib>0</CreateLib>
<CreateHexFile>1</CreateHexFile>
<DebugInformation>1</DebugInformation>
<BrowseInformation>1</BrowseInformation>
<ListingPath></ListingPath>
<HexFormatSelection>1</HexFormatSelection>
<Merge32K>0</Merge32K>
<CreateBatchFile>0</CreateBatchFile>
<BeforeCompile>
<RunUserProg1>0</RunUserProg1>
<RunUserProg2>1</RunUserProg2>
<UserProg1Name></UserProg1Name>
<UserProg2Name></UserProg2Name>
<UserProg1Dos16Mode>0</UserProg1Dos16Mode>
<UserProg2Dos16Mode>0</UserProg2Dos16Mode>
<nStopU1X>0</nStopU1X>
<nStopU2X>0</nStopU2X>
</BeforeCompile>
<BeforeMake>
<RunUserProg1>0</RunUserProg1>
<RunUserProg2>1</RunUserProg2>
<UserProg1Name></UserProg1Name>
<UserProg2Name></UserProg2Name>
<UserProg1Dos16Mode>0</UserProg1Dos16Mode>
<UserProg2Dos16Mode>0</UserProg2Dos16Mode>
<nStopB1X>0</nStopB1X>
<nStopB2X>0</nStopB2X>
</BeforeMake>
<AfterMake>
<RunUserProg1>1</RunUserProg1>
<RunUserProg2>1</RunUserProg2>
<UserProg1Name>cmd /c if not exist ..\OutputBin mkdir ..\OutputBin</UserProg1Name>
<UserProg2Name>C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin --output ..\OutputBin\STM32F103rb_App1.bin ..\Object\STM32F103rb_App1.axf</UserProg2Name>
<UserProg1Dos16Mode>0</UserProg1Dos16Mode>
<UserProg2Dos16Mode>0</UserProg2Dos16Mode>
<nStopA1X>0</nStopA1X>
<nStopA2X>0</nStopA2X>
</AfterMake>
<SelectedForBatchBuild>0</SelectedForBatchBuild>
<SVCSIdString></SVCSIdString>
</TargetCommonOption>
<CommonProperty>
<UseCPPCompiler>0</UseCPPCompiler>
<RVCTCodeConst>0</RVCTCodeConst>
<RVCTZI>0</RVCTZI>
<RVCTOtherData>0</RVCTOtherData>
<ModuleSelection>0</ModuleSelection>
<IncludeInBuild>1</IncludeInBuild>
<AlwaysBuild>0</AlwaysBuild>
<GenerateAssemblyFile>0</GenerateAssemblyFile>
<AssembleAssemblyFile>0</AssembleAssemblyFile>
<PublicsOnly>0</PublicsOnly>
<StopOnExitCode>3</StopOnExitCode>
<CustomArgument></CustomArgument>
<IncludeLibraryModules></IncludeLibraryModules>
<ComprImg>0</ComprImg>
</CommonProperty>
<DllOption>
<SimDllName>SARMCM3.DLL</SimDllName>
<SimDllArguments> -REMAP</SimDllArguments>
<SimDlgDll>DCM.DLL</SimDlgDll>
<SimDlgDllArguments>-pCM3</SimDlgDllArguments>
<TargetDllName>SARMCM3.DLL</TargetDllName>
<TargetDllArguments></TargetDllArguments>
<TargetDlgDll>TCM.DLL</TargetDlgDll>
<TargetDlgDllArguments>-pCM3</TargetDlgDllArguments>
</DllOption>
<DebugOption>
<OPTHX>
<HexSelection>1</HexSelection>
<HexRangeLowAddress>0</HexRangeLowAddress>
<HexRangeHighAddress>0</HexRangeHighAddress>
<HexOffset>0</HexOffset>
<Oh166RecLen>16</Oh166RecLen>
</OPTHX>
<Simulator>
<UseSimulator>0</UseSimulator>
<LoadApplicationAtStartup>1</LoadApplicationAtStartup>
<RunToMain>1</RunToMain>
<RestoreBreakpoints>1</RestoreBreakpoints>
<RestoreWatchpoints>1</RestoreWatchpoints>
<RestoreMemoryDisplay>1</RestoreMemoryDisplay>
<RestoreFunctions>1</RestoreFunctions>
<RestoreToolbox>1</RestoreToolbox>
<LimitSpeedToRealTime>0</LimitSpeedToRealTime>
<RestoreSysVw>1</RestoreSysVw>
</Simulator>
<Target>
<UseTarget>1</UseTarget>
<LoadApplicationAtStartup>1</LoadApplicationAtStartup>
<RunToMain>1</RunToMain>
<RestoreBreakpoints>1</RestoreBreakpoints>
<RestoreWatchpoints>1</RestoreWatchpoints>
<RestoreMemoryDisplay>1</RestoreMemoryDisplay>
<RestoreFunctions>1</RestoreFunctions>
<RestoreToolbox>1</RestoreToolbox>
<RestoreTracepoints>1</RestoreTracepoints>
<RestoreSysVw>1</RestoreSysVw>
</Target>
<RunDebugAfterBuild>0</RunDebugAfterBuild>
<TargetSelection>6</TargetSelection>
<SimDlls>
<CpuDll></CpuDll>
<CpuDllArguments></CpuDllArguments>
<PeripheralDll></PeripheralDll>
<PeripheralDllArguments></PeripheralDllArguments>
<InitializationFile></InitializationFile>
</SimDlls>
<TargetDlls>
<CpuDll></CpuDll>
<CpuDllArguments></CpuDllArguments>
<PeripheralDll></PeripheralDll>
<PeripheralDllArguments></PeripheralDllArguments>
<InitializationFile></InitializationFile>
<Driver>Segger\JL2CM3.dll</Driver>
</TargetDlls>
</DebugOption>
<Utilities>
<Flash1>
<UseTargetDll>1</UseTargetDll>
<UseExternalTool>0</UseExternalTool>
<RunIndependent>0</RunIndependent>
<UpdateFlashBeforeDebugging>1</UpdateFlashBeforeDebugging>
<Capability>1</Capability>
<DriverSelection>4096</DriverSelection>
</Flash1>
<bUseTDR>1</bUseTDR>
<Flash2>BIN\UL2CM3.DLL</Flash2>
<Flash3>"" ()</Flash3>
<Flash4></Flash4>
<pFcarmOut></pFcarmOut>
<pFcarmGrp></pFcarmGrp>
<pFcArmRoot></pFcArmRoot>
<FcArmLst>0</FcArmLst>
</Utilities>
<TargetArmAds>
<ArmAdsMisc>
<GenerateListings>0</GenerateListings>
<asHll>1</asHll>
<asAsm>1</asAsm>
<asMacX>1</asMacX>
<asSyms>1</asSyms>
<asFals>1</asFals>
<asDbgD>1</asDbgD>
<asForm>1</asForm>
<ldLst>0</ldLst>
<ldmm>1</ldmm>
<ldXref>1</ldXref>
<BigEnd>0</BigEnd>
<AdsALst>1</AdsALst>
<AdsACrf>1</AdsACrf>
<AdsANop>0</AdsANop>
<AdsANot>0</AdsANot>
<AdsLLst>1</AdsLLst>
<AdsLmap>1</AdsLmap>
<AdsLcgr>1</AdsLcgr>
<AdsLsym>1</AdsLsym>
<AdsLszi>1</AdsLszi>
<AdsLtoi>1</AdsLtoi>
<AdsLsun>1</AdsLsun>
<AdsLven>1</AdsLven>
<AdsLsxf>1</AdsLsxf>
<RvctClst>0</RvctClst>
<GenPPlst>0</GenPPlst>
<AdsCpuType>"Cortex-M3"</AdsCpuType>
<RvctDeviceName></RvctDeviceName>
<mOS>0</mOS>
<uocRom>0</uocRom>
<uocRam>0</uocRam>
<hadIROM>1</hadIROM>
<hadIRAM>1</hadIRAM>
<hadXRAM>0</hadXRAM>
<uocXRam>0</uocXRam>
<RvdsVP>0</RvdsVP>
<hadIRAM2>0</hadIRAM2>
<hadIROM2>0</hadIROM2>
<StupSel>8</StupSel>
<useUlib>0</useUlib>
<EndSel>0</EndSel>
<uLtcg>0</uLtcg>
<RoSelD>3</RoSelD>
<RwSelD>3</RwSelD>
<CodeSel>0</CodeSel>
<OptFeed>0</OptFeed>
<NoZi1>0</NoZi1>
<NoZi2>0</NoZi2>
<NoZi3>0</NoZi3>
<NoZi4>0</NoZi4>
<NoZi5>0</NoZi5>
<Ro1Chk>0</Ro1Chk>
<Ro2Chk>0</Ro2Chk>
<Ro3Chk>0</Ro3Chk>
<Ir1Chk>1</Ir1Chk>
<Ir2Chk>0</Ir2Chk>
<Ra1Chk>0</Ra1Chk>
<Ra2Chk>0</Ra2Chk>
<Ra3Chk>0</Ra3Chk>
<Im1Chk>1</Im1Chk>
<Im2Chk>0</Im2Chk>
<OnChipMemories>
<Ocm1>
<Type>0</Type>
<StartAddress>0x0</StartAddress>
<Size>0x0</Size>
</Ocm1>
<Ocm2>
<Type>0</Type>
<StartAddress>0x0</StartAddress>
<Size>0x0</Size>
</Ocm2>
<Ocm3>
<Type>0</Type>
<StartAddress>0x0</StartAddress>
<Size>0x0</Size>
</Ocm3>
<Ocm4>
<Type>0</Type>
<StartAddress>0x0</StartAddress>
<Size>0x0</Size>
</Ocm4>
<Ocm5>
<Type>0</Type>
<StartAddress>0x0</StartAddress>
<Size>0x0</Size>
</Ocm5>
<Ocm6>
<Type>0</Type>
<StartAddress>0x0</StartAddress>
<Size>0x0</Size>
</Ocm6>
<IRAM>
<Type>0</Type>
<StartAddress>0x20000000</StartAddress>
<Size>0xc000</Size>
</IRAM>
<IROM>
<Type>1</Type>
<StartAddress>0x8008000</StartAddress>
<Size>0x38000</Size>
</IROM>
<XRAM>
<Type>0</Type>
<StartAddress>0x0</StartAddress>
<Size>0x0</Size>
</XRAM>
<OCR_RVCT1>
<Type>1</Type>
<StartAddress>0x0</StartAddress>
<Size>0x0</Size>
</OCR_RVCT1>
<OCR_RVCT2>
<Type>1</Type>
<StartAddress>0x0</StartAddress>
<Size>0x0</Size>
</OCR_RVCT2>
<OCR_RVCT3>
<Type>1</Type>
<StartAddress>0x0</StartAddress>
<Size>0x0</Size>
</OCR_RVCT3>
<OCR_RVCT4>
<Type>1</Type>
<StartAddress>0x8008000</StartAddress>
<Size>0x1c000</Size>
</OCR_RVCT4>
<OCR_RVCT5>
<Type>1</Type>
<StartAddress>0x0</StartAddress>
<Size>0x0</Size>
</OCR_RVCT5>
<OCR_RVCT6>
<Type>0</Type>
<StartAddress>0x0</StartAddress>
<Size>0x0</Size>
</OCR_RVCT6>
<OCR_RVCT7>
<Type>0</Type>
<StartAddress>0x0</StartAddress>
<Size>0x0</Size>
</OCR_RVCT7>
<OCR_RVCT8>
<Type>0</Type>
<StartAddress>0x0</StartAddress>
<Size>0x0</Size>
</OCR_RVCT8>
<OCR_RVCT9>
<Type>0</Type>
<StartAddress>0x20000000</StartAddress>
<Size>0xc000</Size>
</OCR_RVCT9>
<OCR_RVCT10>
<Type>0</Type>
<StartAddress>0x0</StartAddress>
<Size>0x0</Size>
</OCR_RVCT10>
</OnChipMemories>
<RvctStartVector></RvctStartVector>
</ArmAdsMisc>
<Cads>
<interw>1</interw>
<Optim>4</Optim>
<oTime>0</oTime>
<SplitLS>0</SplitLS>
<OneElfS>1</OneElfS>
<Strict>0</Strict>
<EnumInt>0</EnumInt>
<PlainCh>0</PlainCh>
<Ropi>0</Ropi>
<Rwpi>0</Rwpi>
<wLevel>2</wLevel>
<uThumb>0</uThumb>
<uSurpInc>0</uSurpInc>
<uC99>1</uC99>
<useXO>0</useXO>
<v6Lang>1</v6Lang>
<v6LangP>1</v6LangP>
<VariousControls>
<MiscControls>--no_multibyte_chars</MiscControls>
<Define>USE_HAL_DRIVER,STM32F103xE</Define>
<Undefine></Undefine>
<IncludePath>../Inc;../Drivers/STM32F1xx_HAL_Driver/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy;../Drivers/CMSIS/Device/ST/STM32F1xx/Include;../Drivers/CMSIS/Include;..\m_app;..\HardWare\CAR;..\HardWare\RELAY;..\HardWare\LOG;..\HardWare\IIC;..\HardWare\24C02;..\HardWare\ADC;..\HardWare\LED;..\HardWare\SYSINIT;..\HardWare\UART;..\HardWare\W25Q64;..\HardWare\NETWORK;..\HardWare\WIFI;..\HardWare\MQTT;..\HardWare\FEED_SCALE;..\HardWare\USER_CMD;..\HardWare\HLW8032;..\HardWare\OLED;..\Middlewares\FreeRTOS\Source\include;..\Middlewares\FreeRTOS\Source\portable\RVDS\ARM_CM3</IncludePath>
</VariousControls>
</Cads>
<Aads>
<interw>1</interw>
<Ropi>0</Ropi>
<Rwpi>0</Rwpi>
<thumb>0</thumb>
<SplitLS>0</SplitLS>
<SwStkChk>0</SwStkChk>
<NoWarn>0</NoWarn>
<uSurpInc>0</uSurpInc>
<useXO>0</useXO>
<VariousControls>
<MiscControls></MiscControls>
<Define></Define>
<Undefine></Undefine>
<IncludePath></IncludePath>
</VariousControls>
</Aads>
<LDads>
<umfTarg>1</umfTarg>
<Ropi>0</Ropi>
<Rwpi>0</Rwpi>
<noStLib>0</noStLib>
<RepFail>1</RepFail>
<useFile>0</useFile>
<TextAddressRange>0x08005000</TextAddressRange>
<DataAddressRange>0x20000000</DataAddressRange>
<pXoBase></pXoBase>
<ScatterFile>STM32F103rb_App1\STM32F103rb_App1.sct</ScatterFile>
<IncludeLibs></IncludeLibs>
<IncludeLibsPath></IncludeLibsPath>
<Misc></Misc>
<LinkerInputFile></LinkerInputFile>
<DisabledWarnings></DisabledWarnings>
</LDads>
</TargetArmAds>
</TargetOption>
<Groups>
<Group>
<GroupName>Application/MDK-ARM</GroupName>
<Files>
<File>
<FileName>startup_stm32f10x_hd.s</FileName>
<FileType>2</FileType>
<FilePath>.\RTE\Device\STM32F103RC\startup_stm32f10x_hd.s</FilePath>
</File>
</Files>
</Group>
<Group>
<GroupName>Application/User</GroupName>
<Files>
<File>
<FileName>main.c</FileName>
<FileType>1</FileType>
<FilePath>../Src/main.c</FilePath>
</File>
<File>
<FileName>stm32f1xx_it.c</FileName>
<FileType>1</FileType>
<FilePath>../Src/stm32f1xx_it.c</FilePath>
</File>
<File>
<FileName>stm32f1xx_hal_msp.c</FileName>
<FileType>1</FileType>
<FilePath>../Src/stm32f1xx_hal_msp.c</FilePath>
</File>
<File>
<FileName>stm32f1xx_hal_timebase_tim.c</FileName>
<FileType>1</FileType>
<FilePath>../Src/stm32f1xx_hal_timebase_tim.c</FilePath>
</File>
</Files>
</Group>
<Group>
<GroupName>Middlewares/FreeRTOS</GroupName>
<Files>
<File>
<FileName>tasks.c</FileName>
<FileType>1</FileType>
<FilePath>../Middlewares/FreeRTOS/Source/tasks.c</FilePath>
</File>
<File>
<FileName>queue.c</FileName>
<FileType>1</FileType>
<FilePath>../Middlewares/FreeRTOS/Source/queue.c</FilePath>
</File>
<File>
<FileName>list.c</FileName>
<FileType>1</FileType>
<FilePath>../Middlewares/FreeRTOS/Source/list.c</FilePath>
</File>
<File>
<FileName>port.c</FileName>
<FileType>1</FileType>
<FilePath>../Middlewares/FreeRTOS/Source/portable/RVDS/ARM_CM3/port.c</FilePath>
</File>
<File>
<FileName>heap_4.c</FileName>
<FileType>1</FileType>
<FilePath>../Middlewares/FreeRTOS/Source/portable/MemMang/heap_4.c</FilePath>
</File>
</Files>
</Group>
<Group>
<GroupName>Drivers/STM32F1xx_HAL_Driver</GroupName>
<Files>
<File>
<FileName>stm32f1xx_hal_gpio_ex.c</FileName>
<FileType>1</FileType>
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_gpio_ex.c</FilePath>
</File>
<File>
<FileName>stm32f1xx_hal_tim.c</FileName>
<FileType>1</FileType>
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_tim.c</FilePath>
</File>
<File>
<FileName>stm32f1xx_hal_tim_ex.c</FileName>
<FileType>1</FileType>
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_tim_ex.c</FilePath>
</File>
<File>
<FileName>stm32f1xx_hal_uart.c</FileName>
<FileType>1</FileType>
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_uart.c</FilePath>
</File>
<File>
<FileName>stm32f1xx_hal.c</FileName>
<FileType>1</FileType>
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal.c</FilePath>
</File>
<File>
<FileName>stm32f1xx_hal_rcc.c</FileName>
<FileType>1</FileType>
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_rcc.c</FilePath>
</File>
<File>
<FileName>stm32f1xx_hal_rcc_ex.c</FileName>
<FileType>1</FileType>
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_rcc_ex.c</FilePath>
</File>
<File>
<FileName>stm32f1xx_hal_gpio.c</FileName>
<FileType>1</FileType>
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_gpio.c</FilePath>
</File>
<File>
<FileName>stm32f1xx_hal_dma.c</FileName>
<FileType>1</FileType>
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_dma.c</FilePath>
</File>
<File>
<FileName>stm32f1xx_hal_cortex.c</FileName>
<FileType>1</FileType>
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_cortex.c</FilePath>
</File>
<File>
<FileName>stm32f1xx_hal_pwr.c</FileName>
<FileType>1</FileType>
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_pwr.c</FilePath>
</File>
<File>
<FileName>stm32f1xx_hal_flash.c</FileName>
<FileType>1</FileType>
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_flash.c</FilePath>
</File>
<File>
<FileName>stm32f1xx_hal_flash_ex.c</FileName>
<FileType>1</FileType>
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_flash_ex.c</FilePath>
</File>
<File>
<FileName>stm32f1xx_hal_exti.c</FileName>
<FileType>1</FileType>
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_exti.c</FilePath>
</File>
<File>
<FileName>stm32f1xx_hal_iwdg.c</FileName>
<FileType>1</FileType>
<FilePath>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_iwdg.c</FilePath>
</File>
<File>
<FileName>stm32f1xx_hal_adc.c</FileName>
<FileType>1</FileType>
<FilePath>..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_adc.c</FilePath>
</File>
</Files>
</Group>
<Group>
<GroupName>Drivers/CMSIS</GroupName>
<Files>
<File>
<FileName>system_stm32f1xx.c</FileName>
<FileType>1</FileType>
<FilePath>../Src/system_stm32f1xx.c</FilePath>
</File>
</Files>
</Group>
<Group>
<GroupName>m_app</GroupName>
<Files>
<File>
<FileName>proto.c</FileName>
<FileType>1</FileType>
<FilePath>..\m_app\proto.c</FilePath>
</File>
</Files>
</Group>
<Group>
<GroupName>HardWare</GroupName>
<Files>
<File>
<FileName>air780e.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\CAR\air780e.c</FilePath>
</File>
<File>
<FileName>network.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\NETWORK\network.c</FilePath>
</File>
<File>
<FileName>net_wifi.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\NETWORK\net_wifi.c</FilePath>
</File>
<File>
<FileName>net_wifi_ota.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\NETWORK\net_wifi_ota.c</FilePath>
</File>
<File>
<FileName>esp8266.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\WIFI\esp8266.c</FilePath>
</File>
<File>
<FileName>mqtt_client.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\WIFI\mqtt_client.c</FilePath>
</File>
<File>
<FileName>relay.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\RELAY\relay.c</FilePath>
</File>
<File>
<FileName>log.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\LOG\log.c</FilePath>
</File>
<File>
<FileName>log_cn.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\LOG\log_cn.c</FilePath>
</File>
<File>
<FileName>24c02.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\24C02\24c02.c</FilePath>
</File>
<File>
<FileName>iic.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\IIC\iic.c</FilePath>
</File>
<File>
<FileName>adc.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\ADC\adc.c</FilePath>
</File>
<File>
<FileName>led.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\LED\led.c</FilePath>
</File>
<File>
<FileName>uart.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\UART\uart.c</FilePath>
</File>
<File>
<FileName>feed_scale.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\FEED_SCALE\feed_scale.c</FilePath>
</File>
<File>
<FileName>user_cmd.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\USER_CMD\user_cmd.c</FilePath>
</File>
<File>
<FileName>ble_at.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\USER_CMD\ble_at.c</FilePath>
</File>
<File>
<FileName>system.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\SYSINIT\system.c</FilePath>
</File>
<File>
<FileName>reset_log.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\SYSINIT\reset_log.c</FilePath>
</File>
<File>
<FileName>app_loop.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\SYSINIT\app_loop.c</FilePath>
</File>
<File>
<FileName>w25q64.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\W25Q64\w25q64.c</FilePath>
</File>
<File>
<FileName>hlw8032.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\HLW8032\hlw8032.c</FilePath>
</File>
<File>
<FileName>oled.c</FileName>
<FileType>1</FileType>
<FilePath>..\HardWare\OLED\oled.c</FilePath>
</File>
</Files>
</Group>
<Group>
<GroupName>Doc</GroupName>
<Files>
<File>
<FileName>README.md</FileName>
<FileType>5</FileType>
<FilePath>..\README.md</FilePath>
</File>
</Files>
</Group>
<Group>
<GroupName>::CMSIS</GroupName>
</Group>
</Groups>
</Target>
</Targets>
<RTE>
<apis/>
<components>
<component Cclass="CMSIS" Cgroup="CORE" Cvendor="ARM" Cversion="3.40.0" condition="CMSIS Core">
<package name="CMSIS" schemaVersion="1.3" url="http://www.keil.com/pack/" vendor="ARM" version="4.2.0"/>
<targetInfos>
<targetInfo name="STM32F103rb_App1"/>
</targetInfos>
</component>
</components>
<files>
<file attr="config" category="header" name="RTE_Driver\Config\RTE_Device.h">
<instance index="0" removed="1">RTE\Device\STM32F103RC\RTE_Device.h</instance>
<component Cclass="Device" Cgroup="Startup" Cvendor="Keil" Cversion="1.0.0" condition="STM32F1xx CMSIS Device"/>
<package name="STM32F1xx_DFP" schemaVersion="1.2" url="http://www.keil.com/pack/" vendor="Keil" version="1.0.5"/>
<targetInfos/>
</file>
<file attr="config" category="source" condition="STM32F1xx HD" name="Device\Source\ARM\startup_stm32f10x_hd.s">
<instance index="0" removed="1">RTE\Device\STM32F103RC\startup_stm32f10x_hd.s</instance>
<component Cclass="Device" Cgroup="Startup" Cvendor="Keil" Cversion="1.0.0" condition="STM32F1xx CMSIS Device"/>
<package name="STM32F1xx_DFP" schemaVersion="1.2" url="http://www.keil.com/pack/" vendor="Keil" version="1.0.5"/>
<targetInfos/>
</file>
<file attr="config" category="source" name="Device\Source\system_stm32f10x.c">
<instance index="0" removed="1">RTE\Device\STM32F103RC\system_stm32f10x.c</instance>
<component Cclass="Device" Cgroup="Startup" Cvendor="Keil" Cversion="1.0.0" condition="STM32F1xx CMSIS Device"/>
<package name="STM32F1xx_DFP" schemaVersion="1.2" url="http://www.keil.com/pack/" vendor="Keil" version="1.0.5"/>
<targetInfos/>
</file>
</files>
</RTE>
</Project>

View File

@ -1,483 +0,0 @@
# 智能水产投料机 V1.5.0FreeRTOS · 串口日志中文版)
> **本版本基于 FreeRTOS 实时操作系统**V10.0.1net/app/sys 三任务架构),
> 串口日志为中文输出(**GBK 编码**,串口工具/蓝牙 APP 字符集请选 GBK
## 工程概述
本工程基于 **STM32F103RCT6**(兼容 GD32F103RCT6+ **STM32Cube HAL** + **FreeRTOS V10.0.1** + **Keil MDK**,实现设备的控制、联网与投喂。
- **BootLoader**`STM32F103_BootLoader/`,负责 OTA 固件烧录、试运行计数与失败回滚、主电源门禁
- **应用固件**`STM32F103_App1/`,当前运行版本 V1.5.0FreeRTOS 版,全中文串口日志)
- **双网络通道**4G(Air780E/EG) 优先WiFi(ESP-01S) 兜底,见下文"双通道网络"
- **蓝牙调试**HLK-B40 BLE 透传模块USART1上电自动配置名称/GATT UUID见下文"蓝牙模块配置"
> 注意Keil 工程必须带编译选项 `--no_multibyte_chars`App1 与 BootLoader 均已配置),
> 使 ARMCC 不解读多字节字符中文字符串GBK/UTF-8 均可)字节原样透传输出。
> `missing closing quote` 编译错误。
## FreeRTOS 任务架构
| 任务 | 优先级 | 栈 | 职责 |
| --- | --- | --- | --- |
| net_task | 3 | 3KB | 网络状态机 `NET_process`4G/WiFi 建链与切换、MQTT 收发、GPS 轮询、OTA 下载 |
| app_task | 2 | 2KB | 业务:继电器命令/反馈状态机、断电记忆恢复、投喂秤轮询与保护、状态上报、OTA 确认与健康检查 |
| sys_task | 1 | 1KB | 系统LED、电量1s、蓝牙调试命令、Uptime 打印(联网后每 60s |
| idle 钩子 | - | - | 喂 IWDG任务死循环占 CPU → idle 饿死 → 复位,正是期望行为) |
关键设计:
- **HAL 时基 = TIM2**1ms 中断,`Src/stm32f1xx_hal_timebase_tim.c`,注意 `HAL_TIM_PeriodElapsedCallback` 里必须 `HAL_IncTick()`SysTick 归 FreeRTOS 当 RTOS tickit.c 不再定义 SVC/PendSV/SysTick 三个 Handlerport.c 提供,经 FreeRTOSConfig.h 宏映射)
- **bg_delay 改造**:调度器运行中 = `vTaskDelay` 让出;调度器未启动(开机初始化)= HAL_Delay + 喂狗
- **互斥锁三把**(调度器启动前全部为空操作直通):
- `s_net_at_mutex`network.cnet 任务长 AT 会话(建链/重连/OTA持锁其他任务 `NET_publish_*` 尝试锁、拿不到丢弃,防 AT 应答交织
- `s_storage_mutex`app_loop.c `storage_lock`EEPROM(软件I2C) 与 W25Q64(软件SPI) 跨任务互斥,递归锁
- `s_log_mutex`log.c日志输出不交织
- **内存预算**48KB RAM裸机存量 ZI ~36.3KB + FreeRTOS 堆 10KBconfigTOTAL_HEAP_SIZE≈ 46.3KBFlash ~67KB/224KB 分区。中断优先级维持原值UART/DMA 0~3 级,不调 RTOS API低于 5 级 syscall 阈值无冲突)
## 产品型号配置
同一套板子适配两种产品,编译前在 `Inc/main.h` 顶部通过一个宏二选一:
```c
#define PRODUCT_WITH_FEED_SCALE 0
```
| 取值 | 产品 | 投喂行为 |
| --- | --- | --- |
| `1` | 智能水产投料机(接 RS485 磅秤) | `feeding``onceWt` 定量自动投喂,带重量反增/超时保护;`sw1` 指令被忽略 |
| `0` | 基础四路继电器(不接磅秤) | `feeding` / `sw1` 直接开关投喂继电器 1由平台侧自行计时 |
## 硬件接口
| 接口 | 用途 | 引脚 | 参数 |
| --- | --- | --- | --- |
| USART1 | 蓝牙串口调试 / 日志输出 | PA9/PA10 | 115200-8N1 |
| USART2 | RS485 喂料秤 | PA2/PA3 | 9600-8N1 |
| USART3 | 4G Air780E/EG MQTT+GNSS | PB10/PB11 | 921600-8N1见下文波特率自适应 |
| UART4 | WiFi ESP-01S | PC10/PC11 | 115200→921600-8N1见下文 WiFi 波特率同步) |
| UART5 | HLW8032 电能计量 | PD2(RX) | 4800-8E1只收芯片每 50ms 主动上报 |
| ESP_RST | WiFi 模块复位 | PA5 | 低电平 500ms 复位 |
| CAT_PWR | 4G 模块开机 | PC4 | 开漏输出,拉低=模块开机(上电即拉低) |
| ESP_EN | WiFi 模块使能 | PA6 | 推挽输出,高电平=使能;**默认关闭4G 运行时拉低断电省电,切 WiFi 才拉高** |
| BT_MODE | 蓝牙 AT/透传切换 | PB0 | 开漏输出,拉低 1s 进 AT 模式ble_at.c 控制) |
| BT_RST | 蓝牙模块复位 | PA7 | 推挽输出,低电平=工作(默认),高电平=复位 |
| OLED 屏幕 | 128x64 + GB2312 字库 IC | CLK=PC12 MOSI=PC9 DC=PC8 CS1=PC7 FSO=PC6 CS2=PB15 | GPIO 模拟 SPI显示电压/电流/功率/电能/继电器/网络 |
| Relay1 | 投喂电机控制 | PB7 | 高电平吸合 |
| Relay2~4 | 备用控制 | PB8/PC0/PC2 | 高电平吸合 |
| I2C软件 | AT24C02 配置存储 | PA11/PA12 | 页写入 |
| SPI软件 | W25Q64 外部 Flash | PB3/PB4/PA15/PB5 | OTA 固件暂存 |
| ADC1 | 电池电压检测 | PA0 | 低电量上报 `pow:0` |
| PA8 | 手动模式检测 | PA8 | 高电平=手动模式 |
| PA1 | NET_LED 联网指示 | PA1 | 低电平点亮,见下表 |
| PC13 | LED2 心跳 / TPL5010 喂狗 | PC13 | 每 1s 翻转(见下文看门狗) |
### 指示灯含义
| 灯 | 状态 | 含义 |
| --- | --- | --- |
| NET_LEDPA1 | 100ms 快闪 | SmartConfig 智能配网中,请用 ESP-Touch APP 广播密码 |
| NET_LEDPA1 | 500ms 慢闪 | 联网中4G/WiFi 建链、重连) |
| NET_LEDPA1 | 常亮 | 已联网MQTT 就绪) |
| LED2PC13 | 1s 心跳闪烁 | 系统正常运行(兼任 TPL5010 外置看门狗喂狗) |
## 看门狗(双狗架构)
| 看门狗 | 超时 | 喂狗点 | 作用 |
| --- | --- | --- | --- |
| IWDG片内独立看门狗 | ~26sLSI 漂移下最短 ~17s | APP 由 FreeRTOS **idle 钩子**喂养(`vApplicationIdleHook`BL 的擦除/拷贝循环 | 固件卡死 → 复位BL 启动OTA 试运行期计一次试错 |
| TPL5010外置硬件狗 | 7 分钟 | PC13(LED2) 电平翻转APP `activeEvents()` 每秒翻转BL 进入时及长循环中翻转 | MCU 完全失控(含 IWDG 失效)时的最后兜底 |
注意LED2 同时承担运行指示和外部狗喂狗,**修改 LED2 闪烁逻辑时必须保持周期性翻转**,否则 7 分钟后整板复位。
## 重启记录与断电保护
### 重启记录reset_log.c
- 开机时("固件版本"日志后)打印最近 **10 条**重启记录:`重启原因 + 上次运行时长`
- **存储**W25Q64 `0x100000` 一个 4KB 扇区10 条 32B 记录环形覆盖(序号+原因码+运行秒数+CRC16写满一圈擦一次断电不丢
- **原因来源**`RCC->CSR` 由 BootLoader 读入后放共享 RAM`BOOT_SHARE`,地址 `0x2000BF00`,热复位保持)传给 APP软件复位的细分原因由各复位点 `reset_log_mark()` 预写(被平台踢下线/心跳超时/4G无服务/无SIM/OTA升级/OTA健康超时/4G初始化失败/串口RESET/OTA/RECOVERY命令/4G模组自重启等
- **运行时长**:每秒写入共享 RAM热复位不丢看门狗/软件复位后精确到秒
### 断电保护(法拉电容 + PA0 检测)
- APPPA0 电量检测改为 **50ms 快轮询**(原 1s<10%=外部 12V 断开立即 50ms 快速复核 2 检测+确认全程 ~150ms)→ **先发 `pow:0` 后打日志** 写电能/断电记录 500ms 让模组把报文发完 复位发送通道忙 GPS 轮询占锁时不锁存下个周期重试直到发出或电容耗尽电量百分比做了 0~100% 限幅电容放电时 3.3V 基准下跌会失真
- BootLoader跳转 APP 前读 PA0寄存器直读 ADC主电源未恢复< 11V则每 500ms 检测原地等待**不跳 APP**防止单片机靠电容低压运行 APP 而外设没电的乱态12V 恢复后正常放行
- 断电记录只被下次成功启动消费一次(`boot_seen` 位翻转标记),一次掉电 = 一条记录BL 等待期间不产生记录
- BL 找不到有效 APP如只刷了 BL打印提示并每 3s 主动复位重试,刷入 APP 即恢复
## 电能计量HLW8032
只计量**累计消耗电量**,上报平台标识符 `energy`(单位 kWh两位小数
- **硬件**HLW80325V 供电)经 B0505S 隔离电源 + 光耦隔离,芯片 TXD 接 PD2UART5_RX电压采样 ZMPT107-12mA:2mA初级 220V 串 100K次级并 49.9Ω),电流采样 CT 100A/100mA1000:1输出并 2Ω三个模拟脚各对地 33nF
- **通信**4800bps 8E1F1 上按 9 位帧长 + 偶校验配置),芯片每 50ms 主动上报 24 字节帧,只收不发;帧同步 = 首字节 0x55/0xAA/0xFx + 第二字节 0x5A + 校验和([2]~[22] 相加低 8 位)
- **电量算法**(手册公式):`kWh = 总脉冲 × 功率参数寄存器 × 电压系数 × 电流系数 / (10^9×3600)`PF 寄存器 16 位溢出由数据更新寄存器 bit7 翻转计数(`总脉冲 = k×65536 + PF`50ms 一帧不可能漏翻转;芯片断电重启(计数值倒退)自动重新基准
- **系数宏**`main.h` 的 `HLW_U_COEF`=100K/49.9/1000=2.004)、`HLW_I_COEF`=1/(0.002×1000)=0.5)——改电阻/互感器只动这两个宏
- **防噪声假同步**:要求连续 3 帧且间隔 20~150ms 才认定在线PD2 悬空时电磁干扰偶发也能凑出校验和正确的假帧,但不可能连续卡在 50ms 节奏上)
- **滤波与零漂**:电压/电流/功率经 EMA 滑动平均(系数 1/8约 0.4s 响应)压空载跳变;电流再减零点偏移 `HLW_I_ZERO_A`(0.02A) 钳位到 0功率死区 `HLW_P_DEAD_W`(2W) 以下显示 0、超出再减 `HLW_P_ZERO_W`(0.5W)——宏都在 main.h
- **持久化**累计脉冲数uint64存 EEPROM 偏移 164~171每攒约 0.1kWh 写一次(限制擦写),所有计划内复位(`reset_log_mark` 覆盖的复位点)复位前写一次,断电预警(法拉电容续航内)再写一次——除突然死机外基本不丢电量
- **上报策略**:无论功率多少,每 3 分钟上报一次 `{"energy":%.2f,"power":%.1f}`;继电器开关切换随状态大报文携带 energy。串口电能日志固定 10s 一条。间隔宏 `ENERGY_REPORT_INTERVAL_MS` 在 app_loop.c
- **调试日志**:芯片在线时每 10s 打印一条(`电能: x.xxkWh 电压: xxx.xV 电流: x.xxA 功率: xxxx.xW`),与上报节奏无关
## OLED 屏幕显示128x64 + GB2312 字库 IC
- 移植自《485模拟磅秤数据带CRC》的 oledisp 驱动GPIO 模拟 SPI屏幕 CS1 + 字库 CS2 两路片选,字库数据从 FSO 脚读回)
- 字符串直接写中文(工程源文件即 GBK 编码,字库 IC 按 GB2312 地址换算取字模)
- **已联网界面**sys_task 每 1s 刷新):第 0 行 网络 + 运行时间(`WiFi 00:02:35`,天:时:分),第 1 行 电压/电流,第 2 行 功率/电能(计量芯片离线时显示"计量芯片离线"),第 3 行 继电器状态中文(`开关:开开关关`
- **未联网界面**:只显示联网进度,不显示电能/开关——4G 模式显示"正在等待4G联网"+ 最近完成阶段(模块复位成功/AT指令成功/SIM卡检测成功/附着网络成功/连接服务器中/无SIM卡状态由 `g_net_ui_phase` 从 air780e.c 各阶段更新WiFi 固定热点显示"WiFi固定模式 正在搜索热点"+倒计时SmartConfig 显示"智能配网 请打开APP广播"+倒计时(倒计时由 esp_wait_countdown 写入 `g_net_ui_countdown`
- 开机欢迎页按产品模式显示标题(带秤=`智能水产投料机`,无秤=`继电器控制终端`+ `V1.5.0 RTOS`4G 阻塞建链期间由 bg_delay 钩子每 500ms 刷进度页(调度器启动前 sys_task 未运行,曾一直卡在欢迎页)
## 双通道网络4G 优先WiFi 兜底)
网络层 `HardWare/NETWORK/network.c` 对业务层屏蔽底层模组差异,业务只调 `NET_*` 接口4G 后端(`CAR/air780e.c`,模组内置 MQTT与 WiFi 后端(`NETWORK/net_wifi.c`TCP 透传 + 软件 MQTT同构运行时互斥切换。
### 通道切换状态机
EEPROM偏移 130记忆**上次成功联网方式**`4G` / `WiFi固定热点` / `WiFi配网热点`),仅方式变化时写一次;开机优先跑上次成功的方式:
```text
上次 4G 成功4G无 SIM/无信号每 10s 重试3 次失败)→ WiFi → 回 4G…循环
上次 WiFi 成功WiFi 一轮建链失败 → 4G同上重试→ 回 WiFi…循环
WiFi 建链子顺序net_wifi_init阻塞式等待期间 vTaskDelay 让出 CPU
① 上次是配网热点AT+CIPSTATUS 查已存 AP 自动连接10sSTATUS:2=已拿到IP
② AT+CWJAP 连代码里固定的 SSID/PASSesp8266.h 的 WIFI_SSID/WIFI_PASS60s 倒计时)
③ ESP-Touch SmartConfig 智能配网60s 倒计时,配到的热点由模块保存)
全部失败 → 回 4G
WiFi 运行30s MQTT PINGREQ 心跳,连续 2 帧无应答(约 60s判死TCP 断开(CLOSED)/心跳超时 → 打印原因并直接整机复位重连
CLOSED=服务器主动断开/被平台踢下线ping 超时=服务器无响应/假在线,最坏检测延迟 ~30s
```
> 注:已存 AP 的判断用 `AT+CIPSTATUS` 主动查询而非被动等 `WIFI GOT IP` 打印——
> 后者在波特率同步阶段就会被清缓冲擦掉,永远等不到。
### WiFi 通道实现要点
- **TCP 透传 + 软件 MQTT**ESP-01S 用 `AT+CIPMODE=1` + `AT+CIPSEND` 进透传MQTT 3.1.1 协议栈在 MCU 软件实现(`HardWare/WIFI/mqtt_client.c`),不用 AT+MQTT
- **WiFi 波特率同步**ESP-01S 出厂/RESTORE 后 115200初始化时探测并用 `AT+UART_DEF=921600` 固化掉电保存MCU 侧 `UART4_SetBaudRate()` 同步;失败自动回滚维持 115200
- **接收缓冲共享**WiFi 与 4G 互斥运行,共用 `Rx_Buf`/`U2_CopyBuff``stm32f1xx_it.c` 两路 IDLE 均带激活后端守卫RAM 增量 <2KB
- **AT 应答严格匹配**:发送后才清接收缓冲(防上一条应答迟到尾巴造成"假成功")、二进制安全匹配、收到 `ERROR`/`FAIL` 立即失败;进透传前先 `AT+CIPCLOSE` 清残留连接、`CIPMUX=0` 先于 `CIPMODE=1`
- **ORE 自愈**921600 高波特率下 UART 溢出会让 HAL 中止 DMA 接收,`HAL_UART_ErrorCallback` 里按当前激活通道立即重新武装DMA 填满(连续流无 IDLE 间隙)时在 `HAL_UART_RxCpltCallback` 里搬运并重启,防接收停摆
- **WiFi 通道 OTA**:支持,透传 TCP 上软件实现 HTTP Range 分块下载,见下文"OTA 升级"章节
## 4G 波特率自适应115200 → 921600
- Air780E 出厂/自适应模式只覆盖 9600~115200921600 必须用 `AT+IPR` 固定。
- APP 联网时**永远先按 115200 探测**(避免向自适应侦测窗口发送其他波特率的乱码):
- 通 → 发 `AT+IPR=921600` + `AT&W` 固化,切换到 921600日志出现 `baud -> 921600`
- 不通(模块已固化 921600→ 直接切 921600 连接。
- 切换由 `USART3_SetBaudRate()` 运行中重写 BRR 完成,不影响 GPIO/DMA/NVIC。
## 4G 信号检测AT+CSQ
- 网络附着CGATT成功后查询一次 `AT+CSQ`rssi(0~31) 转百分比串口输出:`> 4G: signal 77% (rssi=24)`
- 无信号rssi=99 或 <2 `g_no_signal_detected`与无 SIM 走同一套重试/回退 WiFi 逻辑
- 信号弱(<20%告警但继续联网
## GPS 定位上报(仅 4G 通道Air780EG
**开关**运行时可切与产品模式同机制——EEPROM(173) 存 0/1空白时用 `Inc/main.h``PRODUCT_WITH_GPS` 宏做默认值;蓝牙命令 `GPS` 查询、`GPS0` 关 / `GPS1` 开,切换后自动重启生效。关闭后不进行任何 GPS 相关 AT 交互(纯 Air780E 用)。
参考工程《air780EG-V1.1加入北斗GPS数据》移植实现`air780e.c` 的 `air780e_gps_poll()`)。
### 工作流程
1. **GNSS 初始化**4G 拨号(`AT+SAPBR=1,1`)成功后执行 `AT+CGNSPWR=1`(开 GNSS 电源)+ `AT+CGNSAID=31,1,1,1`A-GPS 辅助定位)。非 EG 版本模组回 ERROR 时仅告警并关闭轮询(`s_gnss_present=0`),不影响联网
2. **周期查询**:联网 60 秒后首次查询(`GPS_FIRST_DELAY_MS`,留搜星时间),之后每 5 分钟一次(`GPS_REPORT_INTERVAL_MS``AT+CGNSINF`
3. **解析**`+CGNSINF: run,fix,UTC,lat,lon,...`,手工按逗号切分(不依赖 sscanf scanset。**定位无效fix≠1跳过本次上报**(室内常见),不发空串
4. **坐标转换**WGS-84 → GCJ-02见下节
5. **上报**:模组内置 MQTT `AT+MPUB`topic `/iot/data/up/<ClientID>`
### 上报格式
云平台字段标识符 **`GPS`****经度在前、纬度在后**(高德/GeoJSON 习惯):
```json
{"header":"iot.prop.post","body":{"GPS":"115.924092,28.679470"}}
```
### 坐标系转换WGS-84 → GCJ-02
GPS 模块输出 WGS-84 原始坐标,**直接输入高德地图会有 100~600m 加密偏移**(国内地图强制使用 GCJ-02"火星坐标系")。固件在上报前用公开标准算法完成转换(`wgs84_to_gcj02()`,三角函数近似,中国境外自动跳过),上报的坐标可直接输入高德地图/坐标拾取器。
> 注意:若改用百度地图,需要再转一层 GCJ-02 → BD-09。
### 保护机制
- OTA 下载期间(`g_net_tx_busy`)禁止 GPS 查询插队,避免污染 HTTP 数据流
- 查询等待期间有平台下行命令(`+MSUB:`)插队时主动让行,不吞继电器指令
### 使用注意
- GPS 天线需能看到天空:室内基本无法定位(`> GPS: no fix, skip` 属正常),窗边/阳台通常可定,室外最佳;冷启动首次定位约 1~3 分钟
- 验证:串口日志 `> GPS: 115.924092,28.679470`,或直接看平台 `GPS` 字段
## OTA 升级
### 流程
```text
平台下发 iot.ota.upgrade.post(url, crc16, ver)
→ APP: 模块硬复位 → HTTP HEAD 拿大小 → 擦 W25Q64 槽 B
→ 3072 字节/块分段下载AT+HTTPPARA BREAK/BREAKEND + HTTPACTION + HTTPREAD
→ 每块写 W25Q64 并回读校验,全程累计 CRC16
→ 整包 CRC + 栈顶/复位向量校验 → 可靠上报 result.post等 OK + 重试)
→ 写 OTA 信息区(状态 DOWNLOADED) → 复位进 BootLoader
BootLoader:
→ 擦除备份槽 → 备份当前 APP1 到槽 A失败则中止更新留在旧版本
→ 擦 APP1 → 写入新固件 → CRC + 向量校验 → 状态 INSTALLED → 跳转
新 APP 试运行:
→ MQTT 就绪 + 首次发布成功 + 3s → 上报 confirm.post
→ 确认标志写 W25Q64回读验证 + 3 次重试)→ 版本号存 EEPROM
→ 下次启动 BL 清理 OTA 标志,升级完成
```
### Flash 分区
内部 Flash256KB
| 区域 | 地址 | 大小 |
| --- | --- | --- |
| BootLoader | `0x08000000` | 32KB |
| APP1 运行区 | `0x08008000` | 224KB |
W25Q64 外部 Flash8MB
| 区域 | 地址 | 用途 |
| --- | --- | --- |
| 槽 A | `0x000000` (256KB) | 当前固件备份(回滚镜像) |
| 槽 B | `0x040000` (256KB) | 新固件下载暂存 |
| 信息区 A/B | `0x080000` / `0x081000` (各 4KB) | OTA 状态双副本magic + 序号 + checksum |
### OTA 确认通知可靠性
- 确认通知(`iot.ota.confirm.post`)延后到首次发布成功后 **9 秒**发送避开开机上报风暴pow/ICCID/状态/电能挤占模组 MQTT 发送窗口)
- 本地确认标志和版本号**先于**通知落盘——通知发送失败不会导致 BL 误判回滚
- 通知未送达时每 30s 自动重试直到成功(仅补平台通知);开机 pow/ICCID 上报间隔拉开到 300ms
### 健康检查与回退
新固件"健康" = 4G 附着 → MQTT 连接 → 首次发布成功。回退判据(**3 次真实故障**才回退):
- **看门狗复位**IWDG/WWDG固件真的卡死→ BL 计一次试错(读 `RCC->CSR` 判别);
- **健康超时**MQTT 就绪 1 分钟仍无首次发布)→ APP 复位前自行计一次;
- **上电/按键/普通软件复位不计次**——现场断电、断网不会误触发回退;
- 试运行期间完全断网 → 不计次、不回退,持续等待网络恢复。
### 掉电/断网安全性
| 故障点 | 结果 |
| --- | --- |
| 下载中断网/掉电 | 信息区未更新,旧固件照常运行,平台可重推 |
| BL 备份阶段掉电 | `backup_valid=0`,重启后重新备份 |
| BL 擦写 APP1 中途掉电 | 状态 `COPYING` 视为 `DOWNLOADED`,重启后完整重做(备份不会被坏镜像覆盖) |
| 备份创建失败 | **中止更新**,留在旧版本,下次重启重试 |
| 回退过程掉电 | 重启后重新执行回退拷贝 |
### 其他要点
- **双通道均支持 OTA**4G 用模组内置 HTTP`AT+HTTP*` 分块WiFi 通道由 MCU 在透传 TCP 上软件实现 HTTP **Range 分块下载**`NETWORK/net_wifi_ota.c`
- 流程:收 OTA 指令 → 停投喂 → `+++` 退透传MQTT 连接被顶替,单连接限制)→ 连固件服务器 → 每 3072B 一个 `GET``Range: bytes=off-end`MCU 自定节奏,防止服务器一次灌爆接收缓冲)→ 每块写 W25Q64 槽 B + 回读校验 + 累计 CRC16 → 整包 CRC + 固件头校验 → 重连 MQTT 上报 result → 写信息区复位进 BL
- 下载期间 MQTT 断开属正常,进度只在开始/结束上报;失败自动重连 MQTT 恢复业务
- OTA 触发时先强制停止投喂(断开继电器 1
- 下载期间其余任务(秤轮询/LED/电量/蓝牙命令)由 RTOS 调度照常并发运行,业务状态上报暂时屏蔽
- 分段接收按 `+HTTPREAD: <len>` 声明长度收齐即处理无静默等待40KB 固件全程约 38 秒(含模块复位重连 ~15s
## 主要功能
1. **双通道 MQTT 联网**4G(Air780E/EG) 优先 + WiFi(ESP-01S) 兜底自动切换,见上文"双通道网络"
2. **远程控制**:平台下发开关 `sw1/sw2/sw3/sw4`、`feeding` 指令(投料机模式下 `sw1` 被忽略,投喂只能走 `feeding`
3. **自动投喂**`PRODUCT_WITH_FEED_SCALE = 1`
- 平台设置单次投喂量 `onceWt`
- 平台下发 `feeding=true` 启动投喂
- 继电器 1 吸合RS485 秤实时读取重量
- 当重量减少 ≥ `onceWt` 时自动停止,带重量反增与 10 分钟超时保护
4. **去皮/标定**:平台设置 `zero` 空载重量,设置后按最新原始重量重新计算剩余料重并立即上报
5. **GPS 定位上报**:见上文专题(仅 4G 通道)
6. **4G 信号检测**:见上文专题
7. **OTA 升级**见上文专题4G/WiFi 双通道均支持)
8. **蓝牙调试**USART1 支持 `RESET/INFO/STATUS/R1ON~R4OFF/ALLON/ALLOFF/OTA/RECOVERY/HELP` 等命令
9. **掉电安全(断电记忆)**联网成功MQTT 首次就绪)后按 EEPROM 恢复继电器上次状态,随后全量上报让平台与电路一致
- 无秤模式(`PRODUCT_WITH_FEED_SCALE = 0`**四路全部恢复**(含 SW1
- 投料机模式(`PRODUCT_WITH_FEED_SCALE = 1`**继电器 1 不恢复且上电强制断开**(防止误投料浪费饲料),继电器 2~4 恢复
## EEPROM 布局AT24C02256 字节)
上电由 `eepromReadInfo()` 读取并按 Magic 校验;配置损坏/未初始化时回退到 `main.h` 的编译期默认值并重新写入。
| 偏移 | 大小 | 内容 |
| --- | --- | --- |
| 0~19 | 20B | `ClientID` MQTT 客户端 ID |
| 20~39 | 20B | `Username` |
| 40~59 | 20B | `Passward` |
| 60~79 | 20B | `ServerIP` |
| 80~99 | 20B | `Topic`(下行主题前缀 `/iot/data/down/` |
| 100~101 | 2B | `ServerPort` |
| 102~108 | 7B | `Relay_State[7]`[1..4] 四路继电器掉电记忆状态,[0] 保留,[5][6] 未用 |
| 112~115 | 4B | `Magic`0xDEADBEEF检测 EEPROM 是否已初始化109~111 为对齐填充) |
| 116~117 | 2B | `Ver` OTA 固件版本号(确认后落盘,断电保持) |
| 120~121 | 2B | 秤参数:去皮/空载重量 `zero`0.1KG 单位,仅称重模式) |
| 122~123 | 2B | 秤参数:单次投喂量 `onceWt`0.1KG 单位,仅称重模式) |
| 124 | 1B | 秤参数初始化标志 |
| 130 | 1B | 上次成功联网方式0=4G1=WiFi固定热点2=WiFi配网热点仅变化时写 |
| 132~157 | 26B | 蓝牙名称配置缓存(命中则开机跳过蓝牙 AT 设置流程) |
| 160 | 1B | 日志等级0~5TRACE/DEBUG/INFO/WARN/ERROR/FATAL |
| 164~171 | 8B | HLW8032 累计脉冲数uint64每攒 0.1kWh + 断电前写入) |
| 172 | 1B | 产品模式0=四路继电器(无秤)1=称重投料(带秤),空白时用 main.h 宏默认值(蓝牙 SCALE0/SCALE1 切换) |
| 173 | 1B | GPS 定位开关0=关1=开,空白时用 main.h 宏默认值(蓝牙 GPS0/GPS1 切换,重启生效) |
| 175~194 | 20B | OTA 平台任务 id下载开始时写入重启确认补发 result.post 成功后清 0xFF |
| 250 | 1B | 探测字节(上电写 0xA5 回读,验证 I2C 通信) |
> 注意:改 `MqttInfo_Str` 结构体会改变上述偏移秤参数120+和联网方式130
> `feed_scale.c` / `network.c` 里是独立宏定义,需同步避让。
## 平台数据字段
| 字段 | 含义 | 说明 |
| --- | --- | --- |
| `ver` | 固件版本号 | 每次状态上报携带,平台可据此识别设备是否发生回退 |
| `weight` | 剩余料重(净重) | 与平台物模型一致 |
| `zero` | 空载重量(去皮) | 与平台物模型一致 |
| `onceWt` | 单次投喂量 | 每次投喂目标重量;**无秤模式不上报、下发设置被拒绝** |
| `sw1` / `feeding` | 第一路继电器状态 | 1=投喂中0=停止;无秤模式下两字段**同时上报**,平台两个开关同步刷新 |
| `sw2~sw4` | 备用继电器状态 | 0/1 |
| `pow` | 电量状态 | 1=正常0=低电量 |
| `energy` | 累计消耗电量 | HLW8032 计量,单位 kWh两位小数>10W 每 30s否则首次+开关切换) |
| `power` | 实时功率 | HLW8032 计量,单位 W一位小数与 energy 同一条报文上报(策略同 energy |
| `iccid` | SIM 卡号 | 仅 4G 通道MQTT 连上后随 pow 上报一次,用于物联网卡套餐查询缴费 |
| `fwver` | 代码版本字符串 | 格式 `FIRMWARE_VERSION_STR`+`:`+EEPROM 版本序号(如 "1.5.0:15"),字符串类型,每次重启联网后上报一次(与状态大报文中的整数 `ver` OTA 版本号区分开) |
| `GPS` | 定位坐标 | 仅 4G 通道(Air780EG)GCJ-02 高德坐标系,格式 `"经度,纬度"`,每 5 分钟上报 |
> 注:`zero`(剩余料重)与 `onceWt` 为称重模式专用字段——`PRODUCT_WITH_FEED_SCALE=0` 时状态上报不携带这两个字段,平台下发设置会收到 `no scale on this device` 拒绝应答。
下行报文由 `m_app/proto.c` 按键名定位解析(`"sw2"` 不会误中 `"sw21"`,其他字段里的 true/false 不会造成误判)。
## 产品模式(带秤/无秤,运行时可切)
`PRODUCT_WITH_FEED_SCALE` 从纯编译宏改为**运行时可切换**:模式存 EEPROM(172),空白时用 main.h 宏做默认值。蓝牙命令:`SCALE` 查询,`SCALE0`=四路继电器(无秤)`SCALE1`=称重投料(带秤)**切换后自动重启生效**(初始化路径不同,必须重启)。影响范围:状态上报字段(zero/onceWt)、sw1/feeding 指令语义、继电器 1 断电记忆策略、秤轮询开关。
## 串口调试命令
通过 USART1蓝牙串口发送
```text
RESET - 系统复位
INFO - 打印 MQTT 配置
STATUS - 打印继电器状态
R1ON - 打开继电器 1
R1OFF - 关闭继电器 1
R2ON/R2OFF ~ R4ON/R4OFF - 控制继电器 2~4
ALLON - 打开所有继电器
ALLOFF - 关闭所有继电器
OTA - 复位进入 BootLoader 检查升级
RECOVERY - WiFi 模块 AT+RESTORE 恢复出厂(忘掉已存热点)+ 整机复位,用于测试 SmartConfig
BLESET - 强制重新设置蓝牙名称/GATT UUID清 EEPROM 缓存后重跑配置流程)
LOG - 查询当前日志等级
LOGTRACE/LOGDEBUG/LOGINFO/LOGWARN/LOGERROR/LOGFATAL
- 设置日志等级(写入 EEPROM断电不丢
HELP - 显示支持命令
```
SmartConfig 测试流程:关掉固定热点 → 发 `RECOVERY` → 设备复位走完 4G 重试进入 WiFi → 固定热点(60s) 失败后自动进 SmartConfig60s 倒计时)→ 用 ESP-Touch APP 广播密码;配网成功后下次开机按记忆优先 `AT+CIPSTATUS` 秒连已存热点(无需再配)。
## 蓝牙模块配置HLK-B40
- **接线**:模块 UART0_TXD/RXD 接 USART1PA10/PA9PC5功能键**PB0**(开漏输出)
- **上电自动配置**`ble_at.c`):拉低 PC5 1s 进 AT 模式 → 设 `AT+NAME=<BLE_NAME>` → 设 GATT UUID默认 `FFE0` 服务 + `FFE1` 收发共用特征JDY-31/HM-10 同款,通用 BLE 串口 APP 可连)→ `AT+REBOOT` 生效
- **发射功率可调**`main.h` 的 `BLE_TX_POWER`AT+RFPOWER1~18 级,出厂默认 8——多台设备同场所时调小如 1~3缩短搜索距离避免认错缓存同时记录功率值改宏后下次开机自动重设一次
- **零窗口设计**:配置成功后将名称缓存进 EEPROM偏移 132之后每次开机命中缓存就**完全跳过 AT 流程**——蓝牙 APP 不再断联,开机最早的重启原因/重启记录日志都能实时看到;只有代码里改了 `BLE_NAME`main.h才会自动重设一次
- **手动重设**:蓝牙命令 `BLESET`(清缓存强制重跑)
- **日志等级**`LOG` 查询,`LOGTRACE~LOGFATAL` 六档切换,写 EEPROM偏移 160断电保持。档位即"输出阈值"——只打印该级及以上:
| 命令 | 级别 | 输出内容 | 适用场景 |
| --- | --- | --- | --- |
| `LOGTRACE` | 0 最细 | 全部当前≈DEBUG | 底层逐字节排查 |
| `LOGDEBUG` | 1 调试(默认) | AT 指令交互、连接细节、正常运行信息 | 现场排查 |
| `LOGINFO` | 2 信息 | 联网成功、继电器动作、重启记录、警告/错误 | 日常观察 |
| `LOGWARN` | 3 警告 | 仅警告/错误AT 超时、无 SIM、掉线复位 | 稳定运行免打扰 |
| `LOGERROR` | 4 错误 | 仅错误踢下线、心跳超时、CRC 失败) | 只看故障 |
| `LOGFATAL` | 5 致命 | 全部静音 | 极端安静 |
- USART1 接收中断带 ORE 自愈(`HAL_UART_ErrorCallback` 自动恢复接收,蓝牙模块重启乱码不会把命令通道打死)
## 代码分层APP
`main.c` 只保留初始化和调度器启动,逻辑全部下沉到任务与模块:
```c
int main(void)
{
SCB->VTOR = FLASH_BASE | Application_1_Addr;
Board_Init(); // 基础硬件HAL/时钟/GPIO/DMA/UART/看门狗SYSINIT/system.c
app_init(); // 业务模块初始化SYSINIT/app_loop.c
app_rtos_start(); // 创建 net/app/sys 任务 + 启动调度器,不再返回
}
```
## 目录结构
```text
STM32F103_App1/
├── Src/
│ ├── main.c # 仅 VTOR + Board_Init/app_init/app_rtos_start
│ └── stm32f1xx_hal_timebase_tim.c # TIM2 作 HAL 时基SysTick 归 FreeRTOS
├── Inc/
│ ├── main.h # 产品型号宏 PRODUCT_WITH_FEED_SCALE、MQTT 默认配置
│ └── FreeRTOSConfig.h # RTOS 配置堆10KB/钩子/中断阈值,含内存预算注释)
├── Middlewares/
│ └── FreeRTOS/Source/ # FreeRTOS V10.0.1 内核tasks/queue/list + ARM_CM3 port + heap_4
├── m_app/
│ ├── proto.c/h # 平台 JSON 报文取值工具(按键名定位)
│ ├── ota_app.h # Flash 分区定义BootLoader 32K + APP1 224K
│ └── boot_share.h # BL↔APP 共享 RAM 结构(复位原因/运行时长/原因码)
├── HardWare/
│ ├── USER_CMD/ # 蓝牙串口命令处理
│ ├── FEED_SCALE/ # RS485 喂料秤与定量投喂状态机
│ ├── RELAY/ # 继电器控制、反馈检测、平台指令执行、断电记忆恢复
│ ├── NETWORK/ # 网络层network.c 通道切换/统一分发 + net_wifi.c WiFi 后端 + net_wifi_ota.c WiFi OTA
│ ├── WIFI/ # esp8266.c ESP-01S AT驱动/透传/SmartConfig + mqtt_client.c 软件MQTT
│ ├── CAR/ # Air780E/EG AT 驱动、波特率自适应、CSQ 信号检测、GNSS 定位、OTA 下载
│ ├── UART/ # USART1/2/3+UART4/5 初始化、运行中切波特率、中断/DMA 接收、ORE 自愈
│ ├── HLW8032/ # 电能计量UART5 帧解析、PF 脉冲累计、kWh 换算、EEPROM 持久化)
│ ├── 24C02/ # EEPROM 读写(页写入,带跨任务互斥锁)
│ ├── IIC/ # 软件 I2C带总线恢复SDA 被拉死时补 9 时钟释放)
│ ├── ADC/ # 电池电压检测0~100% 限幅、掉电去抖判定)
│ ├── LED/ # 指示灯LED2 兼任 TPL5010 喂狗)
│ ├── LOG/ # 分级日志USART1 输出带互斥锁log_cn.c 中文格式串)
│ ├── SYSINIT/ # Board_Init/IWDG、app_init、RTOS 任务、reset_log 重启记录
│ └── W25Q64/ # 外部 Flash 驱动与 OTA 双槽信息区(带跨任务互斥锁)
└── MDK-ARM/ # Keil 工程文件
STM32F103_BootLoader/
├── Src/main.c # 硬件初始化 + IWDG + 入口
├── m_app/ota_boot.c # OTA 状态机:备份/烧录/回退 + 复位原因透传 + 主电源门禁 + 无APP重试
├── m_app/boot_share.h # 与 APP 共享的 RAM 结构定义(同一份拷贝)
├── HardWare/W25Q64/ # 外部 Flash 驱动(与 APP 共用设计)
├── HardWare/LOG/ # 分级日志(与 APP 相同)
└── MDK-ARM/ # Keil 工程文件
```
## 编译烧录
**Keil GUI**
1. 打开 `MDK-ARM/STM32F103rb_App1.uvprojx`BootLoader 工程同理)
2. 按产品形态设置 `Inc/main.h` 中的 `PRODUCT_WITH_FEED_SCALE`
3. 编译下载(先烧 BootLoader 到 `0x08000000`APP1 起始地址 `0x08008000`
4. 首次启动会自动把默认 MQTT 配置写入 AT24C02
**命令行**CI/脚本可用):
```bash
# 编译Keil 安装目录的 UV4-r 全量 -b 增量,-o 输出日志)
/c/Keil_v5/UV4/UV4.exe -r -j0 STM32F103rb_App1.uvprojx -o build.txt
# 烧录J-Link 命令脚本,工程 MDK-ARM 目录下有现成的 jlink_flash.jlink
JLink.exe -CommanderScript jlink_flash.jlink
```
## 版本
- Version: 1.5.0
- Author: Helei

View File

@ -1,407 +0,0 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : stm32f1xx_hal_msp.c
* Description : This file provides code for the MSP Initialization
* and de-Initialization codes.
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2019 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
extern DMA_HandleTypeDef hdma_usart3_rx;
extern DMA_HandleTypeDef hdma_uart4_rx;
extern DMA_HandleTypeDef hdma_usart2_rx;
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */
/* USER CODE END TD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN Define */
/* USER CODE END Define */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN Macro */
/* USER CODE END Macro */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* External functions --------------------------------------------------------*/
/* USER CODE BEGIN ExternalFunctions */
/* USER CODE END ExternalFunctions */
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* Initializes the Global MSP.
*/
void HAL_MspInit(void)
{
/* USER CODE BEGIN MspInit 0 */
/* USER CODE END MspInit 0 */
__HAL_RCC_AFIO_CLK_ENABLE();
__HAL_RCC_PWR_CLK_ENABLE();
/* System interrupt init*/
/** NOJTAG: JTAG-DP Disabled and SW-DP Enabled
*/
__HAL_AFIO_REMAP_SWJ_NOJTAG();
/* USER CODE BEGIN MspInit 1 */
/* USER CODE END MspInit 1 */
}
/**
* @brief UART MSP Initialization
* This function configures the hardware resources used in this example
* @param huart: UART handle pointer
* @retval None
*/
void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(huart->Instance==USART1)
{
/* USER CODE BEGIN USART1_MspInit 0 */
/* USER CODE END USART1_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USART1 interrupt Init */
HAL_NVIC_SetPriority(USART1_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
/* USER CODE BEGIN USART1_MspInit 1 */
/* USER CODE END USART1_MspInit 1 */
}
else if(huart->Instance==USART2)
{
/* USER CODE BEGIN USART2_MspInit 0 */
/* USER CODE END USART2_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_USART2_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**USART2 GPIO Configuration
PA2 ------> USART2_TX
PA3 ------> USART2_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USART2_RX DMA Init (DMA1_Channel6) */
hdma_usart2_rx.Instance = DMA1_Channel6;
hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart2_rx.Init.Mode = DMA_NORMAL;
hdma_usart2_rx.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(huart, hdmarx, hdma_usart2_rx);
HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 3, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
/* USART2 interrupt Init */
HAL_NVIC_SetPriority(USART2_IRQn, 3, 0);
HAL_NVIC_EnableIRQ(USART2_IRQn);
/* USER CODE BEGIN USART2_MspInit 1 */
/* USER CODE END USART2_MspInit 1 */
}
else if(huart->Instance==USART3)
{
/* USER CODE BEGIN USART3_MspInit 0 */
/* USER CODE END USART3_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_USART3_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**USART3 GPIO Configuration
PB10 ------> USART3_TX
PB11 ------> USART3_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USART3 DMA Init */
/* USART3_RX Init */
hdma_usart3_rx.Instance = DMA1_Channel3;
hdma_usart3_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_usart3_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart3_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart3_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart3_rx.Init.Mode = DMA_NORMAL;
hdma_usart3_rx.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_usart3_rx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(huart,hdmarx,hdma_usart3_rx);
/* USART3 interrupt Init */
HAL_NVIC_SetPriority(USART3_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(USART3_IRQn);
/* USER CODE BEGIN USART3_MspInit 1 */
/* USER CODE END USART3_MspInit 1 */
}
else if(huart->Instance==UART4)
{
/* USER CODE BEGIN UART4_MspInit 0 */
/* USER CODE END UART4_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_UART4_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
/**UART4 GPIO Configuration
PC10 ------> UART4_TX ( ESP-01S RXD)
PC11 ------> UART4_RX ( ESP-01S TXD)
*/
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* UART4 DMA Init (DMA2_Channel3) */
hdma_uart4_rx.Instance = DMA2_Channel3;
hdma_uart4_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_uart4_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_uart4_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_uart4_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_uart4_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_uart4_rx.Init.Mode = DMA_NORMAL;
hdma_uart4_rx.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_uart4_rx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(huart,hdmarx,hdma_uart4_rx);
/* UART4 interrupt Init */
HAL_NVIC_SetPriority(UART4_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(UART4_IRQn);
/* USER CODE BEGIN UART4_MspInit 1 */
/* USER CODE END UART4_MspInit 1 */
}
else if(huart->Instance==UART5)
{
/* Peripheral clock enable */
__HAL_RCC_UART5_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/**UART5 GPIO Configuration
PD2 ------> UART5_RX ( HLW8032 TXDPC12=TX )
*/
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/* UART5 interrupt Init */
HAL_NVIC_SetPriority(UART5_IRQn, 3, 0);
HAL_NVIC_EnableIRQ(UART5_IRQn);
}
}
/**
* @brief UART MSP De-Initialization
* This function freeze the hardware resources used in this example
* @param huart: UART handle pointer
* @retval None
*/
void HAL_UART_MspDeInit(UART_HandleTypeDef* huart)
{
if(huart->Instance==USART1)
{
/* USER CODE BEGIN USART1_MspDeInit 0 */
/* USER CODE END USART1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_USART1_CLK_DISABLE();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);
/* USER CODE BEGIN USART1_MspDeInit 1 */
/* USER CODE END USART1_MspDeInit 1 */
}
else if(huart->Instance==USART2)
{
/* USER CODE BEGIN USART2_MspDeInit 0 */
/* USER CODE END USART2_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_USART2_CLK_DISABLE();
/**USART2 GPIO Configuration
PA2 ------> USART2_TX
PA3 ------> USART2_RX
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_2|GPIO_PIN_3);
/* USART2 interrupt DeInit */
HAL_NVIC_DisableIRQ(USART2_IRQn);
/* USER CODE BEGIN USART2_MspDeInit 1 */
/* USER CODE END USART2_MspDeInit 1 */
}
else if(huart->Instance==USART3)
{
/* USER CODE BEGIN USART3_MspDeInit 0 */
/* USER CODE END USART3_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_USART3_CLK_DISABLE();
/**USART3 GPIO Configuration
PB10 ------> USART3_TX
PB11 ------> USART3_RX
*/
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_10|GPIO_PIN_11);
/* USART3 DMA DeInit */
HAL_DMA_DeInit(huart->hdmarx);
/* USART3 interrupt DeInit */
HAL_NVIC_DisableIRQ(USART3_IRQn);
/* USER CODE BEGIN USART3_MspDeInit 1 */
/* USER CODE END USART3_MspDeInit 1 */
}
else if(huart->Instance==UART4)
{
/* USER CODE BEGIN UART4_MspDeInit 0 */
/* USER CODE END UART4_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_UART4_CLK_DISABLE();
/**UART4 GPIO Configuration
PC10 ------> UART4_TX
PC11 ------> UART4_RX
*/
HAL_GPIO_DeInit(GPIOC, GPIO_PIN_10|GPIO_PIN_11);
/* UART4 DMA DeInit */
HAL_DMA_DeInit(huart->hdmarx);
/* UART4 interrupt DeInit */
HAL_NVIC_DisableIRQ(UART4_IRQn);
/* USER CODE BEGIN UART4_MspDeInit 1 */
/* USER CODE END UART4_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/* ADC MSP Init: 使能ADC1时钟, 配置PA0为模拟输入 */
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
if (hadc->Instance == ADC1) {
__HAL_RCC_ADC1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef g = {0};
g.Pin = GPIO_PIN_0;
g.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(GPIOA, &g);
}
}
/* USER CODE END 1 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
/* HAL ADC injected conversion callback (required by HAL, not used) */
void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef* hadc) { (void)hadc; }

View File

@ -1,357 +0,0 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file stm32f1xx_it.c
* @brief Interrupt Service Routines.
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2019 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f1xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "string.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */
/* USER CODE END TD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/* External variables --------------------------------------------------------*/
extern DMA_HandleTypeDef hdma_usart3_rx;
extern DMA_HandleTypeDef hdma_uart4_rx;
extern UART_HandleTypeDef huart3;
extern UART_HandleTypeDef huart4;
extern UART_HandleTypeDef huart2;
extern UART_HandleTypeDef huart1;
extern UART_HandleTypeDef huart5;
extern DMA_HandleTypeDef hdma_usart2_rx;
extern uint8_t uart2_dma_buf[64];
extern uint8_t uart2_frame_buf[64];
extern volatile uint16_t uart2_frame_len;
extern volatile uint8_t uart2_frame_ready;
/* USER CODE BEGIN EV */
#include "network.h"
/* USER CODE END EV */
/******************************************************************************/
/* Cortex-M3 Processor Interruption and Exception Handlers */
/******************************************************************************/
/**
* @brief This function handles Non maskable interrupt.
*/
void NMI_Handler(void)
{
/* USER CODE BEGIN NonMaskableInt_IRQn 0 */
/* USER CODE END NonMaskableInt_IRQn 0 */
/* USER CODE BEGIN NonMaskableInt_IRQn 1 */
/* USER CODE END NonMaskableInt_IRQn 1 */
}
/**
* @brief This function handles Hard fault interrupt.
*/
void HardFault_Handler(void)
{
/* 硬件错误兜底:用轮询方式往 USART1 打一条消息(中断已不可靠),
* IWDG "HARD FAULT" */
{
static const char msg[] = "\r\n!!! HARD FAULT, wait IWDG reset !!!\r\n";
for (unsigned i = 0; i < sizeof(msg) - 1; i++) {
while ((USART1->SR & USART_SR_TXE) == 0) { }
USART1->DR = msg[i];
}
}
while (1)
{
/* USER CODE BEGIN W1_HardFault_IRQn 0 */
/* USER CODE END W1_HardFault_IRQn 0 */
}
}
/**
* @brief This function handles Memory management fault.
*/
void MemManage_Handler(void)
{
/* USER CODE BEGIN MemoryManagement_IRQn 0 */
/* USER CODE END MemoryManagement_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
/* USER CODE END W1_MemoryManagement_IRQn 0 */
}
}
/**
* @brief This function handles Prefetch fault, memory access fault.
*/
void BusFault_Handler(void)
{
/* USER CODE BEGIN BusFault_IRQn 0 */
/* USER CODE END BusFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_BusFault_IRQn 0 */
/* USER CODE END W1_BusFault_IRQn 0 */
}
}
/**
* @brief This function handles Undefined instruction or illegal state.
*/
void UsageFault_Handler(void)
{
/* USER CODE BEGIN UsageFault_IRQn 0 */
/* USER CODE END UsageFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_UsageFault_IRQn 0 */
/* USER CODE END W1_UsageFault_IRQn 0 */
}
}
/**
* @brief This function handles Debug monitor.
*/
void DebugMon_Handler(void)
{
}
/* SVC_Handler / PendSV_Handler / SysTick_Handler 已由 FreeRTOS port.c 提供
* FreeRTOSConfig.h vPortSVCHandler/xPortPendSVHandler/xPortSysTickHandler
* CMSIS
* HAL TIM2SysTick FreeRTOS RTOS tick */
/**
* @brief This function handles TIM2 global interrupt (HAL timebase).
*/
extern TIM_HandleTypeDef htim2;
void TIM2_IRQHandler(void)
{
/* USER CODE BEGIN TIM2_IRQn 0 */
/* USER CODE END TIM2_IRQn 0 */
HAL_TIM_IRQHandler(&htim2);
/* USER CODE BEGIN TIM2_IRQn 1 */
/* USER CODE END TIM2_IRQn 1 */
}
/******************************************************************************/
/* STM32F1xx Peripheral Interrupt Handlers */
/* Add here the Interrupt Handlers for the used peripherals. */
/* For the available peripheral interrupt handler names, */
/* please refer to the startup file (startup_stm32f1xx.s). */
/******************************************************************************/
/**
* @brief This function handles USART1 global interrupt.
*/
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
/**
* @brief This function handles DMA1 channel3 global interrupt.
*/
void DMA1_Channel6_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hdma_usart2_rx);
}
void DMA1_Channel3_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Channel3_IRQn 0 */
/* USER CODE END DMA1_Channel3_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_usart3_rx);
/* USER CODE BEGIN DMA1_Channel3_IRQn 1 */
/* USER CODE END DMA1_Channel3_IRQn 1 */
}
/**
* @brief This function handles DMA2 channel3 global interrupt (UART4_RX / WiFi).
*/
void DMA2_Channel3_IRQHandler(void)
{
/* USER CODE BEGIN DMA2_Channel3_IRQn 0 */
/* USER CODE END DMA2_Channel3_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_uart4_rx);
/* USER CODE BEGIN DMA2_Channel3_IRQn 1 */
/* USER CODE END DMA2_Channel3_IRQn 1 */
}
/**
* @brief This function handles UART4 global interrupt (WiFi ESP-01S, DMA+IDLE).
*/
void UART4_IRQHandler(void)
{
/* USER CODE BEGIN UART4_IRQn 0 */
uint32_t temp;
if ((__HAL_UART_GET_FLAG(&huart4, UART_FLAG_IDLE) != RESET))
{
/* 清除状态寄存器和数据寄存器 */
__HAL_UART_CLEAR_IDLEFLAG(&huart4);
/* 失效DMA接收 */
HAL_UART_DMAStop(&huart4);
/* 获取接收长度:总大小-剩余大小 */
temp = huart4.hdmarx->Instance->CNDTR;
Rx_Len = Rx_Max - temp;
/* 追加到 U2_CopyBuff 供 AT 指令/MQTT 报文解析;
* USART3 WiFi */
if (Rx_Len > 0 && NET_GetActiveBackend() == NET_BACKEND_WIFI &&
(U2_CopyIndex + Rx_Len) < (U2_COPY_SIZE - 1))
{
memcpy(U2_CopyBuff + U2_CopyIndex, Rx_Buf, Rx_Len);
U2_CopyIndex += Rx_Len;
U2_CopyBuff[U2_CopyIndex] = '\0';
U2_CopyFlag = 1;
}
/* 使能接收DMA传输 */
HAL_UART_Receive_DMA(&huart4, Rx_Buf, Rx_Max);
}
/* USER CODE END UART4_IRQn 0 */
HAL_UART_IRQHandler(&huart4);
/* USER CODE BEGIN UART4_IRQn 1 */
/* USER CODE END UART4_IRQn 1 */
}
/**
* @brief This function handles USART2 global interrupt.
*/
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
/* 485 秤帧定界:线路静默产生 IDLEDMA 缓冲区里即一帧完整数据 */
if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE) != RESET)
{
__HAL_UART_CLEAR_IDLEFLAG(&huart2);
HAL_UART_DMAStop(&huart2);
uint16_t len = (uint16_t)(sizeof(uart2_dma_buf) - huart2.hdmarx->Instance->CNDTR);
if (len > 0 && !uart2_frame_ready) {
memcpy(uart2_frame_buf, uart2_dma_buf, len);
uart2_frame_len = len;
uart2_frame_ready = 1;
}
HAL_UART_Receive_DMA(&huart2, uart2_dma_buf, sizeof(uart2_dma_buf));
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
}
/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
/* USER CODE END USART2_IRQn 1 */
}
/**
* @brief This function handles USART3 global interrupt.
*/
void USART3_IRQHandler(void)
{
/* USER CODE BEGIN USART3_IRQn 0 */
uint32_t temp;
if ((__HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE) != RESET))
{
/* 清除状态寄存器和数据寄存器 */
__HAL_UART_CLEAR_IDLEFLAG(&huart3);
/* 失效DMA接收 */
HAL_UART_DMAStop(&huart3);
/* 获取接收长度:总大小-剩余大小 */
temp = huart3.hdmarx->Instance->CNDTR;
Rx_Len = Rx_Max - temp;
/* 追加到 U2_CopyBuff 供 AT 指令/MQTT 消息解析;
* UART4 4G */
if (Rx_Len > 0 && NET_GetActiveBackend() == NET_BACKEND_4G &&
(U2_CopyIndex + Rx_Len) < (U2_COPY_SIZE - 1))
{
memcpy(U2_CopyBuff + U2_CopyIndex, Rx_Buf, Rx_Len);
U2_CopyIndex += Rx_Len;
U2_CopyBuff[U2_CopyIndex] = '\0';
U2_CopyFlag = 1;
}
/* 使能接收DMA传输 */
HAL_UART_Receive_DMA(&huart3, Rx_Buf, Rx_Max);
}
/* USER CODE END USART3_IRQn 0 */
HAL_UART_IRQHandler(&huart3);
/* USER CODE BEGIN USART3_IRQn 1 */
/* USER CODE END USART3_IRQn 1 */
}
/**
* @brief This function handles UART5 global interrupt (HLW8032).
*/
void UART5_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart5);
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

View File

@ -1,59 +0,0 @@
#ifndef BOOT_SHARE_H
#define BOOT_SHARE_H
#include <stdint.h>
/*==== BL ? APP 共享内存(热复位保持)====
* SRAM
* /
* APP RAM 0x2000BF00 */
#define BOOT_SHARE_ADDR 0x2000BF00u
#define BOOT_SHARE_MAGIC 0x52534852u /* 'RSHR' */
typedef struct {
uint32_t magic; /* BOOT_SHARE_MAGIC */
uint32_t csr_flags; /* BL 开机时写入的 RCC->CSR复位原因标志 */
uint32_t uptime_sec; /* APP 每秒更新的本轮运行时长(热复位不丢) */
uint32_t pending_reason; /* APP 主动复位前写入的细分原因码RSN_x */
uint32_t checksum; /* 前 4 项累加和 */
} boot_share_t;
#define BOOT_SHARE ((volatile boot_share_t *)BOOT_SHARE_ADDR)
/*==== 重启原因码 ====*/
#define RSN_UNKNOWN 0 /* 未知 */
#define RSN_POWER_ON 1 /* 上电/意外掉电 */
#define RSN_PIN_RESET 2 /* 按键复位 */
#define RSN_WATCHDOG 3 /* 卡死看门狗溢出 */
#define RSN_KICKED 4 /* 平台/服务器踢下线 */
#define RSN_PING_TIMEOUT 5 /* 心跳超时(服务器无响应/假在线) */
#define RSN_NO_SERVICE 6 /* 4G 无网络服务 */
#define RSN_NO_SIM 7 /* 无 SIM 卡(预留) */
#define RSN_OTA_UPDATE 8 /* OTA 升级完成重启 */
#define RSN_OTA_HEALTH 9 /* OTA 健康检查超时 */
#define RSN_4G_FAIL 10 /* 4G 初始化失败 */
#define RSN_CMD_RESET 11 /* 串口 RESET 命令 */
#define RSN_CMD_OTA 12 /* 串口 OTA 命令 */
#define RSN_CMD_RECOVERY 13 /* 串口 RECOVERY 命令 */
#define RSN_POWER_LOSS 14 /* 外部 12V 断电(法拉电容续航中记录) */
#define RSN_4G_MODULE 15 /* 4G 模组异常自重启(boot.rom) */
#define RSN_SOFT_GENERIC 16 /* 软件复位(未标记细分原因,如 JLink 复位) */
#define RSN_OTA_FAIL 17 /* OTA 下载/前置检查失败,中止后复位 */
static inline uint32_t boot_share_sum(const volatile boot_share_t *s)
{
return s->magic + s->csr_flags + s->uptime_sec + s->pending_reason;
}
static inline int boot_share_valid(void)
{
return BOOT_SHARE->magic == BOOT_SHARE_MAGIC &&
BOOT_SHARE->checksum == boot_share_sum(BOOT_SHARE);
}
static inline void boot_share_store(void)
{
BOOT_SHARE->checksum = boot_share_sum(BOOT_SHARE);
}
#endif /* BOOT_SHARE_H */

Some files were not shown because too many files have changed in this diff Show More