e_linker_wifi.c
e_linker_wifi_login()(第 477-501 行)4.2 配置对齐主流程:e_linker_wifi_init()(第 180-469 行)4.3 AP SSID 拼接规则:ap_ssid_stitching()(第 1060-1070 行)4.4 状态探测(WIFI / 4G)与状态位图解析4.5 协议支持位图解析:e_linker_protocol_support_state()(第 1005-1031 行)4.6 身份与 P1 数据读取4.7 AT 命令的底层帧格式与超时机制4.8 与 uCOS-II 任务/信号量的配合4.9 关键全局变量的读写汇总5. 对外接口本文件导出(供外部调用)本文件调用的外部函数/变量6. 依赖关系7. 备注与风险点源文件路径:
midware/ethernt/wireless/wifi/e_linker_wifi.c所属模块:midware/ethernt/wireless/wifi(易联/自研 WIFI 通讯棒驱动) 代码行数:1071
1. 文件职责
本文件实现 AlphaESS EMS 主控对"易联(E-LINKER)WIFI 通讯棒 / 4G 通讯棒"的全部应用层 AT 指令交互,是 EMS 无线上云通道的设备侧驱动。
它向上为 wireless_driver.c 提供四类能力:登录握手(e_linker_wifi_login)、身份读取(get_e_linker_wifi_info/get_e_linker_sn)、运行状态探测(e_linker_wifi_status_detect/e_linker_4g_status_detect/e_linker_protocol_support_state),以及 P1 采集器数据读取(e_linker_P1_data)。
数据流位置:wireless_status_detect()(周期任务)→ 本文件的 AT 命令函数 → at_execute_command()(elink_parse.c)→ USART5 收发 → 通讯棒;返回结果写入 g_t_wireless_info(无线运行总表)、g_wifi_state/g_4g_state(状态位图)、g_e_linker_info(棒身份)。
最核心的一环是 e_linker_wifi_init():它把通讯棒的 AP SSID、目标域名、目标端口、设备密钥/密码四组配置与 EMS 本地配置逐项比对,任何一项不一致就打标记,最后统一写回并硬重启通讯棒,保证棒内配置与 EMS 当前服务器设置同步。
本文件不直接操作 USART5 寄存器,也不实现字节流的状态机,这两部分分别由 wireless_driver.c(上电/重启)和 elink_parse.c(at_execute_command/recv_wifi_log)承担。
2. 关键数据结构与宏
| 名称 | 类型 | 说明 |
|---|---|---|
E_LINKER_COMM_DEBUG | 宏 | 定义于 e_linker_wifi.h:7,默认 0;控制本文件所有 AT 交互的 printf |
E_LINKER_4G_COMM_DEBUG | 宏 | 本文件第 153、593 行使用,但全工程无定义,预处理按 0 求值,4G 调试打印永久关闭 |
WIFI_STATE | 联合体 | e_linker_wifi.h:9-17;l_u8_wifi_state 整字节,位域 l_u8_APP_login(bit0)、l_u8_route(bit1) |
elinker_4g_state | 联合体 | e_linker_wifi.h:19-33;位域 u8_sim_pin_state/u8_net_login_state/u8_gprs_state/u8_tcp_state/u8_sta_state/u8_ap_state 共 6 位有效 |
g_wifi_state | 全局变量(本文件定义) | 第 24 行;WIFI 棒连接状态快照,由 e_linker_wifi_state() 写入 |
g_4g_state | 全局变量(本文件定义) | 第 25 行;4G 棒连接状态快照,由 e_linker_4g_state() 写入 |
AT_GET_STATE / AT_GET_C0_LINK / AT_GET_4G_COM / AT_GET_4G_CSQ / AT_GET_STA_EIRP | 宏(AT 命令字) | 定义于 elink_parse.h:36-41,分别为 AT+WIFI_STATE?、AT+C0_LINK?、AT+4G_COM?、AT+CSQ?、AT+STA_EIRP? |
AT_GET_AP_SSID / AT_GET_C0_DSTADDR0 / AT_GET_C0_DSTPORT0 / AT_GET_PWD / AT_GET_PROTOCOL | 宏(AT 命令字) | elink_parse.h:44-48 |
AT_SET_AP_SSID / AT_SET_C0_DSTADDR0 / AT_SET_C0_DSTPORT0 / AT_SET_PWD / AT_SET_ALGORITHM_KEY | 宏(AT 命令字前缀) | elink_parse.h:50-54,AT+SNKEYSET= 用于一机一密密钥下发 |
AT_GET_SN / AT_GET_HW / AT_GET_SW | 宏(AT 命令字) | elink_parse.h:32-34 |
AT_P1_DATA / AT_P1_STATE | 宏(AT 命令字) | elink_parse.h:60-61,AT+P1E_DATA?、AT+SUBDEV_STATE? |
AT_DEFAULT_PWD | 宏 | midware/ethernt/ethernet.h:22,值为 "12345678";不支持 TLS 的模块使用该默认密码 |
ALPHAESS_WIFI_SSID_SUFFIX / E_LINKER_WIFI_SSID_SUFFIX | 宏 | ethernet.h:134-135,分别 "AW"、"EL",用于拼接 AP SSID |
ALPHA_GENERATE_PWD_LENGTH | 宏 | common/alpha_generate_pwd.h:6,值为 9(含 \0),password_buf 的容量 |
MAX_E_LINKER_SN_LEN | 宏 | wireless_driver.h:16,值为 16,SN 长度上限 |
MAX_E_LINKER_P1_LEN | 宏 | wireless_driver.h:17,值为 75,P1 数据帧最小有效长度 |
RECV_AT_CODE_TIMEOUT / SEND_AT_CODE_TIMEOUT | 宏 | elink_parse.h:26-27,1500 / 100 tick(OS_TICKS_PER_SEC=1000,即 1.5s / 100ms) |
SENCOND_3S | 宏 | net/ems.h:26,3000 tick,e_linker_sta_eirp() 的 AT 超时 |
PROTOCOL_TYPE_E | 联合体 | wireless_driver.h:36-50;本文件按位写入其 u8_all:bit0 纯 TCP、bit1 TCP+TLS、bit2 MQTT、bit3 MQTTS |
WIRELESS_TYPE_E | 枚举 | wireless_driver.h:24-34;本文件使用 NONE、E_LINKER_WIFI、ALPHA_LINKER_WIFI 三个取值 |
3. 函数清单
本文件共 25 个函数定义(另有 19 行前置声明,位于第 29-47 行)。
| 函数名 | 作用域 | 说明(一句话) |
|---|---|---|
e_linker_4g_status_detect | 全局(第 54 行) | 探测 4G 棒状态并刷新 g_t_wireless_info 链路/TCP 标志与 App 连接标志,连续 6 次失败则判定模块丢失 |
e_linker_wifi_status_detect | 全局(第 88 行) | 探测 WIFI 棒状态并刷新上述标志,连续 6 次失败则判定模块丢失 |
e_linker_sta_eirp | 全局(第 123 行) | 执行 AT+STA_EIRP? 取信号强度,成功时把 ASCII 结果 atoi 为 s8 返回,失败返回 FALSE(0) |
get_e_linker_4g_csq | 全局(第 147 行) | 执行 AT+CSQ? 并用 sscanf("CSQ:%d,%d") 解出 RSSI/BER,任何异常都回填 0/0 |
e_linker_wifi_init | 全局(第 180 行) | 通讯棒配置对齐总流程:协议探测→SSID/密钥/端口/域名比对→按需写回→wireless_restart() |
e_linker_wifi_login | 全局(第 477 行) | 以调用方给定的登录 AT 命令执行一次握手,最多 2 次,成功后清接收缓冲 |
get_e_linker_wifi_info | 全局(第 509 行) | 依次读 SN / SW / HW,每项最多重试 3 次,任一失败立即返回 FALSE |
e_linker_4g_state | static(第 585 行) | 执行 AT+4G_COM? 并 sscanf 6 个字段填充 g_4g_state 位域 |
e_linker_wifi_state | static(第 630 行) | 执行 AT+WIFI_STATE?,用 strstr(",AP:") 定位并拼出 g_wifi_state.l_u8_wifi_state |
e_linker_tcp_state | static(第 666 行) | 执行 AT+C0_LINK?,返回串首字符为 '1' 即认为 TCP 已连 |
e_linker_P1_data | 全局(第 694 行) | 执行 AT+P1E_DATA? 把 P1 数据帧读入调用方缓冲区,长度 > 75 才算成功 |
get_e_linker_ap_ssid | static(第 732 行) | 读 AP SSID,长度 > 32 判失败 |
get_e_linker_c0_dst_addr0 | static(第 756 行) | 读 C0 通道目标域名/IP,长度 > 50 判失败 |
get_e_linker_c0_dst_port0 | static(第 780 行) | 读 C0 通道目标端口,长度 > 5 判失败,否则 atoi 写入 *port |
set_e_linker_ap_ssid | static(第 808 行) | 拼接 AT+AP_SSID=<ssid>\r\n 并下发 |
set_e_linker_c0_dst_addr0 | static(第 836 行) | 拼接 AT+C0_DSTADDR0=<addr>\r\n 并下发 |
set_e_linker_c0_dst_port0 | static(第 864 行) | 拼接 AT+C0_DSTPORT0=<port>\r\n 并下发 |
get_e_linker_sn | 全局(第 892 行) | 读通讯棒 SN,长度 > MAX_E_LINKER_SN_LEN(16) 判失败 |
get_e_linker_hw | static(第 916 行) | 读硬件版本,长度 > 15 判失败 |
get_e_linker_sw | static(第 937 行) | 读软件版本,长度 > 15 判失败 |
get_e_linker_wifi_pwd | static(第 957 行) | 读设备密码,空串判失败(设备参数不完整) |
set_e_linker_wifi_pwd | static(第 980 行) | 拼接 AT+DEV_PWD=<pwd>\r\n 并下发 |
e_linker_protocol_support_state | 全局(第 1005 行) | 执行 AT+PROTOCOL?,按逗号分段解析协议位图写入 g_t_wireless_info.protocol_support_state.u8_all |
set_e_linker_key_algorithm | static(第 1038 行) | 拼接 AT+SNKEYSET=1,<SN>,<pwd>\r\n 下发一机一密密钥 |
ap_ssid_stitching | static(第 1060 行) | 按通讯棒类型与 TLS 支持情况拼接 AP SSID 字符串,返回静态缓冲区指针 |
4. 核心逻辑解析
4.1 通讯棒登录:e_linker_wifi_login()(第 477-501 行)
本文件不承担"识别是哪种棒"的职责,识别由 wireless_driver.c:308-360 的 wireless_module_find() 完成,它按顺序用三条不同 AT 命令调用本函数尝试登录:
| 顺序 | 命令 | 判定为 |
|---|---|---|
| 1 | AT_GET_4G_COM(AT+4G_COM?) | E_LINKER_4G |
| 2 | AT_ALPHE_LOGIN_E(AT+LOGIN=ALPHA_LINKER) | ALPHA_LINKER_WIFI(自研棒) |
| 3 | AT_E_LOGIN_E(AT+LOGIN=E-LINTER) | E_LINKER_WIFI(易联棒) |
e_linker_wifi_login() 内部是 do { ... } while(--loop),loop 初值 0x02(第 480 行),即最多执行 2 次;注释说明"检测登录2次防止发送错误信息导致模块报错"。无论成功或失败,返回前都会调用 e_linker_clear_buf() 清空 USART5 接收缓冲。注意 do/while 是先执行后判断,所以即使第一次就成功也会 return TRUE,不会重复发送。
4.2 配置对齐主流程:e_linker_wifi_init()(第 180-469 行)
这是本文件最长、也最关键的函数,被 wireless_module_find() 在识别成功后立即调用(wireless_driver.c:323,341,354)。整体分两个阶段。
阶段一(第 194-368 行):读回比对,只置标志不写模块。
外层是一个 while(1) 循环,但循环体末尾第 367 行有一句 break,因此实际只执行一次,循环外壳属于历史遗留(后续任何 return FALSE 都是直接函数返回)。按执行顺序:
e_linker_clear_buf()清串口缓冲(第 194 行)。- 协议探测(第 199-206 行):
for(i=0;i<3;i++)最多重试 3 次调用e_linker_protocol_support_state()。结果同时写入g_t_wireless_info.u8_wifi_support_P1_falg(第 209 行)——只有支持该协议的易联棒才支持 P1 抄表。 - 类型降级(第 211-214 行):若协议探测失败且类型为
ALPHA_LINKER_WIFI,降级为E_LINKER_WIFI。 - AP SSID 比对(第 217-241 行):读回 SSID 与
ap_ssid_stitching(...)期望值strcmp;不一致则l_u8_module_set_flag = TRUE且l_u8_pwd_restore_set_flag = TRUE(SSID 变了意味着是换机/换棒,密码需一并恢复)。 - 密钥算法(第 243-261 行):若
protocol_support_state.bit.u8_tcp_tls_support_flag == TRUE,认为支持 TLS 即支持一机一密,用alpha_generate_pwd(get_system_sn(), password_buf)由系统 SN 生成 8 位密码,再通过set_e_linker_key_algorithm(get_system_sn(), password_buf)下发(最多 3 次);否则password_buf用AT_DEFAULT_PWD("12345678")填充。 - 目标端口比对(第 264-288 行):期望值来自
get_ethernet_server_port(WIRELESS, tls_flag),与棒内读回值不等则置l_u8_module_set_flag。 - 目标域名比对(第 291-315 行):TLS 时期望
wireless_get_ethernet_server_point(get_ethernet_server_point(), get_server_data_encryption_flag()),非 TLS 时期望get_ethernet_server_point();不等则置l_u8_module_set_flag。 - 自研棒短路(第 317-322 行):若类型为
ALPHA_LINKER_WIFI,直接清掉两个密码相关功能位后break——自研棒不参与读写密码流程。 - 密码读回(第 325-365 行):
- 读失败时(第 335-345 行):若与
g_t_system_base_info.s8_e_link_password不同则把期望密码写回该全局并置g_t_config_save_flag.u8_set_sys_base_para_flag = TRUE(要求掉电存储落盘);同时两个密码功能位清FALSE。 - 读成功时(第 346-365 行):若
l_u8_pwd_restore_set_flag == TRUE或本地密码为空,则用期望值覆盖本地并置落盘标志;若棒内密码与本地不一致则置l_u8_module_set_flag;两个密码功能位置TRUE。
- 读失败时(第 335-345 行):若与
break(第 367 行)退出循环。
阶段二(第 370-468 行):按需写回并重启。
若 l_u8_module_set_flag == FALSE,说明棒内配置已与 EMS 一致,直接 return TRUE(第 370-373 行),不重启通讯棒。
否则依次执行四组写操作,每组都是 for(i=0;i<3;i++) 最多 3 次重试,任一失败立即 return FALSE:
| 顺序 | 行号 | 写操作 | 值来源 |
|---|---|---|---|
| 1 | 375-393 | set_e_linker_ap_ssid() | ap_ssid_stitching(tls_flag, wireless_type, get_system_sn()) |
| 2 | 395-414 | set_e_linker_c0_dst_port0() | get_ethernet_server_port(WIRELESS, tls_flag),同时写入 g_stNetSysInfo.u16_skip_port |
| 3 | 416-437 | set_e_linker_c0_dst_addr0() | TLS 走 wireless_get_ethernet_server_point(...),否则 get_ethernet_server_point() |
| 4 | 439-461 | set_e_linker_wifi_pwd() | g_t_system_base_info.s8_e_link_password;仅当两个密码功能位有一个为 TRUE 时才执行 |
最后第 463 行 wireless_restart() 硬重启通讯棒,使新配置生效。
4.3 AP SSID 拼接规则:ap_ssid_stitching()(第 1060-1070 行)
使用 static char ap_ssid[19](含结尾 \0),每次进入先 memset 清零,三条分支:
ALPHA_LINKER_WIFI(自研棒) → <system_sn>"AW" // ALPHAESS_WIFI_SSID_SUFFIX
E_LINKER_WIFI 且 TLS 支持 → <system_sn>"EL" // E_LINKER_WIFI_SSID_SUFFIX
其余(易联且不支持 TLS) → <system_sn> // 无后缀
返回静态缓冲区指针,调用方不得保存跨调用使用。缓冲区 19 字节的边界由系统 SN 长度决定,见第 7 节风险点。
4.4 状态探测(WIFI / 4G)与状态位图解析
e_linker_wifi_state()(第 630-657 行) 执行 AT+WIFI_STATE? 后,用 strstr((char *)pdata, ",AP:") 定位 ,AP: 子串:
res = (*(addr-1) - 0x30); // 逗号前一位数字
g_wifi_state.l_u8_wifi_state = ((*(addr+4) - 0x30) | res << 1);
即把 addr-1 位置的字符放在 bit1(对应 l_u8_route),addr+4 位置的字符放在 bit0(对应 l_u8_APP_login)。这是把 ASCII 数字 '0'/'1' 减 0x30 转成位值的典型写法,但对返回串的格式做了硬编码偏移假设。找不到 ,AP: 时返回 FALSE。
e_linker_4g_state()(第 585-623 行) 执行 AT+4G_COM?,用一次 sscanf 取 6 个十进制字段,按序写入 u8_sim_pin_state → u8_net_login_state → u8_gprs_state → u8_tcp_state → u8_sta_state → u8_ap_state。只有 6 个字段全部解出才返回 TRUE。
e_linker_tcp_state()(第 666-686 行) 执行 AT+C0_LINK?,仅判断 pdata[0] == '1'。
e_linker_wifi_status_detect()(第 88-115 行)——WIFI 棒状态巡检:
if (e_linker_wifi_state() == TRUE) // 探测成功
l_u8_link_cnt = 0; // 清零失败计数
g_t_wireless_info.u8linkstatus = g_wifi_state.state.l_u8_route; // 是否连上 AP
g_t_wireless_info.u8tcpstatus = e_linker_tcp_state(); // 是否连上云服务器
if (App 未连接 && tcpstatus == FALSE)
g_t_app_info.u8_app_connect_flag = g_wifi_state.state.l_u8_APP_login;
g_t_app_info.u32_app_connect_tick = OSTimeGet();
else // 探测失败
if (++l_u8_link_cnt >= 6) // 连续 6 次失败
l_u8_link_cnt = 0;
g_t_wireless_info.u8_wireless_type = NONE; // 判定通讯棒已拔除/失联
g_t_wireless_info.u8_wireless_found = FALSE;
关键点:失败计数用函数内 static u8 l_u8_link_cnt(第 90 行)实现,所以"连续 6 次"是跨调用累积的——该函数由 wireless_driver.c:211(模块重新识别分支)与 wireless_driver.c:252(每 300s 例行检查,SENCOND_300S)两条路径调用。一旦置 NONE,wireless_status_detect() 会转入重新识别流程。计数器未做上溢保护,理论上连续失败 256 次后回绕(因判定条件 >= 6 会提前清零,实际不可达)。
e_linker_4g_status_detect()(第 54-81 行) 结构相同,差别有二:
u8linkstatus = ((g_4g_state.state.u8_sta_state == TRUE) || (g_4g_state.state.u8_sim_pin_state == TRUE))——即"基站已注册 或 SIM 卡在位"都算链路通,条件比 WIFI 侧宽松,且用||把两个独立状态合并成一个布尔,丢失了"SIM 在位但未注册"的区分能力。- App 连接标志取
g_4g_state.state.u8_ap_state。
两个 detect 函数都只在 u8_app_connect_flag == FALSE && u8tcpstatus == FALSE 时才更新 g_t_app_info.u8_app_connect_flag 与 u32_app_connect_tick,避免覆盖上层已确认的连接结果。
4.5 协议支持位图解析:e_linker_protocol_support_state()(第 1005-1031 行)
进入时先把 g_t_wireless_info.protocol_support_state.u8_all = 0 清零(第 1009 行),然后用一次线性扫描解析 AT+PROTOCOL? 的返回串:
for (u8 *p = pdata; *p != '\0'; p++)
{
if (*p == ',') l_u8_delim_tick++; // 逗号计数 = 字段序号
else if (*p == '1') g_t_wireless_info.protocol_support_state.u8_all |= (1 << l_u8_delim_tick);
if (l_u8_delim_tick > 7) break;
}
即"第 N 个逗号之后的字段为 1 则置 bit N",与 PROTOCOL_TYPE_E 的位定义对应:bit0 纯 TCP、bit1 TCP+TLS、bit2 MQTT、bit3 MQTTS。扫描到第 8 个逗号(l_u8_delim_tick == 8)时退出。注意返回值恒为 TRUE(只要 AT 命令得到应答),因此上层 e_linker_wifi_init() 第 209 行的 u8_wifi_support_P1_falg 反映的是"模块有应答"而非"支持某协议"。
4.6 身份与 P1 数据读取
get_e_linker_wifi_info()(第 509-578 行)是三段式串行读取:SN(MAX_E_LINKER_SN_LEN+1 清零,长度上限 16)→ SW(清零 15 字节)→ HW(清零 15 字节),每段 for(i=0;i<3;i++) 最多 3 次。任一环节 3 次都失败就立即返回 FALSE,已读到的部分结果仍留在调用方缓冲区(g_e_linker_info 各字段),上层不判断返回值的场景会用到半成品数据。
e_linker_P1_data()(第 694-724 行)执行 AT+P1E_DATA?,把数据写进调用方缓冲区(wireless_driver.c:438 提供 u8 l_u8_pdata[170]),成功判据是 strlen(pdata) > MAX_E_LINKER_P1_LEN(75),即短于 75 字节的应答视为无效帧。读到的原始 CSV 串随后由 wireless_driver.c:509 用一条 24 字段的 sscanf 解析成电表 SN、版本、P1 周期、正反向电量等。本函数不校验帧内容,也不确认首字节是否为有效分隔符。
4.7 AT 命令的底层帧格式与超时机制
本文件所有 AT 交互都收口到 at_execute_command(cmd, pdata, timeout)(定义在 midware/ethernt/wireless/elink_parse.c:191),因此实际帧格式与超时行为由该层决定:
发送帧格式(ASCII 文本,非二进制):AT+<命令>[=<参数>]\r\n。参数拼接由本文件的 set_e_linker_*() 系列用 sprintf 完成,统一追加 "\r\n" 结尾,无长度域、无命令字校验字段;AT_SET_ALGORITHM_KEY 的参数是 1,<SN>,<pwd> 三段逗号分隔。
接收帧格式:at_execute_command() 先 e_linker_clear_buf() 清 USART5 缓冲,再 OSSemSet(g_t_uart5_rcv_sem, 0, &err) 清接收信号量,然后 e_linker_send(cmd, strlen(cmd), SEND_AT_CODE_TIMEOUT)(100 tick)发送,最后 recv_wifi_log(pdata, timeout) 等应答。
recv_wifi_log()(elink_parse.c:100)以 OSSemPend(g_t_uart5_rcv_sem, timeout, &err) 阻塞等待串口中断给出的信号量,然后跑一个四态行解析状态机:
| 状态 | 迁移条件 | 动作 |
|---|---|---|
AT_RECV_HEAD | 收到 '\n' | 转 READ_DATE |
READ_DATE | 收到 '\r' → 转 DATA_LF_MSG;其它字符 | 逐字节存入 buf[idex],idex++ |
DATA_LF_MSG | 收到 '\n' | 比较整行是否为 "ERROR" 或 "OK",是则清缓冲并直接返回 AT_RECV_ERROE/AT_RECV_OK |
MSG_DATA | 收到 'O' / 'E' / 0x00 | 分别返回 AT_RECV_OK / AT_RECV_ERROE / AT_RECV_OK |
行内超时保护:进入循环时记录 tick_start_temp = OSTimeGet(),之后每轮判断 (OSTimeGet() - tick_start_temp) >= RECV_AT_CODE_TIMEOUT(1500 tick = 1.5s)即 break。循环内若无数据可读会 OSTimeDlyHMSM(0,0,0,50) 让出 50 tick(原码写 50 而非 SENCOND_...),避免空转。
at_execute_command() 最终的成败判定优先级为:应答串含 "Login Error" → 失败;含 "OK" → 成功;否则若状态机返回 AT_RECV_OK → 成功;其余失败。这一顺序使得"登录失败"不会被误判成功。
超时参数一览:发送 100 tick;一般读命令 1500 tick;e_linker_sta_eirp() 用 SENCOND_3S(3000 tick = 3s,因为取 EIRP 较慢)。e_linker_send() 内部还额外保证两帧之间至少间隔 100 tick,不足时 OSTimeDlyHMSM 补足——这对应 wireless_driver.h:75 注释"WIFI 串口每个发送帧间隔至少 30ms,以确保通讯棒能把数据分开"。
4.8 与 uCOS-II 任务/信号量的配合
本文件不创建任务,全部函数都由已有的任务上下文同步调用:
- 主要调用者
wireless_status_detect()位于无线巡检任务,通过g_t_wireless_info.u8_wireless_uart_flag做串口互斥:进入前置TRUE占用,末尾置FALSE释放;同时用u32_WIFI_uart_tick限制串口活动频率。 - 本文件自身不检查
u8_wireless_uart_flag,互斥完全依赖上层。e_linker_wifi_init()会连续发送十几条 AT 命令,期间一直占用串口,若被 P1 读取路径插入会互相打断——虽然wireless_driver.c:484-491的入口条件做了规避(要求u8_wireless_uart_flag == FALSE且距上次串口活动 ≥ 30ms,wireless_driver.c:493才置占用标志),但该保护是"检查后置位"的非原子序列。 - 信号量只用到
g_t_uart5_rcv_sem(USART5 接收完成信号量),由elink_parse.c的at_execute_command()/recv_wifi_log()独占操作;本文件通过e_linker_clear_buf()间接清 USART5 环形缓冲。 - 时间基准
OSTimeGet()在本文件的第 68、101 行用于打u32_app_connect_tick时间戳,在elink_parse.c中用于超时。
4.9 关键全局变量的读写汇总
| 全局变量 | 本文件的读写点 | 作用 | ||
|---|---|---|---|---|
g_t_wireless_info.u8linkstatus | 写:第 62、95 行 | 无线链路是否通(WIFI 用 route,4G 用 sta\ | \ | sim) |
g_t_wireless_info.u8tcpstatus | 写:第 63、96 行 | 是否连上云服务器 | ||
g_t_wireless_info.u8_wireless_type | 写:第 77、110、213 行 | 失联后置 NONE;协议不支持时 ALPHA_LINKER_WIFI→E_LINKER_WIFI | ||
g_t_wireless_info.u8_wireless_found | 写:第 78、111 行 | 失联后清发现标志,触发重新识别 | ||
g_t_wireless_info.u8_wifi_support_P1_falg | 写:第 209 行 | 是否支持 P1 抄表 | ||
g_t_wireless_info.protocol_support_state.u8_all | 写:第 1009、1020 行 | 协议支持位图 | ||
g_t_app_info.u8_app_connect_flag / u32_app_connect_tick | 写:第 67-68、100-101 行 | App 已连接的判定与时间戳 | ||
g_wifi_state.l_u8_wifi_state | 写:第 645 行 | WIFI 状态位图 | ||
g_4g_state.state.* | 写:第 606-611 行 | 4G 六个状态位 | ||
g_stNetSysInfo.u16_skip_port | 写:第 283、397 行 | 无线通道应使用的服务器端口 | ||
g_t_system_base_info.s8_e_link_password | 读:第 337、351、358、444 行;写:第 339-340、353-354 行 | 与通讯棒对齐的设备密码,落盘保存 | ||
g_t_config_save_flag.u8_set_sys_base_para_flag | 写:第 341、355 行 | 请求把系统基础参数写入掉电存储 | ||
g_system_function_info.u_system_function1.bit.u8_e_linker_wifi_pwd | 写:第 319、343、363 行 | 通讯棒是否支持改密码(自研棒/读失败时清 0) | ||
g_system_function_info.t_dev_fun.ems_fun_u.bit.u8_wifi_password_change | 写:第 320、344、364 行 | 云端"改 WIFI 密码"功能是否可用 |
5. 对外接口
本文件导出(供外部调用)
| 导出项 | 声明位置 | 调用者 |
|---|---|---|
g_wifi_state / g_4g_state | e_linker_wifi.c:24-25 | wireless_driver.c 读状态位 |
e_linker_wifi_status_detect() | 第 88 行 | wireless_driver.c:211, 252 |
e_linker_4g_status_detect() | 第 54 行 | wireless_driver.c:217, 256 |
e_linker_sta_eirp() | 第 123 行 | wireless_driver.c:266 |
get_e_linker_4g_csq() | 第 147 行 | wireless_driver.c:275 |
e_linker_wifi_init() | 第 180 行 | wireless_driver.c:323, 341, 354 |
e_linker_wifi_login() | 第 477 行 | wireless_driver.c:318, 336, 349 |
get_e_linker_wifi_info() | 第 509 行 | wireless_driver.c:209, 216 |
e_linker_P1_data() | 第 694 行 | wireless_driver.c:500 |
get_e_linker_sn() | 第 892 行 | midware/debug/shell_usmart.c:21(USMART 命令表) |
e_linker_protocol_support_state() | 第 1005 行 | midware/debug/shell_usmart.c:22 |
仅本文件内部使用的 static 函数共 15 个:e_linker_wifi_state、e_linker_tcp_state、set_e_linker_ap_ssid、set_e_linker_c0_dst_addr0、set_e_linker_c0_dst_port0、get_e_linker_ap_ssid、get_e_linker_c0_dst_addr0、get_e_linker_c0_dst_port0、get_e_linker_hw、get_e_linker_sw、get_e_linker_wifi_pwd、set_e_linker_wifi_pwd、set_e_linker_key_algorithm、e_linker_4g_state、ap_ssid_stitching(对应第 29-47 行的 static 前置声明)。它们与 e_linker_wifi.h 的对外契约无关。
本文件调用的外部函数/变量
| 外部项 | 来源模块 |
|---|---|
at_execute_command() | midware/ethernt/wireless/elink_parse.c:191(声明 elink_parse.h:70) |
e_linker_clear_buf() | elink_parse.c:82 |
AT_* 命令字宏、RECV_AT_CODE_TIMEOUT | elink_parse.h |
g_t_wireless_info、g_e_linker_info、MAX_E_LINKER_*、WIRELESS_TYPE_E、PROTOCOL_TYPE_E | midware/ethernt/wireless/wireless_driver.h |
wireless_restart()、wireless_get_ethernet_server_point() | wireless_driver.c / ethernet.c |
get_ethernet_server_port()、get_ethernet_server_point()、get_server_data_encryption_flag()、g_stNetSysInfo、AT_DEFAULT_PWD、ALPHAESS_WIFI_SSID_SUFFIX、E_LINKER_WIFI_SSID_SUFFIX | midware/ethernt/ethernet.h / ethernet.c |
alpha_generate_pwd() | common/alpha_generate_pwd.c |
get_system_sn() | net/server.c(声明 net/server.h:272) |
g_t_app_info、SENCOND_3S、SENCOND_30MS 等 | net/ems.h |
g_t_system_base_info、g_t_config_save_flag | data/system_config.* |
g_system_function_info | data/system_config.* |
OSTimeGet() | uCOS-II |
atoi/sscanf/sprintf/strstr/strcmp/strlen/memcpy/memset | C 标准库 |
6. 依赖关系
依赖(include,第 12-20 行):
e_linker_wifi.h、elink_parse.h、includes.h(uCOS-II 与公共类型)、wireless_driver.h、ems.h(g_t_app_info、SENCOND_*)、ethernet.h(服务器地址/端口、SSID 后缀、默认密码)、system_config.h(g_t_system_base_info/g_config_save_flag)、modbus.h、alpha_generate_pwd.h。
被依赖:
midware/ethernt/wireless/wireless_driver.c——唯一的生产调用者(状态探测、初始化、登录、P1 读取、EIRP/CSQ)。midware/ethernt/wireless/wireless_driver.h中注释指明"全部函数在wireless_driver.c与wifi/e_linker_wifi.c中",即无线驱动层对本文件有强依赖。midware/debug/shell_usmart.c——把get_e_linker_sn、e_linker_protocol_support_state注册为调试命令。- 间接影响
midware/ethernt/ethernet.c(g_t_wireless_info.u8linkstatus/u8tcpstatus决定能否走无线通道上云)与net/ems.c(g_t_app_info.u8_app_connect_flag)。
7. 备注与风险点
e_linker_wifi_state()对返回串做硬编码偏移解析(第 641-645 行)
addr = strstr(pdata, ",AP:"); res = *(addr-1) - 0x30; 直接用指针加减取字符。若返回串形如 ",AP:..." 恰好在缓冲区首部,addr-1 会越界读到缓冲区之外(pdata[18] 栈数组前的字节);若模块返回 ,AP: 后不足 4 个字符,*(addr+4) 同样会越界。代码没有做长度校验,只有 strstr 找到与没找到两种判定。
recv_wifi_log()写入无长度上限,存在缓冲区溢出风险(elink_parse.c:139-143)
(*(buf+idex)) = byte; idex++; 对 idex 不做任何边界检查,而本文件传给 at_execute_command() 的接收缓冲区多数只有 18 字节(如第 587、632、668、782、839、867、1007 行的 u8 pdata[18])。若通讯棒返回超长行(固件异常、噪声、或登录错误页),会直接写穿栈缓冲区。idex 是 u16,也不会因回绕而提前中止。改为按缓冲区长度传参是最必要的加固点。
sprintf拼 AT 命令缓冲区偏小(第 810、838、866、982、1040 行)
除 set_e_linker_key_algorithm()(char temp[44],写 AT+SNKEYSET=1,<SN>,<pwd>,SN 最长 16 + 密码 8 + 固定 15 字节约 40,接近边界)外,其余 set_e_linker_*() 均为 u8 temp[44]。get_e_linker_c0_dst_addr0() 允许域名长达 50 字节,set_e_linker_c0_dst_addr0() 要用 sprintf 拼 "AT+C0_DSTADDR0="(15) + 域名 + "\r\n",域名超过 27 字节即溢出 44 字节栈缓冲。读取侧校验上限(50)与写入侧缓冲区容量(44)不一致,存在真实溢出通路。
ap_ssid_stitching()的 19 字节静态缓冲同样偏紧(第 1062 行)
拼接 SN + "AW"/"EL",系统 SN 若为 16 字符加 2 字符后缀加 \0 刚好 19;若 SN 更长(get_system_sn() 未在本文件做长度校验)即溢出。该函数返回 static 指针,多线程/重入场景下会被覆盖。
while(1)+ 末尾break的死循环外壳(第 196、367 行)
循环体只执行一次,属于历史遗留结构。它的危险在于把"重试"语义留在了循环外壳里(现由内层 for(i=0;i<3;i++) 承担),后续若有人在体内增加 continue,会变成真正的死循环。
e_linker_protocol_support_state()的位偏移溢出(第 1020-1023 行)
g_t_wireless_info.protocol_support_state.u8_all |= (1 << l_u8_delim_tick); 先按 '1' 置位、后判断 if (l_u8_delim_tick > 7) break;。当 l_u8_delim_tick == 8 且当前字符恰为 '1' 时,会先执行 1 << 8(提升为 int 后按位或进 u8,结果为 0),再退出。结果虽然是无害的 0,但说明边界判断的位置比置位语句晚了一步。
u8_wifi_support_P1_falg的语义与命名不符(第 209 行)
e_linker_protocol_support_state() 只要有 AT 应答就返回 TRUE,与"支持某协议"无关。因此该标志实际含义是"模块有应答",而注释(wireless_driver.h:65)说它表示"支持 18031 版本的 P1 通信"。同一函数还承担了"协议不支持时把 ALPHA_LINKER_WIFI 降级为 E_LINKER_WIFI"的副作用(第 211-214 行),命名与职责都已经偏离。
- 两个密码功能位成对清/置但不配对(第 319-320、343-344、363-364 行)
u8_e_linker_wifi_pwd 与 u8_wifi_password_change 总是一起写相同的值,属于冗余状态位;由于二者都被云端与本地改密流程引用,若将来只改其中一处会产生状态不一致。
e_linker_sta_eirp()失败返回值与合法值冲突(第 123-140 行)
失败返回 FALSE(0),而 atoi 解析出的 EIRP 也可能为 0,调用方无法区分;另外 pdata[18] 装 AT 应答且 atoi 不做合法性校验,噪声串会得到 0。上层 wireless_driver.c:266-270 在链路不通时用 -99 表示无效,与这里的 0 表示法不统一。
e_linker_P1_data()用strlen判有效长度(第 707 行)
P1 帧是二进制/混合内容的定长数据,用 strlen 判长度依赖帧内不含 0x00;一旦帧中间出现 0x00,长度会被截短而误判为无效。另外其调试分支(第 699-703 行)固定读 pdata[0..99],虽然调用方缓冲区是 170 字节(wireless_driver.c:438)暂时安全,但函数本身并不知道缓冲区长度,属于接口隐患。
E_LINKER_4G_COMM_DEBUG未定义(第 153、593 行)
见 e_linker_wifi.h 分析:该宏全工程无定义,4G 调试打印永久失效,此处不会报错但也没有输出。
e_linker_wifi_status_detect()与e_linker_4g_status_detect()的 static 失败计数器(第 56、90 行)
计数器跨调用累积,但两个函数被 wireless_status_detect() 的三条不同分支调用(每 10s 的识别分支、每 300s 的例行分支),"连续 6 次失败"对应的实际时间跨度取决于哪条路径在跑,语义模糊。两个函数体几乎是复制粘贴的对称实现(仅状态来源不同),维护时容易只改一边。
#include "modbus.h"(第 19 行)在本文件中未见任何使用,属于冗余包含;includes.h这类聚合头也容易掩盖真实依赖。
- 文件头注释信息过时:第 3-5 行标注作者 "Alpha EMS Team"、版本 V1.0.0、日期 2017/08/6,但代码中已出现
for (u8 *p = ...)形式的 C99 声明与 ALPHA_LINKER(自研棒)分支,明显晚于该日期,头注释未随之更新。
get_e_linker_4g_csq()的类型混用(第 156-157 行):u32 csq[2]却用%d(int*)写入,e_linker_4g_state()同样把u32 l_u32_com_sta[6]转成(int *)交给%d(第 596-603 行)。在 32 位 ARM 上恰好等宽所以不炸,但属于依赖平台的不严谨写法。
暂无其它问题。