V1.5: OLED界面重构(未联网显示4G/WiFi联网进度+倒计时,联网后显示电能/开关中文/运行时间,防烧屏10s偏移轮换)

This commit is contained in:
helei 2026-08-15 06:40:38 +08:00
parent 9bdee63b31
commit 9338ae002c
11 changed files with 132 additions and 25 deletions

View File

@ -81,6 +81,7 @@ int Verify_Firmware_External(uint32_t ext_addr, uint32_t size)
/*==== 4G Reset Control ====*/ /*==== 4G Reset Control ====*/
/*==== 4G Reset Control ====*/ /*==== 4G Reset Control ====*/
static void cat_reset(void) { static void cat_reset(void) {
g_net_ui_phase = NET_UI_4G_RESET;
log_info("> 4G: 复位..."); log_info("> 4G: 复位...");
/* 关闭 NVIC 中断, 防止模块复位期间乱码触发中断风暴 */ /* 关闭 NVIC 中断, 防止模块复位期间乱码触发中断风暴 */
@ -196,6 +197,7 @@ static int cat_wait_cgatt_attached(int max_wait_s) {
U2_CopyIndex = 0; U2_CopyIndex = 0;
U2_CopyBuff[0] = 0; U2_CopyBuff[0] = 0;
U2_CopyFlag = 0; U2_CopyFlag = 0;
g_net_ui_phase = NET_UI_4G_ATTACH;
CAT1_printf("AT+CGATT?"); CAT1_printf("AT+CGATT?");
uint8_t t = 30; /* 单次查询等 3 秒 */ uint8_t t = 30; /* 单次查询等 3 秒 */
@ -227,6 +229,7 @@ static int cat_wait_cgatt_attached(int max_wait_s) {
static int cat_connect_network(void) { static int cat_connect_network(void) {
log_info("> 4G: 网络..."); log_info("> 4G: 网络...");
g_net_ui_phase = NET_UI_4G_AT;
/* 波特率同步:永远先按 115200 探测(出厂/自适应模式的模块不能被 /* 波特率同步:永远先按 115200 探测(出厂/自适应模式的模块不能被
921600 921600
@ -246,6 +249,7 @@ static int cat_connect_network(void) {
if (catSendCmd("AT", "OK", 3, 20) != 0) return -1; if (catSendCmd("AT", "OK", 3, 20) != 0) return -1;
if (catSendCmd("ATE0", "OK", 1, 10) != 0) return -1; if (catSendCmd("ATE0", "OK", 1, 10) != 0) return -1;
g_net_ui_phase = NET_UI_4G_SIM; /* AT 通过,开始检测 SIM/信号 */
/* 检测 SIM 卡:多次查询 AT+CPIN?,识别无 SIM 卡场景 */ /* 检测 SIM 卡:多次查询 AT+CPIN?,识别无 SIM 卡场景 */
int sim_retry = 3; int sim_retry = 3;
@ -265,6 +269,7 @@ static int cat_connect_network(void) {
strstr((char *)U2_CopyBuff, "SIM NOT INSERTED") || strstr((char *)U2_CopyBuff, "SIM NOT INSERTED") ||
strstr((char *)U2_CopyBuff, "+CME ERROR: 10")) { strstr((char *)U2_CopyBuff, "+CME ERROR: 10")) {
log_warn("> 4G: 未检测到 SIM 卡"); log_warn("> 4G: 未检测到 SIM 卡");
g_net_ui_phase = NET_UI_4G_NOSIM;
g_no_sim_detected = 1; g_no_sim_detected = 1;
return -2; return -2;
} }
@ -325,6 +330,7 @@ static int cat_connect_network(void) {
/*==== MQTT Connect ====*/ /*==== MQTT Connect ====*/
static int cat_connect_mqtt(void) { static int cat_connect_mqtt(void) {
g_net_ui_phase = NET_UI_4G_MQTT;
char b[120]; log_info("> 4G: MQTT..."); char b[120]; log_info("> 4G: MQTT...");
/* 复用 catSendCmd 等待 MCONNECT 的真正连接确认, 不清空已有缓冲区 */ /* 复用 catSendCmd 等待 MCONNECT 的真正连接确认, 不清空已有缓冲区 */

View File

@ -39,6 +39,9 @@ volatile uint8_t g_no_sim_detected = 0;
volatile uint8_t g_no_signal_detected = 0; volatile uint8_t g_no_signal_detected = 0;
/* 当前激活的网络后端 */ /* 当前激活的网络后端 */
volatile net_ui_phase_t g_net_ui_phase = NET_UI_NONE;
volatile int g_net_ui_countdown = 0;
static net_backend_t s_backend = NET_BACKEND_4G; static net_backend_t s_backend = NET_BACKEND_4G;
/* 上次成功联网方式EEPROM 偏移 130秤参数占用到 126避开 */ /* 上次成功联网方式EEPROM 偏移 130秤参数占用到 126避开 */

View File

@ -29,6 +29,21 @@ void NET_SaveMode(uint8_t mode); /*
* AT USART3 */ * AT USART3 */
extern volatile uint8_t g_net_tx_busy; 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 =====*/ /*===== API =====*/
void NET_init(void); void NET_init(void);
void NET_process(void); void NET_process(void);

View File

@ -275,6 +275,12 @@ void oled_print(const char *s)
} }
} }
/*==== µ±Ç°ÁÐÓÒÒÆ px ÏñËØ ====*/
void oled_xoff(uint8_t px)
{
s_col += px;
}
/*==== 整行显示:计算像素宽度,不足 128px 用空格补齐(覆盖上次内容)====*/ /*==== 整行显示:计算像素宽度,不足 128px 用空格补齐(覆盖上次内容)====*/
void oled_print_line(const char *s) void oled_print_line(const char *s)
{ {

View File

@ -12,6 +12,7 @@
void oled_init(void); /* GPIO + 屏幕初始化app_init 调用) */ void oled_init(void); /* GPIO + 屏幕初始化app_init 调用) */
void oled_clear(void); /* 全屏清屏 */ void oled_clear(void); /* 全屏清屏 */
void oled_locate(uint8_t row, uint8_t col8); /* 定位row 0~3col8 为 8 像素单位 0~15 */ void oled_locate(uint8_t row, uint8_t col8); /* 定位row 0~3col8 为 8 像素单位 0~15 */
void oled_xoff(uint8_t px); /* 在当前列基础上右移 px 像素(防烧屏偏移用) */
void oled_print(const char *gbk_str); /* 从当前位置显示 GBK 字符串 */ void oled_print(const char *gbk_str); /* 从当前位置显示 GBK 字符串 */
void oled_print_line(const char *gbk_str); /* 整行显示:不足 128px 用空格补齐覆盖旧内容 */ void oled_print_line(const char *gbk_str); /* 整行显示:不足 128px 用空格补齐覆盖旧内容 */

View File

@ -198,31 +198,84 @@ static void app_task(void *arg)
} }
} }
/*==== OLED 基础信息:电压/电流/功率/电能/继电器/网络1s 刷新 ====*/ /*==== OLED 界面刷新1s 一次)
static void oled_home_refresh(void) * ///+
* g_net_ui_phase 4G/WiFi WiFi
* 10s 0/2/4px ====*/
static void oled_home_refresh(uint32_t up_ms)
{ {
char buf[24]; char buf[24];
/* 防烧屏:每 10s 换一档水平偏移0/2/4px */
uint8_t xoff = (uint8_t)((HAL_GetTick() / 10000u) % 3u) * 2u;
if (hlw8032_online()) { if (g_net_mqtt_ready) {
snprintf(buf, sizeof(buf), "%.1fV %.2fA", /*---- 已联网界面 ----*/
(double)hlw8032_get_voltage_v(), (double)hlw8032_get_current_a()); if (hlw8032_online()) {
oled_locate(0, 0); oled_print_line(buf); snprintf(buf, sizeof(buf), "%.1fV %.2fA",
snprintf(buf, sizeof(buf), "%.1fW %.2fkWh", (double)hlw8032_get_voltage_v(), (double)hlw8032_get_current_a());
(double)hlw8032_get_power_w(), (double)hlw8032_get_energy_kwh()); oled_locate(0, 0); oled_xoff(xoff); oled_print_line(buf);
oled_locate(1, 0); oled_print_line(buf); snprintf(buf, sizeof(buf), "%.1fW %.2fkWh",
(double)hlw8032_get_power_w(), (double)hlw8032_get_energy_kwh());
oled_locate(1, 0); oled_xoff(xoff); oled_print_line(buf);
} else {
oled_locate(0, 0); oled_xoff(xoff); oled_print_line("计量芯片离线");
oled_locate(1, 0); oled_xoff(xoff); oled_print_line("");
}
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_locate(2, 0); oled_xoff(xoff); oled_print_line(buf);
/* 网络 + 运行时间:满 1 天显示 "1天02时",否则 "27:35" */
uint32_t mins = up_ms / 60000u;
const char *nt = (NET_GetActiveBackend() == NET_BACKEND_WIFI) ? "WiFi" : "4G";
if (mins >= 1440u) {
snprintf(buf, sizeof(buf), "%s已连接%u天%02u时", nt,
(unsigned)(mins / 1440u), (unsigned)((mins % 1440u) / 60u));
} else {
snprintf(buf, sizeof(buf), "%s已连接%02u:%02u", nt,
(unsigned)(mins / 60u), (unsigned)(mins % 60u));
}
oled_locate(3, 0); oled_xoff(xoff); oled_print_line(buf);
} else { } else {
oled_locate(0, 0); oled_print_line("计量芯片离线"); /*---- 未联网界面:联网进度 ----*/
oled_locate(1, 0); oled_print_line(""); net_ui_phase_t ph = g_net_ui_phase;
oled_locate(0, 0); oled_xoff(xoff); oled_print_line("");
if (ph == NET_UI_WIFI_FIXED || ph == NET_UI_WIFI_SMART ||
NET_GetActiveBackend() == NET_BACKEND_WIFI) {
if (ph == NET_UI_WIFI_FIXED) {
oled_locate(1, 0); oled_xoff(xoff); oled_print_line("WiFi固定模式");
oled_locate(2, 0); oled_xoff(xoff); oled_print_line("正在搜索热点");
} else if (ph == NET_UI_WIFI_SMART) {
oled_locate(1, 0); oled_xoff(xoff); oled_print_line("智能配网");
oled_locate(2, 0); oled_xoff(xoff); oled_print_line("请打开APP广播");
} else {
oled_locate(1, 0); oled_xoff(xoff); oled_print_line("WiFi模式");
oled_locate(2, 0); oled_xoff(xoff); oled_print_line("初始化中...");
}
snprintf(buf, sizeof(buf), "剩余 %ds", g_net_ui_countdown);
oled_locate(3, 0); oled_xoff(xoff); oled_print_line(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_locate(1, 0); oled_xoff(xoff); oled_print_line("正在等待4G联网");
oled_locate(2, 0); oled_xoff(xoff); oled_print_line(step);
oled_locate(3, 0); oled_xoff(xoff); oled_print_line("");
}
} }
snprintf(buf, sizeof(buf), "开关:%d%d%d%d",
(int)MqttInfoStr.Relay_State[1], (int)MqttInfoStr.Relay_State[2],
(int)MqttInfoStr.Relay_State[3], (int)MqttInfoStr.Relay_State[4]);
oled_locate(2, 0); oled_print_line(buf);
const char *net = !g_net_mqtt_ready ? "未联网" :
(NET_GetActiveBackend() == NET_BACKEND_WIFI) ? "WiFi 已连接" : "4G 已连接";
oled_locate(3, 0); oled_print_line(net);
} }
/* 电能日志/上报间隔:想改频率就改这个宏(单位 ms */ /* 电能日志/上报间隔:想改频率就改这个宏(单位 ms */
@ -253,7 +306,7 @@ static void sys_task(void *arg)
batt_tick = HAL_GetTick(); batt_tick = HAL_GetTick();
update_batter(); update_batter();
hlw8032_tick(); /* 攒够约 0.1kWh 写一次 EEPROM */ hlw8032_tick(); /* 攒够约 0.1kWh 写一次 EEPROM */
oled_home_refresh(); /* OLED 基础信息 1s 刷新 */ oled_home_refresh(up_ms); /* OLED 界面 1s 刷新 */
reset_log_tick((uint32_t)(up_ms / 1000)); /* 共享 RAM 更新运行秒数,热复位不丢 */ reset_log_tick((uint32_t)(up_ms / 1000)); /* 共享 RAM 更新运行秒数,热复位不丢 */
} }

View File

@ -10,6 +10,7 @@
#include "esp8266.h" #include "esp8266.h"
#include "air780e.h" /* U2_CopyBuff / bg_delay */ #include "air780e.h" /* U2_CopyBuff / bg_delay */
#include "network.h"
#include "uart.h" #include "uart.h"
#include "log.h" #include "log.h"
#include "led.h" #include "led.h"
@ -71,6 +72,7 @@ static int esp_wait_countdown(const char *expect, int timeout_s)
} }
} }
if (remain % 10 == 0 && sec > 0) { if (remain % 10 == 0 && sec > 0) {
g_net_ui_countdown = sec; /* OLED 显示用 */
log_info("> WiFi: 倒计时 %ds", sec--); log_info("> WiFi: 倒计时 %ds", sec--);
} }
} }
@ -244,6 +246,8 @@ int esp_join_fixed_ap(void)
{ {
char cmd[96]; char cmd[96];
g_net_ui_phase = NET_UI_WIFI_FIXED;
log_info("> WiFi: 连接固定热点 '%s' (%ds)", WIFI_SSID, WIFI_JOIN_TIMEOUT_S); log_info("> WiFi: 连接固定热点 '%s' (%ds)", WIFI_SSID, WIFI_JOIN_TIMEOUT_S);
/* 保持 CWAUTOCONN=1默认连接成功的AP会被模块保存 /* 保持 CWAUTOCONN=1默认连接成功的AP会被模块保存
@ -267,6 +271,7 @@ int esp_join_fixed_ap(void)
int esp_smartconfig(void) int esp_smartconfig(void)
{ {
log_info("> WiFi: SmartConfig 开始 (%ds), 请用 ESP-Touch APP", WIFI_SMART_TIMEOUT_S); 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 快闪提示配网模式 */ g_net_led_smartconfig = 1; /* NET_LED 100ms 快闪提示配网模式 */
if (esp_send_cmd("AT+CWSTARTSMART", "OK", 5000) != 0) { g_net_led_smartconfig = 0; return -1; } if (esp_send_cmd("AT+CWSTARTSMART", "OK", 5000) != 0) { g_net_led_smartconfig = 0; return -1; }

View File

@ -6,7 +6,7 @@
/*===== 用户配置区 =====*/ /*===== 用户配置区 =====*/
/* 固定 WiFi 凭据:优先尝试连接的热点;连不上(60s)则进 Smart Config 智能配网 */ /* 固定 WiFi 凭据:优先尝试连接的热点;连不上(60s)则进 Smart Config 智能配网 */
#define WIFI_SSID "TP_Link-01" /* TODO: 輕흙미땍 WiFi 츰냔 */ #define WIFI_SSID "TP_Link-02" /* TODO: 輕흙미땍 WiFi 츰냔 */
#define WIFI_PASS "zxcvbnmm112" /* TODO: 填入固定 WiFi 密码 */ #define WIFI_PASS "zxcvbnmm112" /* TODO: 填入固定 WiFi 密码 */
//#define WIFI_SSID "TP_Link" /* TODO: 填入固定 WiFi 名称 */ //#define WIFI_SSID "TP_Link" /* TODO: 填入固定 WiFi 名称 */

View File

@ -77,7 +77,7 @@
<tvExpOptDlg>0</tvExpOptDlg> <tvExpOptDlg>0</tvExpOptDlg>
<IsCurrentTarget>1</IsCurrentTarget> <IsCurrentTarget>1</IsCurrentTarget>
</OPTFL> </OPTFL>
<CpuCode>18</CpuCode> <CpuCode>255</CpuCode>
<Books> <Books>
<Book> <Book>
<Number>0</Number> <Number>0</Number>
@ -937,6 +937,19 @@
<RteFlg>0</RteFlg> <RteFlg>0</RteFlg>
<bShared>0</bShared> <bShared>0</bShared>
</File> </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>
<Group> <Group>
@ -947,7 +960,7 @@
<RteFlg>0</RteFlg> <RteFlg>0</RteFlg>
<File> <File>
<GroupNumber>8</GroupNumber> <GroupNumber>8</GroupNumber>
<FileNumber>51</FileNumber> <FileNumber>52</FileNumber>
<FileType>5</FileType> <FileType>5</FileType>
<tvExp>0</tvExp> <tvExp>0</tvExp>
<Focus>0</Focus> <Focus>0</Focus>

View File

@ -122,7 +122,9 @@
- 移植自《485模拟磅秤数据带CRC》的 oledisp 驱动GPIO 模拟 SPI屏幕 CS1 + 字库 CS2 两路片选,字库数据从 FSO 脚读回) - 移植自《485模拟磅秤数据带CRC》的 oledisp 驱动GPIO 模拟 SPI屏幕 CS1 + 字库 CS2 两路片选,字库数据从 FSO 脚读回)
- 字符串直接写中文(工程源文件即 GBK 编码,字库 IC 按 GB2312 地址换算取字模) - 字符串直接写中文(工程源文件即 GBK 编码,字库 IC 按 GB2312 地址换算取字模)
- 显示内容(`oled_home_refresh()`sys_task 每 1s 刷新):第 0 行 电压/电流,第 1 行 功率/电能(计量芯片离线时显示"计量芯片离线"),第 2 行 四路继电器状态(`开关:1010`),第 3 行 网络状态WiFi 已连接/4G 已连接/未联网) - **已联网界面**sys_task 每 1s 刷新):第 0 行 电压/电流,第 1 行 功率/电能(计量芯片离线时显示"计量芯片离线"),第 2 行 继电器状态中文(`开关:开开关关`),第 3 行 网络 + 运行时间(`WiFi已连接02:35`,满 1 天显示 `1天02时`
- **未联网界面**:只显示联网进度,不显示电能/开关——4G 模式显示"正在等待4G联网"+ 最近完成阶段(模块复位成功/AT指令成功/SIM卡检测成功/附着网络成功/连接服务器中/无SIM卡状态由 `g_net_ui_phase` 从 air780e.c 各阶段更新WiFi 固定热点显示"WiFi固定模式 正在搜索热点"+倒计时SmartConfig 显示"智能配网 请打开APP广播"+倒计时(倒计时由 esp_wait_countdown 写入 `g_net_ui_countdown`
- **防烧屏**:每 10s 内容整体右移 0/2/4px 轮换
- 开机显示"智能控制器 + 版本号"欢迎页,首次刷新后进入常态界面 - 开机显示"智能控制器 + 版本号"欢迎页,首次刷新后进入常态界面
## 双通道网络4G 优先WiFi 兜底) ## 双通道网络4G 优先WiFi 兜底)

View File

@ -118,3 +118,6 @@
- `BT_MODE`(PB0):蓝牙 AT/透传切换此前版本已实现ble_at.c 开漏控制) - `BT_MODE`(PB0):蓝牙 AT/透传切换此前版本已实现ble_at.c 开漏控制)
- 新增 OLED 屏幕128x64 + 晶联讯 GB2312 字库 IC`HardWare/OLED/oled.c`GPIO 模拟 SPI - 新增 OLED 屏幕128x64 + 晶联讯 GB2312 字库 IC`HardWare/OLED/oled.c`GPIO 模拟 SPI
CLK=PC12 MOSI=PC9 DC=PC8 CS1=PC7 FSO=PC6 CS2=PB151s 刷新电压/电流/功率/电能/继电器/网络状态 CLK=PC12 MOSI=PC9 DC=PC8 CS1=PC7 FSO=PC6 CS2=PB151s 刷新电压/电流/功率/电能/继电器/网络状态
- OLED 界面重构未联网只显示联网进度4G 各阶段中文提示 / WiFi 固定热点搜索倒计时 /
SmartConfig"请打开APP广播"倒计时),联网后才显示电能/开关/网络/运行时间;
开关状态改中文(开关:开开关关);防烧屏每 10s 内容右移 0/2/4px 轮换