TRIO-Basic 从入门到精通(十):UDP通讯实现网络串口化测试与应用
👁 3
分类:机器人自动化
本教程讲解TRIO控制器通过UDP通讯实现网络串口化的完整方案:使用OPEN#20 AS "dgram:IP(端口)"指令发送UDP数据包,WAIT UNTIL实现20ms定时播报,再用Wireshark与WinPcap上位机程序抓包验证。实测数据包准时稳定、无丢包,适用于控制器与第三方设备实时数据交互。
TRIO-Basic 从入门到精通(十):UDP通讯实现网络串口化测试与应用
一、教程背景
在工业控制场合,经常需要向第三方硬件或电脑快速、实时地发送数据,并且往往无法预先确认连接是否成功建立。传统做法是使用串口,但在没有串口网络环境的情况下,UDP 通讯是很好的替代方案。UDP(用户数据报协议)是一种无连接的传输层协议(IETF RFC 768,IP 报文协议号 17),提供面向事务的简单不可靠信息传送服务;它虽然在大数据分割发送时不能保证顺序,但对工业控制领域的数据播报场景已足够满足要求。本讲在 TRIO 控制器上用 TRIO-Basic 实现 UDP 数据定时发送(网络串口化),并用 Wireshark 与 WinPcap 上位机程序完成收包测试,适用于控制器与第三方设备/上位机的实时数据交互场景。
二、技术要点/技术方案
- UDP 通讯指令:在 TRIO-Basic 中通过
OPEN #n AS "dgram:目标IP(端口)" FOR READ_WRITE打开 UDP 数据报通道,用PRINT #n,字符串发送数据。 - 定时发送:使用
WAIT UNTIL cal_timept-TICKS >= send_timept实现阻塞计时,按固定时间间隔(本实验 20ms)周期发送数据包。 - 报文格式:示例报文以 "XG" 开头 + 6 位序号(STR(cal_allnum,0,6))+ 回车换行(CHR(13)+CHR(10)),方便接收端识别与校验。
- 网络抓包验证:电脑端安装 Wireshark(示例版本 Wireshark-win32-2.0.5.exe)抓取 UDP 数据包,验证数据是否实时、稳定、不丢包。
- 上位机收包:使用 WinPcap 开发包(WinPcap_4_1_3.exe 驱动 + WpdPack_4_1_2.zip 库,开发环境 Microsoft Visual Studio 2010),用 C 语言解析 IP/UDP 帧头,识别 "XG" 标记的数据并打印来源 IP、端口与报文内容。
三、实施步骤
- 编写并下载控制器发送程序:
- 先
CLOSE#20关闭端口并延时等待; OPEN#20 AS "dgram:192.168.0.10(8080)" FOR READ_WRITE打开 UDP 通道,向 192.168.0.10 的 8080 端口广播数据;- 初始化发送计数,进入循环:用阻塞计时实现 20ms 定时,构造 "XG+6位序号+回车换行" 报文,
PRINT #20,str_array发送。
- 先
- 在电脑端安装 Wireshark 并启动抓包,确认实时收到来自控制器的数据包,收包时间稳定、无丢包。
- 搭建上位机收包程序(VS2010 + WinPcap):
- 定义 IP 帧头结构(ip_header,24 字节)与 UDP 帧头结构(udp_header,8 字节);
- 用
pcap_findalldevs_ex获取网卡列表,选择连接控制器的网卡; pcap_open以混杂模式打开网卡,pcap_compile+pcap_setfilter设置 "ip and udp" 过滤条件;pcap_loop启动循环捕获,在回调函数packet_handler中解析以太网帧(偏移 14 字节定位 IP 头)、IP 头(ver_ihl&0xf)*4 定位 UDP 头,读取 UDP 数据前两位判断是否为 "XG" 标记,命中则打印时间戳、源/目标 IP、端口及报文内容。
- 运行上位机程序,选择实际连接控制器的网卡,观察收包结果。
- 结论验证:数据包准时收到,未发生丢包和数据错误。
四、代码示例
控制器发送端(TRIO-Basic):
CLOSE#20
WA(100)
OPEN#20 AS "dgram:192.168.0.10(8080)" FOR READ_WRITE
WA(50)
DIM send_timept,cal_timept,cal_allnum AS INTEGER
DIM str_array AS STRING(20)
str_array="XG"
str_array=str_array+"000000"
str_array=str_array+CHR(13)+CHR(10)
PRINT #0,str_array
cal_allnum=0
TICKS=0
cal_timept=TICKS
send_timept=20
WHILE TRUE
WAIT UNTIL cal_timept-TICKS >=send_timept
cal_timept=cal_timept-send_timept
cal_allnum=cal_allnum+1
str_array="XG"
str_array=str_array+STR(cal_allnum,0,6)
' str_array=str_array+"000000"
str_array=str_array+CHR(13)+CHR(10)
' PRINT #0,str_array
PRINT #20,str_array
WEND
CLOSE#20
上位机收包端(C + WinPcap):
//=================================================================
//测试标题:UDP测试
//测试时间:2017-11-10
//测试人 :桂志微
//公司 :上海摩习恩自动化工程有限公司
//=================================================================
#include "pcap.h"
//IP地址
typedef struct ip_address{
u_char byte1;
u_char byte2;
u_char byte3;
u_char byte4;
}ip_address; //4字节
//IPV4帧头结构
typedef struct ip_header{
u_char ver_ihl; // 版本信息4BIT 信息长度4BIT 1字节
u_char tos; // 服务类型 1字节
u_short tlen; // 总长度 2字节
u_short identification; // 数据包标识 2字节
u_short flags_fo; // 标记3BIT 帧偏移13BIT 2字节
u_char ttl; // 活动时间 1字节
u_char proto; // 协议类型 1字节
u_short crc; // 校验 2字节
ip_address saddr; // 源IP地址 4字节
ip_address daddr; // 目标地址 4字节
u_int op_pad; // 填充选项 4字节
}ip_header; //24字节
//UDP帧头
typedef struct udp_header{
u_short sport; // 源端口 2字节
u_short dport; // 目标端口 2字节
u_short len; // 数据帧长度 2字节
u_short crc; // 校验 2字节
}udp_header; //8字节
//响应数据包句柄
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);
int main()
{
//网卡驱动
pcap_if_t *alldevs;
pcap_if_t *d;
int inum;
int i=0;
pcap_t *adhandle;
char errbuf[PCAP_ERRBUF_SIZE];
u_int netmask;
char packet_filter[] = "ip and udp";
struct bpf_program fcode;
//获取所有网卡驱动链表
if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
{
fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
exit(1);
}
//打印所有网卡驱动链表
for(d=alldevs; d; d=d->next)
{
printf("%d. %s", ++i, d->name);
if (d->description)
printf(" (%s)\n", d->description);
else
printf(" (No description available)\n");
}
if(i==0)
{
printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
return -1;
}
//选择驱动网卡
printf("Enter the interface number (1-%d):",i);
scanf_s("%d", &inum);
if(inum < 1 || inum > i)
{
printf("\nInterface number out of range.\n");
/* Free the device list */
pcap_freealldevs(alldevs);
return -1;
}
//定位选择网卡驱动
for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);
//打开网卡驱动
if ( (adhandle= pcap_open(d->name, // 驱动名称
65536, // 捕获数据包
// 存放数据包长度65536
PCAP_OPENFLAG_PROMISCUOUS, // 混合模式
1000, // 读取超时
NULL, // 远程身份验证
errbuf // 错误缓冲
) ) == NULL)
{
fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n");
//释放驱动链表
pcap_freealldevs(alldevs);
return -1;
}
//链路层检查,只支持以太网链路层
if(pcap_datalink(adhandle) != DLT_EN10MB)
{
fprintf(stderr,"\nThis program works only on Ethernet networks.\n");
//释放驱动链表
pcap_freealldevs(alldevs);
return -1;
}
if(d->addresses != NULL)
//获取接口首地址掩码
netmask=((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;
else
//如果接口没有地址,我们提供C类网络掩码
netmask=0xffffff;
//编译过滤器
if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) <0 )
{
fprintf(stderr,"\nUnable to compile the packet filter. Check the syntax.\n");
//释放驱动链表
pcap_freealldevs(alldevs);
return -1;
}
//设置过滤器
if (pcap_setfilter(adhandle, &fcode)<0)
{
fprintf(stderr,"\nError setting the filter.\n");
//释放驱动链表
pcap_freealldevs(alldevs);
return -1;
}
printf("\nlistening on %s...\n", d->description);
//释放驱动链表
pcap_freealldevs(alldevs);
//启动循环捕获
pcap_loop(adhandle, 0, packet_handler, NULL);
return 0;
}
//响应数据包句柄-每个数据包进行回调
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
struct tm ltime;
char timestr[16];
char Bmark[20];
ip_header *ih;
udp_header *uh;
u_int ip_len;
u_short sport,dport;
time_t local_tv_sec;
int indexP=0;
//将时间戳转换为可读格式
local_tv_sec = header->ts.tv_sec;
localtime_s(<ime, &local_tv_sec);
strftime( timestr, sizeof timestr, "%H:%M:%S", <ime);
//打印时间戳和包的长度
printf("%s.%.6d len:%d ", timestr, header->ts.tv_usec, header->len);
//pkt_data数据帧
//定位以太网IP帧位置
ih = (ip_header *) (pkt_data +14);
//定位UDP数据帧位置
ip_len = (ih->ver_ihl & 0xf) * 4;
uh = (udp_header *) ((u_char*)ih + ip_len);
//从网络字节顺序转换为主机字节顺序
sport = ntohs( uh->sport );
dport = ntohs( uh->dport );
//定位UDP数据帧
Bmark[0]=*((u_char*)uh + 8+0);
Bmark[1]=*((u_char*)uh + 8+1);
if(Bmark[0]=='X' && Bmark[1]=='G')
{
for(indexP=0;indexP<8;indexP++)
{
Bmark[indexP]=*((u_char*)uh + 8+indexP);
}
Bmark[indexP]='\0';
//打印数据包消息
//打印ip地址和udp端口
printf("%d.%d.%d.%d.%d -> %d.%d.%d.%d.%d:%s\n",
ih->saddr.byte1,
ih->saddr.byte2,
ih->saddr.byte3,
ih->saddr.byte4,
sport,
ih->daddr.byte1,
ih->daddr.byte2,
ih->daddr.byte3,
ih->daddr.byte4,
dport,
Bmark);
}
}
五、应用价值
- UDP 无连接特性适合向未知连接状态下的第三方设备稳定播报数据,实现"网络串口化"。
- 定时发送机制(20ms 周期)保证数据实时性,实测收包稳定、无丢包、无数据错误。
- 报文带标记(XG)与序号,接收端可方便识别与校验数据完整性。
- 上位机基于 WinPcap 抓包解析,可精确获取数据来源 IP、端口与内容,便于系统集成与排障。
- 全程实测验证(Wireshark + 上位机程序双通道确认),方案可直接复用到工业现场。
六、SEO关键词
TRIO运动控制器,TRIO-Basic,UDP通讯,网络串口化,数据报协议,WinPcap,Wireshark,定时发送,上位机抓包,OPEN指令
本文内容整理自 guimotion 的《TRIO-Basic从入门到精通教程》系列(CSDN,CC BY-SA 4.0),由 ZeroOne AI 重新组织编写。
