尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于Modbus RTU的PLC扫码支付对接实践

基于Modbus RTU的PLC扫码支付对接实践 1. 项目背景与接口选型思路做工业设备对接扫码支付这几年基本成了自助终端类项目的标配功能。自动售货机、充电桩、门禁闸机、自助洗衣房、共享按摩椅甚至一些小型的加工设备计费终端都需要实现“扫码付款 — 平台确认 — 设备动作”这条链路。而我这次要拆解的是其中最容易踩坑也最让人头疼的一段PLC怎么跟扫码支付模块把通信打通。先说结论多数现场采用的是串口RS232/RS485 Modbus RTU 协议来对接而不是走网口或者4G模组。原因很简单——PLC的串口资源几乎是标配老一点的机型也都有COM口而扫码支付终端为了兼容工业场景基本都保留了串口从站模式。两边都能提供标准串口那通信就绕不开Modbus这个老熟人。这次项目我用的主控是台达DVP系列PLC扫码支付模块是一台支持Modbus RTU从站协议的扫码终端带语音播报、小显示屏、4G联网串口参数是RS485、9600波特率、8数据位、无校验、1停止位。整套系统需要实现的功能是PLC实时轮询支付终端的状态寄存器当检测到支付成功信号后驱动一个输出点去控制继电器从而放行设备同时把设备的状态比如正在运行、故障、完成回传给平台侧。文章里面我会把从硬件接线到协议报文再到PLC梯形图程序的完整过程都过一遍。适合这几类读者参考自己搭过串口但没接过支付模块的PLC工程师做自助终端集成需要把支付逻辑写进PLC的电气工程师以及那些想搞明白“支付模块到底怎么跟PLC说话”的现场调试人员。2. 硬件连接与通信参数配置2.1 串口接线方式与线序确认串口对接看着简单实际上很多项目前期的坑都埋在这儿。先分清楚你手里那个扫码支付模块是RS232口还是RS485口。RS232是点对点、电压电平通信一般就3根线——TXD、RXD、GND距离不要超过15米RS485是差分信号A/B两线支持多设备挂总线距离能到1200米。工业现场用的支付终端绝大多数都是RS485口因为自助设备分散、走线长485抗干扰能力要强得多而且以后如果一台PLC要挂多个支付终端485总线是唯一合理的选择。我这台的标注是A接485、B接485-。台达PLC侧DVP系列内置的COM口如果是485通常定义是D和D-如果是带扩展卡的还要看卡上丝印。接线时注意A对D、B对D-别反了。有些工程商图省事把A/B反接试一次没反应就去怀疑设备实际上串口对接出问题第一排查项永远是线序。还要提醒一句不要把屏蔽层当成地线来接。屏蔽层应该单端接地或者悬空特别是在两个设备都是隔离电源供电的场合屏蔽层如果两端都接地会形成地环路轻则丢帧重则烧接口。现场调试这么多年我见过的串口通信故障大概有四成是接错线三成是地电位差异导致报文错乱剩下才是参数和程序问题。2.2 串口参数一致性9600/8/N/1 为什么这么配扫码支付终端出厂默认参数多数是9600、8、N、1PLC这边也要设成完全一致。Modbus RTU是异步串口帧收发双方必须在波特率、数据位、校验位、停止位上完全一致否则解帧必错。这一点新手容易忽略的是停止位和校验位——很多PLC默认是偶校验而支付模块出厂很多是无校验两者对不上建立不了通信。拿台达DVP来说它的COM2口可以通过D1120特殊寄存器配置通信参数。比如D1120设为H0086就代表9600、8、N、1具体每一位的含义是低字节bit0~bit3是波特率bit4是校验位选择bit5/bit6是数据位和停止位组合。H0086拆开来看——bit0~bit3为6对应9600bit4为0表示无校验bit5/bit6组合为0对应8位数据位1位停止位。设置完了之后还需要把D1120的设定值送进特殊寄存器同时将D1125设为通信超时时间D1126为发送余量时间这些参数直接影响通信稳定性。如果扫码模块那边参数被改过比如之前有人把它调成了19200那PLC这边不改到一致调一整天也白搭。所以开工第一步我建议先用串口调试助手把支付终端单独测一遍确认真实参数再决定PLC侧怎么配。后面我会专门讲用Modbus Slave模拟软件验证的流程。2.3 USB转串口工具与驱动的那些事调试阶段绕不开USB转串口模块。CH340和FTDI芯片的两种最常见CH340便宜、国内用得最多缺点是有些高波特率下波形质量一般FTDI稳定、兼容性好但也贵而且市面上假货不少买的时候要留个心眼。这里的经验是调试Modbus RTU这种低速协议CH340足够用了真正不稳定的时候反而很少是转换器的问题而是干扰、接地和参数配置问题。驱动安装完了以后在设备管理器里确认端口号COM几这点特别重要。很多工程师在调试软件里选错COM口数据死活不出来一查才发现根本没有物理上的接通。Windows下如果插上没反应优先检查的是驱动是否装好、USB线是不是“充电专用线”而不是数据线。这两条占USB转串口调试故障的七成以上很玄学但很常见。我还习惯用串口数据记录仪看一眼原始hex数据而不是只看解析后的报文。因为有些时候PLC侧的请求报文是错的但调试助手按模解析也能显示CRC错误或者功能码异常——这时候你不看原始帧永远找不到根本原因。好一点的串口调试软件都有HEX收发和定时发送功能我后面会讲具体怎么看帧。3. Modbus RTU协议拆解与报文分析3.1 一个帧里到底有什么Modbus RTU的报文是非常规整的地址码1字节 功能码1字节 数据区N字节 CRC校验2字节。所有的请求和响应都以这个结构组织所以排查通信问题时先用Hex格式抓一帧按结构拆开看就能快速定位问题在谁的头上、错在哪个字段。地址码这个字节是设备地址取值范围一般是1~2470作为广播地址。扫码支付终端作为一个Modbus从站会被分配一个站号比如01或02。PLC作为主站发送报文时地址码必须对应终端上设定的站号不一致的话终端根本不会回帧因为别的从站收到地址不匹配的帧只会静默丢弃。功能码是告诉从站“你要干什么”。初学者最容易把功能码当成寄存器地址的一部分实际上它们是两个维度功能码决定操作类型寄存器地址决定操作哪个数据。扫码支付场景里用的最多的是03读保持寄存器和06写单个寄存器。03用来轮询支付状态、设备状态、故障码06一般用来下发金额、启动支付流程、清除状态。偶尔还会用到10写多个寄存器用来批量下发订单信息但那种场景一般走平台下发更常见PLC侧很少主动写大段数据。CRC校验是整个帧的最后两个字节是CRC-16Modbus变体。它是主站对前面所有字节做多项式计算后的结果从站收到后同样计算一遍比对不上就丢弃这个帧。CRC错误最常见的诱因是波特率不匹配、干扰导致电平翻转、485芯片收发方向切换时序不对。我见过有人拿串口调试助手能正常收发但一接PLC就频繁CRC校验失败最后查出是PLC发送完毕到切换为接收之间的延时太短导致收到了自己发出报文的回波尾巴。3.2 扫码支付的标准寄存器地址映射支付终端厂家通常会提供一份寄存器地址表虽然各家具体地址可能不同但大体的设计思路是相通的。拿我这台终端来说40001地址0x0000指令寄存器PLC写入“启动扫码支付”指令40002地址0x0001金额寄存器PLC写入需要支付的金额单位通常是分40003地址0x0002状态寄存器PLC读取值含义0空闲1支付中2支付成功3支付失败4超时40004地址0x0003订单号寄存器PLC读取支付完成后的订单尾号40005地址0x0004功能码返回记录用于诊断上一次操作的结果这里有一个非常关键的点Modbus协议里的寄存器地址有两种表达方式——一种是协议层地址从0开始一种是数据模型地址从1开始常写作40001。PLC编程时填的是通信报文里的协议地址也就是0x0000、0x0001这种而手册里写40001是给人看的实际报文里地址码是0x0000。千万不要把手册上写的40001直接填进PLC的指令里那样会差了1个地址读回来的数据全是偏的。我一开始做这个项目也在这上面栽过一次读状态总是不对后来打印原始报文才发现地址差了一字节。3.3 实操报文示例轮询成功的完整交互先说PLC主动去读状态寄存器这一帧。假设终端站号为01要读地址0x0002的1个寄存器报文是01 03 00 02 00 01 25 CA逐字节解释01是地址码03是功能码00 02是起始寄存器地址0x000200 01是要读取的寄存器个数25 CA是CRC16校验由前面所有字节算出来。终端正常响应帧长这样01 03 02 00 02 39 8501是回地址码03功能码02表示后面数据区有2个字节00 02就是状态寄存器的值十进制为2对应“支付成功”39 85是CRC。如果PLC这边要下发支付金额假设金额是5元也就是500分用功能码06写单个寄存器地址0x000101 06 00 01 01 F4 D9 7C01地址码、06功能码、00 01寄存器地址、01 F4就是十六进制500、D9 7C是CRC。支付成功之后平台侧也会回调终端终端再把状态寄存器刷新为2这样PLC轮询到2就知道可以执行放行动作了。4. PLC侧编程实现支付轮询逻辑4.1 台达PLC通信参数的软件配置台达DVP系列用WPLSoft或者ISPSoft编程通信这块主要靠特殊寄存器设定。前面提到的D1120是COM2的通信格式配好后要把它写入特殊寄存器才能生效。我习惯在程序初始化段用MOV指令写一次参数同时用M1120作为通信格式设定保持位确保上电后通信参数立即生效不依赖人机界面手动设。举例来说如果要把COM2配成9600、8、N、1梯形图里就这么写LD M1001 // 第一次扫描脉冲 MOV K96 D1120 // 9600波特率8/N/1格式 SET M1120 // 启用通信格式设定 MOV K50 D1125 // 通信超时时间50ms MOV K0 D1126 // 发送余量时间0msD1120的具体数值并不是直接写一个波特率数字进去而是用组合位编码。通常台达手册里会给一张速查表比如K96对应9600、8、N、1K97对应9600、7、E、1K86是19200、8、N、1。这里不要靠记忆编程时翻一下手册最稳因为不同系列的PLC编码表会有微调。另一个容易踩的坑是M1120必须置位如果不置位D1120里的配置不会生效PLC可能还是按出厂默认的4800去通信。4.2 轮询状态用MODRW指令还是RS指令台达PLC内置了MODRW指令专门用于Modbus通信比用RS指令自己拼帧要省心很多。MODRW格式大致是MODRW S1 S2 S3 S4 N MS1是通信端口地址S2是从站站号S3是通信指令功能码S4是起始寄存器地址N是读写个数M是PLC内保存数据的起始地址。比如要读1号从站的40003寄存器协议地址0x0002程序可以写成MODRW K2 K1 H03 H02 K1 D100意思是COM2口从站1号功能码03地址0x02读1个寄存器结果存到D100。当轮询指令发出后M1129会作为通信忙标志保持一段时间等M1129复位且M1143通信完成/异常标志建立时D100里才是有效数据。如果只看一次扫描就取D100大概率取到的是上一次的值。这里有个工程习惯问题不要在一个扫描周期里连续发多条MODRW否则会出现总线冲突。正确的做法是采用“一个周期发一条完成后再发下一条”的轮询方式用两个寄存器做发送标志位。轮询间隔建议留50ms以上让从站有足够时间响应也避免总线被问爆。4.3 支付状态机的梯形图设计支付状态机是整个PLC程序的灵魂。逻辑上可以这样拆空闲状态等待命令 → 通过人机界面按钮触发支付 → 先写金额地址再写启动指令 → 循环轮询状态寄存器 → 读到2成功就给输出点置位 → 设备放行 → 设备动作完成后再清除状态 → 回到空闲。梯形图的核心片段大概是这样以台达指令为例// 触发支付当M50从0变1且当前空闲(M60OFF) // 第一步写金额5元到40002 LD M50 ANI M60 ANI M61 SET M61 // 进入支付流程标志 MOV K500 D120 // 金额500分暂存 MODRW K2 K1 H06 H01 K1 D120 // 写40002500 SET M1129 // 等MODRW完成后写启动指令到40001 LD M61 ANI M1129 MOV K1 D121 MODRW K2 K1 H06 H00 K1 D121 // 写400011 SET M1129 // 轮询状态每500ms读一次40003 LD M61 ANI M1129 TMR T50 K50 // 500ms定时 LD T50 MODRW K2 K1 H03 H02 K1 D100 SET M1129 // 判断结果 LD M61 ANI M1129 CMP K2 D100 M62 // 如果D1002M63导通 LD M63 SET Y0 // 放行继电器 SET M64 // 支付成功标志 RST M61这段程序未必能直接抄到你自己的机型里因为不同品牌指令细节有差异但状态机的思路是通用的让状态之间靠标志位衔接严格执行“发一条、等完成、再发下一条”的节奏绝不在一个扫描周期里多头抢总线。4.4 数字量输出点控制继电器和变频器启动的区别项目做到一半有个朋友问我PLC的数字量输出点直接控制继电器和用开关量去启动变频器是不是一回事这里面的坑还挺值得展开的因为它在自助设备项目里真的很常见。如果是驱动中间继电器Y0输出24V DC给线圈线圈吸合后触点去闭合负载回路这时候接线相对简单关键是注意感性负载继电器线圈、接触器线圈两端要并联续流二极管不然断电瞬间的反电动峰会损伤PLC输出点。如果是直接用PLC开关量去启停变频器虽然变频器的多功能输入端子本质也是开关量但通常用的是PLC的晶体管输出漏型或源型要根据变频器给定信号类型选而且要注意PLC输出电压等级和变频器输入侧是否匹配。很多新工程师容易踩的一个坑是直接把变频器的RUN端子接到PLC输出点结果启动那一刻变频器报故障查了半天才发现是PLC输出电压不够或者公共端COM没接对。变频器输入回路的公共端一般要单独接入24V地或者0V不能跟PLC输出端子的COM混为一谈这个严格按照变频器手册接线就对了。另外在一些老项目里PLC输出的是继电器型触点接变频器数字输入时还得注意触点的电气寿命频繁启停的场合建议让PLC先去控制中间继电器再由继电器触点驱动变频器端子这样保护了PLC的输出继电器也隔离了变频器侧可能的干扰。5. 调试工具链与数据观测方法5.1 Modbus Poll和Modbus Slave主从站模拟神器刚接触Modbus调试的工程师我强烈建议在手里备两样软件Modbus Poll主站模拟和Modbus Slave从站模拟。这俩软件配对使用能把你从“对着设备瞎猜”中解放出来。Modbus Poll是用来模拟主站的它可以直接连接真实的从站设备比如扫码支付终端手动填写寄存器地址、功能码、轮询周期把所有寄存器的值实时刷出来。调PLC之前我用Modbus Poll先测一次支付终端的响应是否正常快速确定地址映射对不对、功能码支不支持这能排除掉“设备压根没配好”的干扰因素。Modbus Slave是用来模拟从站的在另一个极端场景里极有用——PLC程序没接真实支付终端时我用Modbus Slave模拟一个支付终端挂在485总线上让PLC先跑起来看程序逻辑通不通。等PLC侧的程序验证无误才把真实终端接上做联调。这两个工具切换着用能省下大量现场排查时间。这俩软件都有试用版网上也能找到注册文件但我不太建议为了省钱去搞破解生产环境用的工具稳定优先一个不稳定的调试软件会误导排查方向。5.2 串口调试助手的抓帧技巧串口调试助手的核心用途不是“看数据收没收到”而是“看报文对不对”。现场调试时把调试助手串接在PLC和支付终端之间用T型口并联也可注意485总线的物理限制把收到的hex帧完整记下来再去对照Modbus报文结构分析。我习惯的调试步骤是先用调试助手定点发一帧“01 03 00 02 00 01 25 CA”看终端有没有响应。如果有响应对照响应帧看寄存器的值是否符合预期。如果没有响应先用万用表量485的A/B电压差在2V以上基本说明物理链路正常再检查站号是否匹配。如果PLC连上后端子的响应时序不对重点看PLC的发送间隔和收发切换时间。实操中还有一个非常关键的点485是半双工同一时刻只能一方发送。如果调试助手串接在线路上建议调试助手自己不要开启“自动发送”功能也不要在PLC跑的时候用调试助手同时发帧不然总线冲突会导致两边都收不到数据。5.3 串口数据记录仪的应用场景很多工程师调试串口时长这样用各种串口工具抓一阵子数据盯着屏幕看遇到偶发性丢帧时屏幕上滚动的数据已经过去了根本来不及分析。这时候一台串口数据记录仪的价值就出来了。串口数据记录仪的本质是一个带时间戳的存储型抓包工具可以长时间记录串口上的所有收发数据并记录每一帧到达的精确时间。长时间挂机后发现偶发通信异常靠肉眼盯屏基本抓不到规律但用记录仪把一整晚的数据存下来第二天拿到电脑上分析就能看到是不是每多少分钟规律性地坏一帧还是深夜某个整点异常这样定位方向就会清晰很多。我之前在一个充电桩项目里碰到过支付模块偶发通信超时现场蹲了一下午没复现。后来用数据记录仪挂了一个晚上看到凌晨2点左右必丢一帧查了周围环境才发现附近有一个半整点断电的广告灯箱电源干扰隔着护栏直接耦合到了485线上。这种问题没有长时间抓帧记录真的很难定位。5.4 Linux串口收数丢失的处理思路现在不少自助终端方案不是直接用PLC而是PLC一块Linux工控板做上层业务逻辑PLC负责执行工控板负责页面和支付平台交互。这种情况下Linux下串口收数据丢失也是一个高频问题。Linux串口接收丢数据原因不外乎三个串口缓冲区溢出、应用程序读取不及时、低层驱动丢数据。常规处理手段是增大串口接收缓冲区、使用DMA方式接收、把串口读取放到独立线程中读完数据先放到内存队列业务逻辑从队列里取保证接收不被业务卡顿阻塞。如果是STM32这类单片机做中转串口DMA空闲中断接收是最稳的组合。接收到一帧完整数据后再判断帧起点、站号、CRC解析完才交给业务层。我在嵌入式端处理Modbus时也是沿用这套思路——中断或DMA只管收数据解析放在主循环或者独立任务里避免中断里做耗时的协议解析导致丢下一帧。6. 项目联调过程实录与现场经验6.1 上电联调的完整执行步骤现场联调时我推荐的顺序是“先独立验证、再单端联调、最后全链路联调”不要上来就把PLC、支付终端、云平台全打开一起跑出了问题根本不知道查谁。第一步单独给支付终端上电确认它能正常语音播报、显示二维码。用Modbus Poll连接它写一笔1元的支付金额触发支付流程拿手机扫一下看状态寄存器是否从“支付中”变成“支付成功”。这一步如果通了说明终端本身没问题Modbus从站功能正常寄存器映射也和手册一致。第二步把PLC接上只跑轮询逻辑。用Modbus Slave模拟支付终端接在485总线上让PLC连续读确认PLC侧读到的数据和Modbus Slave上填的数据一致。这样就把PLC侧的程序问题排除掉了。第三步接入真实支付终端去掉Modbus Slave。PLC直接轮询真实终端用调试助手串接看实际报文交互。最后再接入云平台做完整的“扫码付款→状态变化→PLC驱动继电器→设备动作”全链路验证。每一步都确认无误后再进入下一步任何一个环节异常都能立刻锁定。6.2 现场踩过的坑轮询太快导致总线拥塞这个项目第一次联调时我把轮询间隔设成了50ms。逻辑上看起来没问题50ms一个周期不算快终端响应也很快。但实跑起来出问题的是PLC在发出读命令后总线还没有完全释放下一轮又发出去了。Modbus RTU的帧与帧之间必须有一定的静默时间一般是3.5个字符时间两台设备之间如果间隔太短从站处理不过来就会出现应答超时、CRC错误交替出现的情况。后来我把轮询周期调到300ms加上通信组件自身的收发切换延时问题立刻消失。经验教训就是Modbus RTU不是越快越好主从之间要留够响应时间尤其当从站本身还要跟云平台做HTTPS通信时它内部的处理可能远远慢于串口波特率允许的速度这时候盲目缩短轮询周期只会自找麻烦。6.3 扫码支付成功后继电器不动作的排查过程联调过程中还遇到过一个有意思的问题状态寄存器明明已经读到“2”了梯形图判断也通过了但继电器就是没动作。第一反应是看程序结果逻辑没问题用万用表量Y0输出也有24V再量继电器线圈电压也有但线圈没吸合。后来拆开继电器外壳才发现线圈是好的但COM触点螺丝没拧紧或者说接线端子接触电阻太大导致电流不够驱动后级负载。这种问题在文档里写不出来的——所有电气参数看着都对但物理接触不良让信号传不过去。排查思路很直接从Y0输出逐级往后用电笔或者万用表量量到哪一级电压异常问题就在哪一级。不要一上来就怀疑程序先确认物理链路通不通。6.4 与其他品牌PLC的兼容性要点虽然这次用的台达DVP但很多工程师手头可能有三菱、西门子、汇川、欧姆龙这些品牌结构上是类似的。三菱FX系列走Modbus一般用RS指令按帧发送接收西门子S7-200 SMART自带Modbus RTU指令库直接调用MBUS_CTRL和MBUS_MSG汇川和台达的思路更接近有现成的Modbus轮询功能。不同品牌的寄存器地址填写也要小心。三菱和台达在加1问题上基本一致都是写协议地址西门子S7-200的Modbus库里面地址写法是40001风格的库内部会自动处理偏移。如果以后跨品牌接同样的扫码终端千万别把地址格式想当然地照搬先查对应品牌的指令手册确认好地址偏移规则。还有一个跨品牌常见问题是PLC程序扫描周期对通信的影响有些品牌MODRW指令在通信没完成时如果程序又扫描到同一个线圈会导致重发那就必须在通信忙标志位上下功夫用一个单独的置位复位逻辑来保证指令只在完成之后才会再次触发。7. 常见问题速查与最终经验小结为了便于现场排查我整理了一份高频故障速查表统一贴在工程调试记录本上遇到问题先对着表过一遍能省掉不少冤枉时间。现象可能原因优先排查手段收不到任何响应帧线序接反、站号不对、波特率不一致万用表量A/B电压差检查站号核对D1120参数收到响应但CRC总错波特率微偏差、干扰、收发切换时序不对用调试助手抓原始帧缩短线缆检查屏蔽接地偶发性丢帧、超时轮询间隔太短、供电不稳、电磁干扰调大轮询周期检查供电电源长时间抓帧记录状态寄存器读到固定值不变地址偏了一位或功能码用错用Modbus Poll单独验证地址对照报文hex值支付成功但继电器不动作物理接线接触不良后级供电不足从Y0逐级量电压电流排查触点氧化/虚接报文收发正常但PLC程序不触发扫描周期内标志位冲突检查通信忙标志位确认指令执行完成时序再说一个很多人容易忽略但很实用的经验凡是涉及485总线的项目终端电阻120欧一定不能随便乱接。短距离几十米内点对点通信不接终端电阻通常也能跑但一旦线拉长到百米或者现场干扰大不接终端电阻就会出现信号反射尤其在波特率9600下表现为偶发CRC错误。反之如果距离很近也把终端电阻两端都接了会让总线负载过重驱动能力弱的主站反而带不动从站。现场调试扫码支付对接说到底是一个“帧对帧”的验证过程。协议不理解直接拿设备互怼出现问题时两眼一抹黑理解了Modbus RTU报文的结构和时序按“独立验证—单端联调—全链路联调”的顺序推进问题都是可以被拆解定位的。我在这类项目上最大的体会是先把物理链路和参数确认到100%正确再谈程序逻辑和业务功能因为协议栈上层的问题再花哨根子多半都扎在最基础的接线和参数配置里。最后分享一个我个人的调试小习惯所有串口通信项目在调试阶段我都会强制自己保留一份hex报文日志哪怕当时觉得没用等系统跑了三个月突然出问题时这份日志就是回溯故障的宝贵线索。通信这东西玄学问题大多不是玄学只是你没在正确的层次上看到证据而已。
返回列表