带 UART Wi-Fi 模块的微控制器不必自己实现 VPN 就能访问其后的服务。模块本身已经运行 TCP/IP 协议栈并通过串口提供套接字,同一局域网内的网关可以承载这些流量穿过隧道。只有能按顺序回答四个问题,这个设计才成立,因此请把它当作检查清单:地址、套接字、路由、边界。在第一个不通过的关口就停下,因为后面的关口无法弥补它。

为每一层分配一个职责

UART 链路在微控制器和模块之间传输 AT 命令与负载。模块加入 Wi-Fi 网络并拥有 IP 连接,包括其源地址。网关拥有隧道、凭据和转发策略。正是把这些职责分开,设计才在每个阶段都可调试。

这种分离还改变了网关策略的写法。源规则应当写明模块的局域网地址,而不是串口链路之后微控制器的虚构地址。在这个设计中,微控制器没有自己的 IP 身份,假装它有只会产生永远匹配不上的规则。

通过网络支持的任一预留机制为模块指定固定地址,或者把设备放在独立网段并让网关按接口匹配。重启后会变化的地址池地址,最终会破坏你当初写对的策略。

网关能说出模块的地址吗

在打开任何套接字之前,确认模块已关联、有地址,并且网关能在预期的接口上看到它。这是最容易静默失败的关口,因为模块会很乐意报告一次从未离开局域网的连接尝试。

AT+CWMODE=1
AT+CWJAP="lab-wifi","passphrase"
AT+CIPSTA?
AT+CIPMUX=0

单连接模式让命令序列保持简单,是一个传感器对一个服务说话的正确起点。多连接模式会改变之后每条命令的语法,所以从其他固件版本抄示例之前,先确定自己在哪种模式。

从模块打开套接字

地址和模式确定后,模块就能打开到私有服务的 TCP 连接。下面的地址是文档网段中的示例,请换成你自己的并保持相同形式。

AT+CIPSTART="TCP","10.90.0.20",9000
AT+CIPSEND=18
{"t":21.4,"rh":48}

那条 AT 命令打开的只是普通 TCP 套接字,而套接字不是隧道:其中没有任何东西创建 VPN,也没有任何东西加密负载。固件的 TLS 模式是独立功能,有自己的命令和自己的证书处理,而任意 UART 模块可能两者都不支持。请查阅你的固件的 TCP/IP AT 命令参考,不要假定上面的语法适用于它。

AT+CIPSEND 中的负载长度必须与实际发送的内容一致,否则模块会等待永远不会到来的字节,连接看起来就像卡住。这种故障看起来像网络问题,几乎总是长度上差了一个。

让连接经由网关转发

网关需要到私有目的地的路由、允许从设备网络转发进隧道的权限,以及能回到模块的返回路径。如果远端不回程路由到设备子网,设计就需要一条有意的转换规则,而这条规则会改变服务看到的源身份。

ip route get 10.90.0.20
sudo tcpdump -ni wg0 'host 10.90.0.20 and port 9000'
sudo conntrack -L | grep 9000

先观察返回路径。被转发到服务却永不返回的数据包会让模块超时,而网关上没有任何错误,这会让人盯着 Wi-Fi 链路看几个小时。conntrack 条目会告诉你网关到底有没有看到回应。

确定隧道究竟在哪里结束

隧道从网关开始。它不加密 UART 链路,也不会让模块到网关的局域网段成为隧道的一部分。该网段上任何需要保护的东西都需要自己的机制,而本例中的普通 TCP 并不是 TLS。

也要确定隧道掉线时会发生什么。如果投递必须只走 VPN,转发策略就不能允许同一目的地走备用路径,因为备用路由会悄悄让要求失效。限制重连行为,免得断开的模块用重试淹没串口链路或远端服务。

把这四个答案记在一起:模块的地址、套接字目标、网关路由,以及加密从哪里开始。下一个调试这台设备的人四处都需要,而且没有一处能只从固件推断出来。

相关文章