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%
需要產品及方案支持
-
傳感器
+關注
關注
2576文章
55021瀏覽量
791216 -
MODBUS
+關注
關注
28文章
2456瀏覽量
83179 -
編程
+關注
關注
90文章
3716瀏覽量
97177 -
TCP
+關注
關注
8文章
1424瀏覽量
83495
發布評論請先 登錄
Modbus TCP通信報文解析
Modbus TCP Server程序開發與Yocto系統構建
LabVIEW Modbus TCP通訊
Modbus TCP轉Modbus RTU的實現
Modbus TCP如何使用
如何快速實現Modbus RTU和Modbus TCP協議轉換?
Modbus/TCP通訊配置
Modbus RTU和Modbus TCP的區別
ModBus RTU 與 ModBus TCP
MODBUS TCP 轉 CANOpen
Modbus TCP編程與實驗
評論