EMS 知识库
首页 / knowledge/代码解析/midware/ethernt/wireless/wifi

e_linker_wifi.c

源文件路径: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_statestatic(第 585 行)执行 AT+4G_COM? 并 sscanf 6 个字段填充 g_4g_state 位域
e_linker_wifi_statestatic(第 630 行)执行 AT+WIFI_STATE?,用 strstr(",AP:") 定位并拼出 g_wifi_state.l_u8_wifi_state
e_linker_tcp_statestatic(第 666 行)执行 AT+C0_LINK?,返回串首字符为 '1' 即认为 TCP 已连
e_linker_P1_data全局(第 694 行)执行 AT+P1E_DATA? 把 P1 数据帧读入调用方缓冲区,长度 > 75 才算成功
get_e_linker_ap_ssidstatic(第 732 行)读 AP SSID,长度 > 32 判失败
get_e_linker_c0_dst_addr0static(第 756 行)读 C0 通道目标域名/IP,长度 > 50 判失败
get_e_linker_c0_dst_port0static(第 780 行)读 C0 通道目标端口,长度 > 5 判失败,否则 atoi 写入 *port
set_e_linker_ap_ssidstatic(第 808 行)拼接 AT+AP_SSID=<ssid>\r\n 并下发
set_e_linker_c0_dst_addr0static(第 836 行)拼接 AT+C0_DSTADDR0=<addr>\r\n 并下发
set_e_linker_c0_dst_port0static(第 864 行)拼接 AT+C0_DSTPORT0=<port>\r\n 并下发
get_e_linker_sn全局(第 892 行)读通讯棒 SN,长度 > MAX_E_LINKER_SN_LEN(16) 判失败
get_e_linker_hwstatic(第 916 行)读硬件版本,长度 > 15 判失败
get_e_linker_swstatic(第 937 行)读软件版本,长度 > 15 判失败
get_e_linker_wifi_pwdstatic(第 957 行)读设备密码,空串判失败(设备参数不完整)
set_e_linker_wifi_pwdstatic(第 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_algorithmstatic(第 1038 行)拼接 AT+SNKEYSET=1,<SN>,<pwd>\r\n 下发一机一密密钥
ap_ssid_stitchingstatic(第 1060 行)按通讯棒类型与 TLS 支持情况拼接 AP SSID 字符串,返回静态缓冲区指针

4. 核心逻辑解析

4.1 通讯棒登录:e_linker_wifi_login()(第 477-501 行)

本文件不承担"识别是哪种棒"的职责,识别由 wireless_driver.c:308-360 的 wireless_module_find() 完成,它按顺序用三条不同 AT 命令调用本函数尝试登录:

顺序命令判定为
1AT_GET_4G_COM(AT+4G_COM?)E_LINKER_4G
2AT_ALPHE_LOGIN_E(AT+LOGIN=ALPHA_LINKER)ALPHA_LINKER_WIFI(自研棒)
3AT_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 都是直接函数返回)。按执行顺序:

  1. e_linker_clear_buf() 清串口缓冲(第 194 行)。
  2. 协议探测(第 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 抄表。
  3. 类型降级(第 211-214 行):若协议探测失败且类型为 ALPHA_LINKER_WIFI,降级为 E_LINKER_WIFI。
  4. AP SSID 比对(第 217-241 行):读回 SSID 与 ap_ssid_stitching(...) 期望值 strcmp;不一致则 l_u8_module_set_flag = TRUE 且 l_u8_pwd_restore_set_flag = TRUE(SSID 变了意味着是换机/换棒,密码需一并恢复)。
  5. 密钥算法(第 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")填充。
  6. 目标端口比对(第 264-288 行):期望值来自 get_ethernet_server_port(WIRELESS, tls_flag),与棒内读回值不等则置 l_u8_module_set_flag。
  7. 目标域名比对(第 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。
  8. 自研棒短路(第 317-322 行):若类型为 ALPHA_LINKER_WIFI,直接清掉两个密码相关功能位后 break——自研棒不参与读写密码流程。
  9. 密码读回(第 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。
  10. break(第 367 行)退出循环。

阶段二(第 370-468 行):按需写回并重启。

若 l_u8_module_set_flag == FALSE,说明棒内配置已与 EMS 一致,直接 return TRUE(第 370-373 行),不重启通讯棒。

否则依次执行四组写操作,每组都是 for(i=0;i<3;i++) 最多 3 次重试,任一失败立即 return FALSE:

顺序行号写操作值来源
1375-393set_e_linker_ap_ssid()ap_ssid_stitching(tls_flag, wireless_type, get_system_sn())
2395-414set_e_linker_c0_dst_port0()get_ethernet_server_port(WIRELESS, tls_flag),同时写入 g_stNetSysInfo.u16_skip_port
3416-437set_e_linker_c0_dst_addr0()TLS 走 wireless_get_ethernet_server_point(...),否则 get_ethernet_server_point()
4439-461set_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 行) 结构相同,差别有二:

两个 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 任务/信号量的配合

本文件不创建任务,全部函数都由已有的任务上下文同步调用:

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_statee_linker_wifi.c:24-25wireless_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_TIMEOUTelink_parse.h
g_t_wireless_info、g_e_linker_info、MAX_E_LINKER_*、WIRELESS_TYPE_E、PROTOCOL_TYPE_Emidware/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_SUFFIXmidware/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_flagdata/system_config.*
g_system_function_infodata/system_config.*
OSTimeGet()uCOS-II
atoi/sscanf/sprintf/strstr/strcmp/strlen/memcpy/memsetC 标准库

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。

被依赖:

7. 备注与风险点

  1. e_linker_wifi_state() 对返回串做硬编码偏移解析(第 641-645 行)

addr = strstr(pdata, ",AP:"); res = *(addr-1) - 0x30; 直接用指针加减取字符。若返回串形如 ",AP:..." 恰好在缓冲区首部,addr-1 会越界读到缓冲区之外(pdata[18] 栈数组前的字节);若模块返回 ,AP: 后不足 4 个字符,*(addr+4) 同样会越界。代码没有做长度校验,只有 strstr 找到与没找到两种判定。

  1. 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,也不会因回绕而提前中止。改为按缓冲区长度传参是最必要的加固点。

  1. 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)不一致,存在真实溢出通路。

  1. ap_ssid_stitching() 的 19 字节静态缓冲同样偏紧(第 1062 行)

拼接 SN + "AW"/"EL",系统 SN 若为 16 字符加 2 字符后缀加 \0 刚好 19;若 SN 更长(get_system_sn() 未在本文件做长度校验)即溢出。该函数返回 static 指针,多线程/重入场景下会被覆盖。

  1. while(1) + 末尾 break 的死循环外壳(第 196、367 行)

循环体只执行一次,属于历史遗留结构。它的危险在于把"重试"语义留在了循环外壳里(现由内层 for(i=0;i<3;i++) 承担),后续若有人在体内增加 continue,会变成真正的死循环。

  1. 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,但说明边界判断的位置比置位语句晚了一步。

  1. 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 行),命名与职责都已经偏离。

  1. 两个密码功能位成对清/置但不配对(第 319-320、343-344、363-364 行)

u8_e_linker_wifi_pwd 与 u8_wifi_password_change 总是一起写相同的值,属于冗余状态位;由于二者都被云端与本地改密流程引用,若将来只改其中一处会产生状态不一致。

  1. e_linker_sta_eirp() 失败返回值与合法值冲突(第 123-140 行)

失败返回 FALSE(0),而 atoi 解析出的 EIRP 也可能为 0,调用方无法区分;另外 pdata[18] 装 AT 应答且 atoi 不做合法性校验,噪声串会得到 0。上层 wireless_driver.c:266-270 在链路不通时用 -99 表示无效,与这里的 0 表示法不统一。

  1. e_linker_P1_data() 用 strlen 判有效长度(第 707 行)

P1 帧是二进制/混合内容的定长数据,用 strlen 判长度依赖帧内不含 0x00;一旦帧中间出现 0x00,长度会被截短而误判为无效。另外其调试分支(第 699-703 行)固定读 pdata[0..99],虽然调用方缓冲区是 170 字节(wireless_driver.c:438)暂时安全,但函数本身并不知道缓冲区长度,属于接口隐患。

  1. E_LINKER_4G_COMM_DEBUG 未定义(第 153、593 行)

见 e_linker_wifi.h 分析:该宏全工程无定义,4G 调试打印永久失效,此处不会报错但也没有输出。

  1. e_linker_wifi_status_detect() 与 e_linker_4g_status_detect() 的 static 失败计数器(第 56、90 行)

计数器跨调用累积,但两个函数被 wireless_status_detect() 的三条不同分支调用(每 10s 的识别分支、每 300s 的例行分支),"连续 6 次失败"对应的实际时间跨度取决于哪条路径在跑,语义模糊。两个函数体几乎是复制粘贴的对称实现(仅状态来源不同),维护时容易只改一边。

  1. #include "modbus.h"(第 19 行)在本文件中未见任何使用,属于冗余包含;includes.h 这类聚合头也容易掩盖真实依赖。
  1. 文件头注释信息过时:第 3-5 行标注作者 "Alpha EMS Team"、版本 V1.0.0、日期 2017/08/6,但代码中已出现 for (u8 *p = ...) 形式的 C99 声明与 ALPHA_LINKER(自研棒)分支,明显晚于该日期,头注释未随之更新。
  1. 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 上恰好等宽所以不炸,但属于依赖平台的不严谨写法。

暂无其它问题。