11.7 Modbus TCP编程与实验
本课程并没有支持Modbus TCP协议的传感器,所以使用将会编写2个程序:
①modbus_server_tcp.c:模拟一个Modbus TCP传感器
②modbus_client_tcp.c:操作传感器
程序结构如下图所示:
在硬件上无需进行任何特殊的连接。
本节源码位于如下目录:
下面以情景分析的方法讲解代码。假设在开发板上执行如下命令:
左右滑动查看完整内容
# ./modbus_server_tcp 127.0.0.1 & # ./modbus_client_tcp 127.0.0.1 led1 on
11.7.1 server初始化与等待连接
在“modbus_server_tcp.c”中,代码如下:
左右滑动查看完整内容
41 ctx = modbus_new_tcp(argv[1], 1502); 42 if (ctx == NULL) { 43 fprintf(stderr, "Unable to allocate libmodbus context "); 44 return -1; 45 } 46 47 //modbus_set_slave(ctx, SERVER_ID); 48 49 mb_mapping = modbus_mapping_new_start_address(0, 50 NB_BITS, /* 5 个 DO 寄存器,对应 beep1,beep2,led1,led2,led3 */ 51 0, 52 NB_INPUT_BITS, 53 0, 54 NB_REGISTERS, 55 0, 56 NB_INPUT_REGISTERS); /* 2 个 AI 寄存器,对应温度和湿度 */ 57 memset(mb_mapping->tab_bits, 0, NB_BITS); 58 memset(mb_mapping->tab_input_registers, 0, NB_INPUT_REGISTERS*2); 59 60 memset(old_bits, 0, NB_BITS); 61 memset(old_regs, 0, NB_INPUT_REGISTERS*2); 62 63 s = modbus_tcp_listen(ctx, 1); 64 modbus_tcp_accept(ctx, &s);
第41行:分配一个modbus_t结构体,里面含有IP和端口。
第47行:设置自己的传感器地址,这行被注释掉了,在Modbus TCP协议里,即使客户端使用不同的设备地址发来请求,server端都会接收到这些所有请求(它忽略设备地址)。
第49~56行:分配Modbus寄存器。它根据《11.5.2 传感器点表》来模拟一个传感器。
第57~58行:设置DO、AI寄存器初始值为0。
第60~61行:设置2个数组的值为0,这2个数组将用来跟Modbus寄存器进行比较,这样才能知道Client 程序有没有修改这些值。
第63~64行:这是跟Modbus RTU协议不同的地方,它们初始化socket,等待客户端连接。
11.7.2 client初始化与发起连接
在“modbus_client_tcp.c”中,代码如下:
左右滑动查看完整内容
33 ctx = modbus_new_tcp(argv[1], 1502); 34 if (ctx == NULL) { 35 fprintf(stderr, "Unable to allocate libmodbus context "); 36 return -1; 37 } 38 39 modbus_set_slave(ctx, SERVER_ID); 40 41 if (modbus_connect(ctx) == -1) { 42 fprintf(stderr, "Connection failed: %s ", modbus_strerror(errno)); 43 modbus_free(ctx); 44 return -1; 45 }
第33行:分配一个modbus_t结构体,设置IP和端口。
第39行:设置要访问的Modbus传感器地址。
第41行:发出连接请求。
11.7.3 server等待请求
在“modbus_server_tcp.c”中,代码如下:
左右滑动查看完整内容
66 while (1) 67 { 68 do { 69 rc = modbus_receive(ctx, query); 70 /* Filtered queries return 0 */ 71 } while (rc == 0); 72
第69行:等待client发来请求。
11.7.4 client 发出请求
在“modbus_client_tcp.c”中,代码如下:
左右滑动查看完整内容
65 if (!strcmp(argv[2], "beep1")) 66 addr = 0; 67 if (!strcmp(argv[2], "beep2")) 68 addr = 1; 69 if (!strcmp(argv[2], "led1")) 70 addr = 2; 71 if (!strcmp(argv[2], "led2")) 72 addr = 3; 73 if (!strcmp(argv[2], "led3")) 74 addr = 4; 75 76 if (addr == -1) 77 { 78 usage(argv[0]); 79 return -1; 80 } 81 82 if (!strcmp(argv[3], "on")) 83 status = 1; 84 else 85 status = 0; 86 87 rc = modbus_write_bit(ctx, addr, status); 88 if (rc == 1) 89 { 90 printf("modbus_write_bit ok "); 91 } 92 else 93 { 94 printf("modbus_write_bit err: %d, %s ", rc, strerror(errno)); 95 }
第65~85行:根据参数设置addr、status。
第87行:发出“写AO寄存器的请求”。
11.7.5 server处理请求并回应
在“modbus_server_tcp.c”中,代码如下:
左右滑动查看完整内容
75 if (rc >= 0) { 76 77 printf("Get query for UID %d ", query[6]); 78 79 /* 使用随机数模拟温度、湿度 */ 80 mb_mapping->tab_input_registers[0] = rand() % 1000; /* 温度,单位:0.1C */ 81 mb_mapping->tab_input_registers[1] = rand() % 1000; /* 湿度,单位:0.1% */ 82 83 rc = modbus_reply(ctx, query, rc, mb_mapping); 84 } 85 if (rc == -1) { 86 printf("Connection closed! "); 87 modbus_close(ctx); 88 modbus_tcp_accept(ctx, &s); 89 } 90 91 /* 根据 client 设置的数值,假装操作蜂鸣器和 LED */ 92 if (mb_mapping->tab_bits[0] != old_bits[0]) 93 { 94 printf("set beep1 %s ", mb_mapping->tab_bits[0] ? "on" : "off"); 95 old_bits[0] = mb_mapping->tab_bits[0]; 96 } 97 98 if (mb_mapping->tab_bits[1] != old_bits[1]) 99 { 100 printf("set beep2 %s ", mb_mapping->tab_bits[1] ? "on" : "off"); 101 old_bits[1] = mb_mapping->tab_bits[1]; 102 } 103 104 if (mb_mapping->tab_bits[2] != old_bits[2]) 105 { 106 printf("set led1 %s ", mb_mapping->tab_bits[2] ? "on" : "off"); 107 old_bits[2] = mb_mapping->tab_bits[2]; 108 } 109 110 if (mb_mapping->tab_bits[3] != old_bits[4]) 111 { 112 printf("set led2 %s ", mb_mapping->tab_bits[4] ? "on" : "off"); 113 old_bits[3] = mb_mapping->tab_bits[4]; 114 } 115 116 if (mb_mapping->tab_bits[4] != old_bits[4]) 117 { 118 printf("set led3 %s ", mb_mapping->tab_bits[4] ? "on" : "off"); 119 old_bits[4] = mb_mapping->tab_bits[4]; 120 }
第77行:打印client端发来的请求包里的“设备地址”,你可以根据这个“设备地址”去操作不同的设备,本程序未使用它。
第80~81行:使用随机数填充AO寄存器模拟温湿度。如果client读取温湿度的话,下面第83行的“modbus_reply”就会回复这些温湿度值。
第83行:使用“modbus_reply”发出回复包给client。
第85~89行:如果出错,重新等待client建立连接。
第91~120行:根据client发来的数据,操作硬件(这里仅仅是打印信息)。
11.7.6 上机实验
把代码上传到Ubuntu。
然后,在Ubuntu下执行如下命令进行编译:
左右滑动查看完整内容
$ source /opt/remi-sdk/environment-setup-aarch64-poky-linux $ make $ scp modbus_client_tcp root@192.168.5.9:/home/root $ scp modbus_server_tcp root@192.168.5.9:/home/root
最后,在开发板上执行如下命令(先执行 modbus_server):
左右滑动查看完整内容
# cd /home/root # ./modbus_server_tcp 127.0.0.1 & # ./modbus_client_tcp 127.0.0.1 led1 on Get query for UID 4 set led1 on modbus_write_bit ok Connection closed! # ./modbus_client_tcp 127.0.0.1 read Get query for UID 4 Temprature = 38.6C, Humity = 49.2% Get query for UID 4 Temprature = 64.9C, Humity = 42.1% Get query for UID 4 Temprature = 36.2C, Humity = 2.7%
需要产品及方案支持
全部0条评论
快来发表一下你的评论吧 !