1. 串口服务器到底是个什么东西:一根DB9线怎么变成网络里的一台设备
先讲个我上个月刚碰到的现场。客户机房里有一台2011年出厂的精密空调,控制板背面只留了一个DB9母头,说明书早丢了,厂商的监控软件还是XP时代的产物,装在新电脑上直接报错。客户提的需求很朴素:想在中控室的电脑上实时看到这台空调的回风温度、压缩机状态,最好还能远程改一下设定温度。设备没问题、需求也不复杂,卡点就一个——那台空调不会说话,它只会往串口上吐字节。
这种情况下,串口服务器就是那个"翻译官"。它一头插着RS232或者RS485的串口线,另一头插一根普通的网线,把串口上跑的字节流原封不动地装进TCP报文里,再从网线那头吐出去。上位机不用再去找那根九针线,只要知道"某IP的某个端口"就够了。也就是说,串口服务器做的事情本质上是给一个没有网络能力的设备,发了一张网络身份证。
我写这篇东西的出发点,是想把这件事从一个"看得懂参数表"的层面,拉到"现场能调通、出了问题能查出来"的层面。因为我在现场见过太多次这种情况:设备买回来了,说明书翻了三遍,接线也没接错,但就是收不到数据;或者今天通了,明天重启一下又不行了。这些问题的根子往往不在设备质量,而在于对串口的电气特性、对TCP的连接模型、对两者之间那条"翻译规则"的理解有偏差。
这篇文章适合几类人看:一是刚接手工业数据采集项目的工程师,手上有一堆老设备要联网;二是做弱电集成、机房监控的技术人员,经常要对接UPS、空调、电表;三是做上位机软件的开发者,别人给了你一个IP和端口,但你不清楚底下发生了什么。不管你是哪一类,读完至少能做到:选型时知道该问供应商哪几个问题,调试时知道从哪一步开始查,出问题时知道先怀疑谁。
1.1 从一台只有九针口的老电表说起
很多人第一次接触这东西,是因为手上的设备"太老了"。老到没有以太网口,老到没有WiFi模块,甚至连USB都没有。这些设备当年设计的时候,联网这个概念本身就不存在于它的世界里,它只认三根线:收、发、地。
工业上这类设备非常多。配电室的多功能电表、机房的UPS、水处理厂的流量计、产线上的伺服驱动器、门禁控制板、称重仪表……它们有一个共同特点:通信协议简单、数据量小、但生命周期极长。一台电表能用十五年,可配套的电脑五年就得换一轮,中间这十年怎么衔接?换设备的成本远高于加一个转换器,于是串口服务器就有了它存在的空间。
我第一次拆开一台串口服务器的时候有点意外,里面的结构其实很朴素:一个主控芯片负责跑TCP/IP协议栈,一个UART控制器负责跟外部串口对话,中间是一段缓冲区加一段状态机代码。它没有操作系统,没有屏幕,就几十KB的固件,成本也压得很低。但正是这个朴素的结构决定了它的所有特性——它不会帮你解析协议,不会帮你理解业务,它只干一件事:把字节从一边搬到另一边,尽量不丢、不乱、不黏。
理解了这一点,后面的很多"怪现象"就能解释了。为什么它不认你的数据格式?因为它根本不看内容。为什么它一次只允许一个连接?因为它的缓冲区可能就几KB。为什么配置改完要重启?因为它的运行逻辑非常单薄。
1.2 拆解它内部的搬运逻辑:字节是如何钻进网线的
我们拿一个最典型的场景来还原。一台电表通过RS485接到串口服务器的A、B端子,串口服务器的网口连到交换机,中控室的电脑上跑着一个采集软件。
电表侧发生的事是:每隔一秒钟,电表主动吐出一帧数据,比如Modbus RTU格式,01 03 04 00 0A 00 0B xx xx,前面是地址和功能码,中间是数据,最后两字节是CRC校验。这一串字节以9600bps的速度,一个比特一个比特地从A、B这对差分线上发出来。串口服务器的UART收到完整一帧后,通过CRC判断这帧有没有出错,没错就塞进内部缓冲区。
网络侧发生的事是:串口服务器的协议栈从缓冲区里取出这些字节,加上TCP头、IP头、以太网帧头,从网口发出去。电脑上的软件通过socket收到这串字节时,它看到的只是一个TCP数据流,跟从网上下载文件相比没有任何区别。
关键点在于中间那一层缓冲区。它既是"救命的",也是"惹事的"。说它救命,是因为串口的速度(9600bps约等于960字节/秒)和网口的速度(100Mbps约等于12.5MB/秒)差了三个数量级,如果没有缓冲区做削峰填谷,稍微一点网络抖动就会导致数据全丢。说它惹事,是因为缓冲区一旦满了就会溢出丢数据,一旦排空策略不对就会把两帧数据粘在一起。
所以配置串口服务器时,有一个参数远比大多数人以为的重要,那就是分包策略(有的厂家叫打包长度、打包间隔、帧间隔时间)。它的意思是:串口侧收到的字节,是攒到多少个字节发一次,还是等多少毫秒没有新数据就发一次。这个参数设错了,上位机收到的数据要么被切成两半,要么两三帧挤在一起,解析代码立刻就报错。
1.3 三种工作模式的区别:谁先发起连接决定了整个架构
几乎所有的串口服务器都有三种模式可选,但很多人在配置时是随手点一个,能通就行。实际上这三种模式决定了完全不同的系统架构。
TCP Server模式是最常见的一种。串口服务器自己作为一个TCP服务端,监听某个端口(比如4001),等着电脑来连。电脑是主动方,串口服务器是被动方。这种模式的好处是串口服务器不需要知道电脑在哪,适合中控室电脑的IP可能变化、或者有多个电脑想轮流连的场景。缺点是,如果电脑不主动连,串口设备的数据就永远发不出去,它只能等。
TCP Client模式反过来,串口服务器主动去连一个固定的IP和端口。这种模式适合电脑在公网、串口设备在分散的现场,或者现场设备多、不想一个个去配电脑连接的情况。它的问题是:如果电脑没开机或者服务没起来,串口服务器的连接尝试会一直失败,需要它支持重连机制。另外,如果现场有几十台串口服务器同时连一台服务器,那台服务器需要有足够的连接承载能力。
UDP模式用的是无连接的数据报,不握手、不重传。它的优势是延迟低、开销小,适合高频上报、偶尔丢一两帧无所谓的数据;劣势是不保证到达、不保证顺序,也更容易被中间的网络设备拦截。我在做防水浸监测这种"上报频率高、单帧价值低"的项目时会用UDP,但在配置参数、抄表这类数据必须完整到达的场景,一律用TCP。
提示:模式选错是新手最常犯的错误,而且症状很迷惑——用ping能通,端口也开着,但就是没有任何数据。因为ping走的是ICMP,跟TCP模式对不对没有任何关系。查这个问题的第一件事,就是回到配置页面确认模式、目标IP、端口三者是否自洽。
1.4 把它当成一台"没有屏幕的电脑"来理解
我后来总结出一个比较好用的心智模型:把串口服务器当成一台没有操作系统、没有显示器的小电脑。它有IP、有端口、有缓冲区、有处理器,只不过它的"输入输出设备"是一对串口线。
这个模型能解释很多问题。比如,为什么它不能同时被两个上位机连接?因为一台电脑串口的独占性天然如此,两个程序同时读同一个串口会互相抢字节。比如,为什么它的配置改完要重启?因为很多低端型号的配置是存在Flash里,启动时读一次,运行中不会动态加载。比如,为什么它对网络延迟这么敏感?因为缓冲区就那么点大,串口侧还在源源不断地灌数据。
再比如,"虚拟串口"这件事也能解释得通。虚拟串口软件装在电脑上,它做的事就是:在系统里创建一个假的COM10,然后在上位机打开COM10的时候,偷偷把读写操作转换成对某IP某端口的TCP收发。对于那个XP时代的老软件来说,它完全不知道自己在走网络,它以为自己插着一根九针线。这是串口服务器落地时最省事的一条路——不用改一行代码,就把老软件送上了网。
2. 选型阶段就该定下来的事:口数、电气标准和环境耐受
买串口服务器这件事,最怕的是买回来发现"少一样东西"。少一个口、少一路隔离、少一个导轨卡扣,都会让你在现场很难受。而这些属性在选型阶段其实都是可以确定的,只是很多人没意识到要问。
我在帮客户做方案的时候,一般会列一张表,把下面这几个问题逐条确认。这张表填完了,选型基本不会出大方向的问题。
| 要确认的问题 | 为什么关键 | 常见误判 |
|---|---|---|
| 串口设备是RS232还是RS485/422 | 电气标准不同,接错可能损坏设备 | 看到DB9就认为是232 |
| 需要几路串口 | 决定是买1口还是4口/8口 | 只算现在的设备,没算预留 |
| 每个口是主站还是从站 | 决定数据方向和轮询逻辑 | 以为485就是双向随便发 |
| 现场供电是直流还是交流 | 决定选宽压DC还是内置电源 | 现场只有24V,买了220V版 |
| 是否需要光电隔离 | 决定长距离、强干扰环境能否扛住 | 觉得隔离是"锦上添花" |
| 安装方式是导轨还是壁挂 | 决定柜内怎么装 | 到了现场发现卡不上 |
| 上位机软件是否支持网络 | 决定要不要用虚拟串口 | 以为所有软件都支持TCP直连 |
2.1 RS232、RS485、RS422:三种电气标准不能混着买
这三个名字经常被混着叫,但它们在电气层面完全是三回事。
RS232是三线制(收、发、地),全双工,点对点。它的电平是负逻辑,正电压表示0,负电压表示1,摆幅大概在±3V到±15V之间。传输距离在标准里写的是15米,实际工程里我会按10米以内来算,波特率越高距离越短。它的特点是设备端接口常常是DB9或DB25,一旦超过十米八米就会开始出问题,而且抗干扰能力弱。
RS485是差分信号,用A、B一对线传一路信号,所以通常是半双工——同一条线上,要么发要么收,不能同时。它的电平是两根线之间的电压差,共模干扰会被抵消掉,所以抗干扰能力强很多,标称距离1200米,实际在波特率9600的时候跑到800米左右是比较靠谱的。它的接线是菊花链式的,一条总线挂多个从站设备。
RS422也是差分,但它是四线制,收发各用一对,所以能全双工。它的距离和抗干扰能力和485相当,但能挂的节点数少一些,一般用于点对点的双向通信场景。
这三个标准在串口服务器上是不同的物理接口。买的时候要看清楚:232的接口一般是DB9或接线端子,485/422一般是A、B、Y、Z四个端子或者两个端子(只有485时)。有些设备是232/485/422三合一可切换的,价格会高一点,但在不确定现场设备类型的时候是很划算的保险。我个人的经验是,如果项目里设备类型还没完全定,宁可多花几百块买可切换的型号,也别赌一次。
注意:把RS232的信号直接接到RS485的A、B端子上,大概率什么也收不到,某些情况下还可能因为电平冲突损伤接口芯片。现场如果拿不准,先用万用表量一下静态电平,或者直接翻设备手册的通信接口章节。
2.2 一口、四口还是十六口:怎么算自己需要多少路
口数的估算,新手常常是"现在有几台设备就买几口",结果设备一扩容就傻眼。我一般按这个公式来:需要的口数 = 当前设备数按协议分组后的组数 × 1.5。
为什么要按协议分组?因为同一条RS485总线上挂的设备,必须共用同一个波特率和数据格式。如果一个项目里有9600的电表和19200的温湿度传感器,它们就不能挂在同一条总线上,必须分成两条。所以你需要先按"波特率+协议类型"把设备分类,看看分成几组,再决定买几口。
乘1.5是留余量。现场的事很难说,临时加一台仪表、客户又提了个新需求、某台设备的通信口坏了要单独接,这些都需要额外的口。四口和八口的价差通常不像口数差那么大,多留一点接口往往比后期再加一台设备划算。
另外要注意的是,有些多口型号是"每个口独立配置",每个口可以设不同的波特率、不同的模式、不同的端口号,这在中大型项目里非常重要。而有些便宜的型号是多口共享一组配置,用起来会很别扭。这一点一定要在选型时问清楚。
2.3 "9600, 8, N, 1"这串东西到底在说什么
这串参数是串口设备的"语言约定",两边不一致就完全是乱码。它的四个部分分别代表:
- 波特率:每秒传输的符号数。9600、19200、38400、115200是工业上最常用的几个值。它决定了数据快慢,也间接决定了可靠传输距离。
- 数据位:每个字符用几位数据表示,通常是8位,也有7位的(一些老式仪表)。
- 校验位:N是无校验,E是偶校验,O是奇校验。它的作用是在噪声环境下做一次粗校验。
- 停止位:通常是1位,也有1.5位和2位的情况。
工业现场九成以上是"9600, 8, N, 1"或者"19200, 8, N, 1"。我在现场调不通的时候,第一件事就是用示波器或者串口调试工具去测电压波形,反推波特率。因为很多老设备的手册早就丢了,只能靠测量。
流控是另一个容易被忽略的参数。它分硬件流控(RTS/CTS)和软件流控(XON/XOFF)。绝大多数工业串口设备不使用流控,配置里设为"无"就行。但如果你的设备是那种数据量大、发送方不等接收方准备好就猛发的类型,那没有流控就会丢数据。这时候要么在协议层做应答,要么启用硬件流控。
2.4 宽压供电、光电隔离、防雷与导轨安装:柜内这几件事别省
这几项听起来像是"锦上添花",但在工业环境里它们决定了设备能不能活过第一年。
宽压供电指的是设备支持一个较宽的输入电压范围,比如9到36V直流。这在你现场只有24V开关电源的时候很有用,并且能容忍电压波动。如果买的是固定12V输入,现场一旦给你接了个24V,直接烧掉。
光电隔离是我强烈建议要有的一项。它把串口侧和网络侧、电源侧在电气上彻底隔开,隔离电压一般是2kV或者更高。在配电室、变频器柜、电机旁边这种环境,地电位差、浪涌、静电都很常见,没有隔离的设备很容易莫名死机或者接口击穿。我见过一台没隔离的串口服务器,装在变频柜里,每半个月挂一次,后来换成隔离型的,两年没出过问题。
防雷主要针对室外走线或者跨楼栋的场景。如果串口线要从一个楼走到另一个楼,或者要出到室外设备,必须考虑加装浪涌保护器。有的串口服务器自带TVS管做一定程度的保护,但那只够应付静电,不够应付雷击浪涌。
导轨安装是柜内安装的标配。DIN35导轨几乎是所有电气柜的通用标准,如果买的是桌面式、带小耳朵的型号,到了现场你会发现没法固定在柜子里,只能悬着或者用扎带绑,既不美观也不安全。
3. 从接线到第一个字节:一次完整的调试过程
选型、到货、上柜之后,最紧张的就是第一次上电调试。我习惯把调试过程拆成五个阶段,每个阶段都有明确的"通过标准",不通过就不往下走。这样出问题的时候,故障范围就被限制在一个阶段里,排查速度会快很多。
3.1 串口侧接线:TX、RX、GND和A、B到底怎么对
RS232的接线要记住一个概念:交叉。一头是发送,另一头就要接收。标准的DB9针脚定义里,2脚是RXD,3脚是TXD,5脚是GND。所以串口服务器的TXD要接到设备的RXD,串口服务器的RXD要接到设备的TXD,GND对GND。如果是用DB9直连线,那就要看线是公头还是母头,公母直连的情况下内部通常已经交叉过了。这是我见过最多人搞错的地方,症状就是"完全没数据",因为收发的两根线接反了。
另外,有些设备需要握手信号才肯通信。它可能会检测DTR、DSR、CTS这几个引脚的状态,如果没有拉高,它就不往外发数据。这种情况的处理办法是短接对应的引脚,比如把4脚(DTR)和6脚(DSR)短接,把7脚(RTS)和8脚(CTS)短接,制造一个"对方已经准备好"的假象。这种歪招在老设备上非常常见。
RS485的接线相对简单但更不能错。A接A、B接B,如果接到一半发现不通,把A、B对调一下试试。这不是玄学,因为不同厂家对A和B的定义有时候是反的,行业里确实存在这个混乱。我一般会准备好一个万用表,先测量空闲状态下A与B之间的电压差,正的时候是逻辑1,负的时候是逻辑0,借这个来判断极性是不是对的。
还有两个细节:总线终端电阻和手拉手拓扑。当波特率在115200这种高速下,或者线比较长的时候,需要在总线的两端各加一个120欧的终端电阻来消除反射。拓扑上必须是一根线串下去,不能在中间分叉出很长的支线,否则会产生信号反射导致误码。
3.2 网络侧:IP、掩码、网关和端口号应该怎么安排
网络侧的配置通常有几种途径:设备自带的一个网页界面、厂家提供的配置软件、串口命令,或者拨码开关。我比较推荐用网页界面,它能让你直观地看到所有参数,改完之后也可以一键恢复出厂设置。
参数上要理清几件事:
- IP地址:要么跟上位机在同一个网段,要么能路由到。公网方案我一般不推荐,除非有严格的安全措施。
- 掩码和网关:如果上位机和设备在同一网段,不填网关也没关系。跨网段就必须填对。
- 端口号:TCP Server模式下自己要指定一个,常见的有4001、5000、8899。不要用23(Telnet的默认端口),也不要跟系统端口冲突,一般选5000以上的比较安全。
- 工作模式:前面说过的Server/Client/UDP三选一。
我强烈建议给每一台串口服务器分配一个固定的IP,而不是靠DHCP。因为工业现场的网络设备通常没有网管,DHCP服务器宕机或者租期到了没续上,设备IP一变,上位机就连不上了。固定IP + 纸质台账,是最土但最可靠的办法。
3.3 用电脑验证链路:ping通只说明一半问题
很多人把"能ping通"当作调试成功,这是一个很大的误区。ping只验证了IP层是通的,也就是设备在网络里活着,但它完全不涉及串口。
正确的验证顺序是四步:
- ping通目标IP:验证网络层可达。
- 用telnet或TCP调试工具连目标端口:验证TCP层的连接能建立。这一步如果失败,说明模式不对、端口号不对,或者被防火墙拦了。
- 向端口发送一帧真实的协议报文:比如Modbus的
01 03 00 00 00 02 C4 0B,然后观察有没有应答返回。这一步如果有返回,说明整条链路,从串口服务器的UART到现场设备,都是通的。 - 观察数据的内容和节奏:应答的字节对不对、CRC对不对、时间间隔是不是和预期一致。
我最常用的工具是一个TCP调试助手,可以在电脑上当一个TCP Client去连串口服务器,也可以监听端口等它的连接。手边有Python的话也可以直接写几行:
import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(3) s.connect(("192.168.1.200", 4001)) # Modbus RTU:读从站1的保持寄存器,起始地址0,读2个 req = bytes.fromhex("010300000002C40B") s.send(req) print("发送:", req.hex()) try: resp = s.recv(256) print("接收:", resp.hex()) except socket.timeout: print("超时,没有收到应答") s.close()这段代码很朴素,但它能一次性验证我从网络到串口的所有环节。如果打印出010304xxxx....,说明读到了两个寄存器的值。如果超时,问题在现场设备或者串口参数。
3.4 虚拟串口:让老掉牙的上位机软件以为自己还插着线
现场经常遇到这种情况:上位机软件是厂家给的,只支持串口,界面里只有一个下拉框让你选COM口,别的什么都不支持。这时候虚拟串口就是救星。
原理不复杂。虚拟串口软件在你的电脑上创建一对"虚拟的COM口",比如COM10和COM11,然后把COM10跟某个IP的某个端口绑定起来。当上位机打开COM10的时候,软件就把对COM10的读写操作转成了TCP的发送和接收。
配置流程大概是这样:
- 安装虚拟串口软件,重启电脑。
- 新建一个虚拟串口对,比如COM10对COM11。
- 把COM11配置成"连接到一个TCP服务端",填上串口服务器的IP和端口。
- 打开上位机软件,在串口设置里选COM10,波特率、数据位、校验位按现场设备填。
- 上位机发起通信,虚拟串口软件自动建立连接,双向数据就通了。
这里有两个坑要注意。第一,波特率只是形式上的。走网络之后真实速率由网络决定,虚拟串口上的波特率设置只是让你的上位机软件不自检报错,实际不生效。第二,虚拟串口软件对超时和断线重连的处理差别很大。有些软件在断线后会一直卡住,让上位机看起来像死机;有些会自己重连。选的时候要看清楚,最好是能设置自动重连和超时时间的。
还有一个更省事的思路:如果上位机软件用的是标准Modbus协议,那么可以在串口服务器侧就把它设置成"Modbus网关"模式,让上位机直接用Modbus TCP去读写,彻底绕开虚拟串口。这需要上位机软件支持Modbus TCP,不少组态软件是支持的。
4. 现场最常踩的五个坑,以及我自己的排查顺序
下面这几个坑,我几乎每个都踩过至少一次。我把它们写出来的时候有意保留了排查的过程,因为排查过程本身比结论更有用。
4.1 能ping通、端口也开着,但就是收不到数据
这是最高频的一类问题。症状是:ping通,telnet连接也能建立,但send之后没有任何返回。
排查顺序我一般是这样走的:
第一步,确认工作模式和连接方向。如果串口服务器配的是TCP Server,而你的软件也在用Server模式监听,两个Server是永远碰不上的。必须一边是Server一边是Client。
第二步,确认串口参数。波特率、数据位、校验、停止位这四项必须和现场设备完全一致。我见过很多次是波特率填成了9600,而设备实际是19200。
第三步,确认串口接线。RS232有没有交叉、RS485的A、B有没有接反、GND有没有接。特别是GND,有些人觉得485是差分不需要地线,实际上在很多现场,两地电位差超过接口芯片共模范围的时候,不接地线就会通信异常。
第四步,确认现场设备是否在"说话"。有的设备是从站,你不问它它不说。有的设备需要先发一个唤醒命令。这时候需要一个USB转串口的小工具,直接在串口服务器的串口端并上去看一眼波形和字节。
第五步,确认现场设备本身是好的。这一步别跳过。我遇到过客户抱怨串口服务器有问题,查了半天,最后发现是那台流量计本身就没通上电。
4.2 数据粘包和半包:为什么你的报文总是断在中间
TCP是字节流协议,它不保留应用层的消息边界。你在串口这头发了两帧,到了TCP那头可能是一个大包,也可能是三个小包,完全看网络栈怎么处理。
现场表现有两种:
- 粘包:一次recv收到
010304000A000B010304000A000B,两帧挤在一起,如果解析程序按固定长度切就会出错。 - 半包:一次recv只收到
0103,后面一半下次才到,解析程序一样会错。
解决办法有三个层次。
第一层,在串口服务器上设分包参数。大多数型号支持"按长度打包"和"按时间间隔打包"。对于定长协议(比如Modbus RTU的请求一般是8字节),可以设置成每8字节发一次;对于不定长协议,就设置成"收到最后一个字节后等待X毫秒再发"。这里的X很讲究:设太小,一帧被拆成两次发;设太大,实时性变差。我一般从20毫秒开始试,根据协议帧长和波特率调整。有个大致估算方法:一帧N字节,波特率B,传输时间约为N×10/B秒。比如32字节在9600波特率下大约是33毫秒,那打包间隔设到40到50毫秒比较合适。
第二层,在解析程序里做缓冲重组。这是更根本的做法。在应用层维护一个byte缓冲区,每次收到数据就追加进去,然后按照协议的帧边界去解析,解析出一帧就消费掉,剩下的留在缓冲区等下次数据。Modbus RTU的帧边界可以靠功能码对应的数据长度来推,也可以靠3.5个字符时间的静默间隔来判断。
第三层,在协议上做改造。如果可行,给每帧加上帧头和长度字段,那么接收方就总能知道该收多少字节。这需要改现场设备的固件,大多数时候是做不到的,但在新设计系统时可以这样规划。
4.3 一个串口被两个上位机抢:谁先连上谁说了算
串口服务器的一个串口,物理上只能被一个逻辑通道占用。如果它设置成TCP Server并且允许多个连接,那么当第二个客户端也连上来的时候,两个客户端发出去的数据都会灌到同一个串口里,串口返回的数据也会同时发给两个客户端。这在某些场景下是可行的(比如多个显示器看同一份数据),但如果两个客户端都在主动发查询命令,就一定会乱。
处理办法有几种:
- 限制最大连接数为1。这是最直接的,第二个连接直接被拒绝。
- 用TCP Client模式,让串口服务器只连一个固定的服务端,天然排他。
- 上层的中间服务。让一个程序专门负责跟串口服务器通信,然后把数据分发给多个应用。这是中大型项目的标准做法,也方便做数据缓存和历史存储。
我在一个配电监控项目里就吃过这里的亏。当时两个采集程序同时运行,一个负责实时展示,一个负责写数据库,两边都在对同一批电表轮询,结果电表的应答时有时无,查了很久才反应过来是串口抢占。后来把两个程序合并成一个,前面加一层采集模块,问题立刻消失了。
4.4 乱码和丢包:接地、屏蔽和线长在作怪
串口数据出现乱码,八成是电气层面的问题,不是软件的问题。我按这个顺序排查:
线长超标。RS232超过15米、RS485超过1200米(实际中我按800米算),信号就开始失真。办法是换485、加中继器、或者缩短距离。
没有接地。485总线虽然用差分传输,抗共模干扰,但如果两端的设备地电位差太大,超过了接口芯片的共模输入范围(常见是-7V到+12V),照样收不到。这时候需要一根地线把两端的参考地连起来,或者用带隔离的串口服务器把两端彻底隔开。
线缆选择不对。485总线应该用双绞线,最好是带屏蔽的双绞线。用普通的平行线,两条线之间的耦合不对称,抗干扰能力就大打折扣。屏蔽层要不要接地?我的做法是单端接地,把屏蔽层在主机侧接到大地,远端悬空。两端都接地会形成地环流,反而引入干扰。
总线上有干扰源。变频器、大功率电机、接触器这些东西在工作的时候会往外喷电磁噪声。走线的时候要尽量远离它们,交叉的时候走直角,不要平行长距离走。
终端电阻缺失或者重复。120欧的终端电阻应该只在总线的两个物理端点各加一个,中间节点不加。加多了会让总线负载过重,信号幅度被拉低。
软件侧还有一个坑:接收超时设置得太短。如果程序设了100毫秒的读取超时,而设备应答需要150毫秒,那程序就会不停地超时。这个参数要根据现场设备的实际响应时间调整。
4.5 断电重启之后IP变了或者配置丢了
这个问题的根源通常是三个:
一、IP是通过DHCP拿的。断电重启时DHCP服务器正好在重启,设备拿不到地址,会用默认的169.254网段,上位机当然连不上。改成固定IP。
二、配置没有保存。有些型号的配置页面有"应用"和"保存并重启"两个按钮,只点了应用,配置在内存里生效了,但没写入Flash,断电就丢。这个坑我在第一次用某个型号的时候踩过,改了半小时,一断电全白改。现在我的习惯是配置完必须做一次断电测试。
三、设备本身有看门狗但配置区损坏。极少数情况下Flash会写坏,设备恢复出厂设置。这种情况一般伴随着设备的电源质量不好,或者频繁断电。解决办法是给它配一个像样的开关电源,必要的话加一个小UPS。
顺便说一个习惯:每次配置完,把配置界面截图存档。设备台账上记录IP、端口、波特率、模式、安装位置、设备型号。项目做完一年后现场出问题,翻出这张截图,能省你几个小时。
5. 接进自己的系统:从Modbus到脚本采集
调通了链路只是第一步,真正有价值的是把数据接进业务系统。这一段我讲几种从简到繁的接入方式。
5.1 Modbus RTU和Modbus TCP之间到底是什么关系
工业现场用得最多的协议就是Modbus。它的RTU版本跑在串口上,TCP版本跑在网络上,两者的区别其实很小。
RTU的帧结构是:从站地址(1字节)+ 功能码(1字节)+ 数据(N字节)+ CRC16(2字节)。 TCP的帧结构是:事务ID(2字节)+ 协议ID(2字节)+ 长度(2字节)+ 单元ID(1字节)+ 功能码(1字节)+ 数据(N字节)。后面那一坨其实就是RTU的PDU部分去掉了CRC。
所以从RTU转TCP,本质上就是加一个7字节的MBAP头,去掉CRC,再把单元ID从RTU的从站地址搬过来。这个转换很多串口服务器自己就能做,配置项一般叫"Modbus网关"或者"Modbus RTU to TCP"。
两种做法各有取舍:
| 做法 | 优点 | 缺点 |
|---|---|---|
| 串口服务器做网关转换 | 上位机直接说Modbus TCP,不用装虚拟串口 | 功能受限,多主站并发时可能不支持 |
| 用虚拟串口走RTU | 兼容所有老软件,行为跟本地串口一样 | 需要装驱动,长期运行可能不稳定 |
| 自己写程序做转换 | 完全可控,可以做缓存、重试、批量优化 | 有开发成本,需要维护 |
我自己的偏好是:小项目用虚拟串口,快速交付;中大型项目直接走Modbus TCP,或者干脆自己写一层采集服务。
Modbus TCP的请求帧很好构造,比如读从站1的保持寄存器,起始0,数量2:
import socket # 事务ID=0001, 协议ID=0000, 长度=0006, 单元ID=01, 功能码=03, 起始=0000, 数量=0002 frame = bytes.fromhex("000100000006010300000002") s = socket.create_connection(("192.168.1.200", 502), timeout=3) s.send(frame) resp = s.recv(1024) print("返回:", resp.hex()) # 前7字节是MBAP头,第8字节是单元ID,第9字节是功能码 if len(resp) >= 9: unit = resp[6] func = resp[7] byte_count = resp[8] payload = resp[9:9 + byte_count] print(f"单元={unit} 功能码={func} 数据={payload.hex()}") s.close()这段代码里有个细节值得注意:每一帧都要有一个不同的事务ID。如果连发两帧用了同一个事务ID,某些实现会分不清应答对应哪一帧。事务ID从1开始递增,超过65535就回绕,这是标准做法。
5.2 用脚本直连串口服务器取数
如果不想用Modbus TCP,而是直接通过串口服务器的TCP端口发RTU帧,也是完全可以的。这时候串口服务器只做透明传输,不做任何协议转换。
import socket import time import struct def crc16(data: bytes) -> bytes: """Modbus CRC16,返回低字节在前""" crc = 0xFFFF for b in data: crc ^= b for _ in range(8): if crc & 1: crc = (crc >> 1) ^ 0xA001 else: crc >>= 1 return struct.pack("<H", crc) def read_holding(sock, slave, start, count): pdu = struct.pack(">BBHH", slave, 0x03, start, count) frame = pdu + crc16(pdu) sock.send(frame) resp = sock.recv(256) if len(resp) < 5: return None # 校验 body, recv_crc = resp[:-2], resp[-2:] if crc16(body) != recv_crc: print("CRC校验失败") return None byte_count = resp[2] values = struct.unpack(">" + "H" * (byte_count // 2), resp[3:3 + byte_count]) return values sock = socket.create_connection(("192.168.1.200", 4001), timeout=3) while True: vals = read_holding(sock, 1, 0, 2) print("读到:", vals) time.sleep(2)这段代码比用的库更值得看的是它的结构:自己算CRC、自己校验、自己解包。把这个跑通,你对Modbus的理解就不只是"调用库"那个层次了。现场的很多怪问题,其实都是在这一层暴露出来的。
5.3 组态软件和SCADA怎么对接
如果项目用的是组态软件(国内外的都有),对接串口服务器一般有两类做法。
走虚拟串口:在组态软件里配置一个串口设备,选择虚拟出来的COM口,然后按照设备的点表去定义寄存器。这种方式不需要组态软件支持网络,兼容性最好。
走Modbus TCP:在组态软件里选择Modbus TCP驱动,填IP和端口(通常是502),然后配置从站地址、寄存器地址、数据类型。这种方式配置量少、性能更好,我比较推荐。注意单元ID在Modbus TCP里通常填成1,有些驱动会把它叫做"站号",实际上在TCP里它的作用已经被弱化了。
如果组态软件两者都不支持,还有一个办法:写一个中间服务,用Python或者C#定时去采集串口服务器,把数据写进数据库(MySQL、SQL Server都行),然后组态软件用ODBC去读表。这个方案听起来绕,但在很多"必须用指定组态软件"的项目里是唯一能走通的路,而且它还有一个额外好处:数据被持久化了,可以做历史曲线、报表和告警。
5.4 往上再走一层:接入数据平台的几种思路
现在越来越多的项目要求数据上平台。串口服务器本身通常不具备直接对接云平台的能力(少数型号支持MQTT),所以中间一般需要一个网关或者边缘计算设备。
做法有三种:
方案一:服务器侧做采集。一台服务器用脚本轮询所有串口服务器,采集完之后入库并转发到平台。这是最传统的方式,灵活度最高,缺点是对服务器的可用性有要求。
方案二:边缘网关采集。在靠近现场的位置放一台低功耗工控机或者边缘网关,本地跑采集程序,做初步的清洗和缓存,再上传。这样做的好处是断网时数据不会丢,网络恢复后可以补传。
方案三:串口服务器直连平台。部分型号支持MQTT或者HTTP上报,配置一下目标地址和主题就能把串口数据发出去。这种方式最省事,但数据处理能力很有限,主要适合简单的透传场景。
三种方案的取舍,我一般看两个维度:现场网络质量如何、数据丢失的容忍度有多高。如果现场网络靠4G并且时断时续,那必须用边缘网关做本地缓存,否则数据会丢得很难看。
6. 几个典型场景的落地思路
写到这里,我想拿几个我做过的场景,把前面讲的东西串一遍。这样比看参数表更直观。
6.1 机房UPS与精密空调的集中监控
机房里最需要监控的往往是那些最不起眼的设备:UPS、精密空调、配电柜。它们的共同点的确很一致——都有串口,都不太聪明。
这个场景的典型配置是:一台四口串口服务器装在机柜里,UPS的DB9接到第1口(RS232),精密空调的DB9接到第2口(RS232),配电柜的智能电表通过RS485接到第3口(485),第4口留作备用。串口服务器配成TCP Server,四个口分别用4001到4004四个端口。中控室的一台采集服务器上跑程序,同时连四个端口轮询。
这里有两个细节。第一,UPS和空调的协议往往不是标准的Modbus,而是厂家私有的,需要向他们索要串口通信协议文档。有些厂家会直接给,有些要签保密协议,有些干脆不给,这时候就只能靠抓包分析。第二,这些设备的告警信息很关键,掉电、市电异常、温度过高,这些状态必须能实时推送到运维群里,所以采集程序最好做成"变化即上报"的模式,而不是单纯地轮询展示。
6.2 产线上PLC和仪表的数据采集
产线场景对实时性要求高,对数据的完整性要求也高。常见配置是:每台设备旁边放一台串口服务器,就近接入车间的工业交换机,然后统一汇到车间的采集服务器。
这类项目里我会特别注意三件事。一是轮询周期要算清楚。比如一条线上有20台设备,每台响应需要50毫秒,那么一轮最少需要1秒。如果要求500毫秒刷新一次,就必须分成两个采集进程或者提高波特率。二是要区分关键数据和一般数据。产量计数这种数据可以慢一点采,但故障信号必须快。三是现场的网络要有冗余或者至少要做好监控,串口服务器掉线了得知道,不能等生产停了才发现。
6.3 门禁、考勤和闸机设备的联网
这类场景的特点是:设备分散在各个门口,每个门只有一两台设备,但总数量不小。如果用一台多口设备集中接,那就要把线从各个门口拉到弱电间,线缆成本很高。更好的做法是每个门就近放一台单口或者双口串口服务器,通过门禁系统已有的网线走数据。
这种分布式部署最麻烦的是IP管理和设备台账。我一般会按楼层和门号来编IP,比如一层是192.168.10.x,二层是192.168.20.x,第三段按门口顺序排。同时维护一个表格,记录IP、安装位置、对应的门、设备型号、上线日期。听着土,但真的能救命。
还有一点要提醒:门禁设备的串口往往是最简单的单向通信,很多情况下设备根本不需要应答,只要你发它就执行。这种场景用UDP反而更合适,延迟低、开销小,丢一帧重发就行。
6.4 配电室电表集中抄读
这是串口服务器最经典的用途。一个配电室里可能有几十块多功能电表,全部挂在一条或者几条RS485总线上,每块表有独立的从站地址。串口服务器负责把这条总线接到网络上,后台的抄表系统按照地址依次去读每块表的数据。
这个场景的关键是总线的组织方式和抄表节奏的控制。一条485总线挂多少块表?这取决于波特率、每块表的响应时间和总线的电气负载。我的经验值是:9600波特率下,挂20到30块表比较稳妥;如果表更多,就分几条总线,用多口串口服务器分别接。
抄表节奏要留够余量。如果一块表的应答需要100毫秒,那么轮询间隔至少要留200毫秒。太紧凑的话,前一块表的应答还没发完,下一块的请求就发出去了,总线上就会撞车,表现为应答错乱或者无应答。
还有一个技巧是分级抄表:重要的数据(总有功电能、三相电流)每5分钟抄一次,次要的数据(电压、功率因数)每15分钟抄一次,不影响业务的诊断数据每天抄一次。这样能大幅降低总线压力,也能让采集程序跑得更轻松。
再说一个我自己踩过的坑。有一次项目上线后,抄表数据总是缺几个点,看日志是超时。查了半天,最后发现是那块表在整点有个内部任务,那几秒钟不响应外部请求。解决办法是把抄表时间错开整点,从每小时的1分开始抄。这种问题,任何手册上都不会写,只能靠观察现场。
7. 我个人在这件事上的一点体会
做这类项目做了不少年,我最大的感受是:串口服务器的价值不在它本身,而在于它让多少老设备能继续工作。它不是新技术,也不是什么高精尖的东西,但它解决的是一个非常现实的问题——设备还能用、协议还在跑、数据还有价值,只是缺一个出口。
如果你现在正准备上这么一个项目,我建议你先花半天时间做三件事。第一,把现场所有需要联网的设备列一个清单,标上接口类型、协议、波特率、数据格式、设备地址,能测的都实测一遍,不要只信手册。第二,把网络规划画出来,IP怎么分配、交换机在哪、走线怎么走,画完你就知道该买几口、买哪种。第三,先拿一台设备做通,从物理接线一直到后台看到数据,全流程走一遍。这三件事做完,后面的批量实施会顺得超乎你的想象。
最后分享一个小习惯。我现在所有的现场盒子里都会放两样东西:一根USB转RS485的小线和几张标签纸。前者用来在怀疑串口服务器的时候,直接绕过它去测设备;后者用来在每根线的两头贴上标签,写清楚"到哪台设备、哪个口、什么协议"。这两样东西加在一起不到一百块,但它们帮我省下的排查时间,我估计得有几百个小时。