11.5 Modbus RTU客户端编程与实验
瑞米派开发板作为client(主设备),去访问Modbus传感器(作为Server)。
本节源码位于如下目录:
11.5.1 硬件连接
硬件连接原理图如下:
实物连接图如下:
11.5.2 传感器点表
对于Modbus设备,怎么访问它们?它们的寄存器分别有什么功能?这在“点表”里描述,每个寄存器被称为一个“点”。
百问网的温湿度传感器的点表如下:
设备 地址 | 寄存器 地址 | 寄存器 类别 | 用途 | 描述 |
03H | 0000H | DO | 控制器蜂鸣1 | 1-响 |
0001H | DO | 控制器蜂鸣2 | 1-响 | |
0002H | DO | 控制LED1 | 1-亮 | |
0003H | DO | 控制LED2 | 1-亮 | |
0004H | DO | 控制LED3 | 1-亮 | |
0000H | AI | 读取温度 | 单位0.1摄氏度 16位有符号整数 | |
0001H | AI | 读取湿度 | 单位0.1%RH 16位有符合整数 |
11.5.3 程序解析
代码在如下目录里:
假设执行如下命令:
./modbus_client /dev/ttySC4 read
程序运行的情景分析如下
1. 初始化与连接
代码如下:
33 ctx = modbus_new_rtu(argv[1], 115200, 'N', 8, 1);
34 if (ctx == NULL) {
35 fprintf(stderr, "Unable to allocate libmodbus context\n");
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\n", modbus_strerror(errno));
43 modbus_free(ctx);
44 return -1;
45 }
第33行:分配一个modbus_t结构体。
第39行:设置要访问的Modbus传感器地址。
第41行:打开串口设备。
2. 读取传感器数据
代码如下:
47 if (!strcmp(argv[2], "read"))
48 {
49 while (1)
50 {
51 rc = modbus_read_input_registers(ctx, 0, 2, vals);
52 if (rc == 2)
53 {
54 printf("Temprature = %d.%dC, Humity = %d.%d%%\r\n", vals[0]/10, val
s[0]%10, vals[1]/10, vals[1]%10);
55 }
56 else
57 {
58 printf("modbus_read_input_registers err: %d, %s\r\n", rc, strerror
(errno));
59 }
60 sleep(2);
61 }
62 }
第51行:读取2个AI寄存器。
第54行:打印温湿度值。
11.5.4 上机实验
注意:假设你在“/home/ubuntu/apps/libmodbus-3.1.10”目录下编译了Libmodbus,并且在“/home/ubuntu/apps/libmodbus-3.1.10/tmp”目录下安装了Libmodbus。如果你的路径不一样,需要修改后续程序的Makefile。
把代码上传到Ubuntu。
然后,在Ubuntu下执行如下命令进行编译:
$ source /opt/remi-sdk/environment-setup-aarch64-poky-linux
$ make
$ scp modbus_client root@192.168.5.9:/mnt
最后,在开发板上执行如下命令:
# ./modbus_client /dev/ttySC4 beep1 on
modbus_write_bit ok
# ./modbus_client /dev/ttySC4 beep1 off
modbus_write_bit ok
# ./modbus_client /dev/ttySC4 led1 on
modbus_write_bit ok
# ./modbus_client /dev/ttySC4 led1 off
modbus_write_bit ok
# ./modbus_client /dev/ttySC4 read
Temprature = 32.0C, Humity = 39.2%
Temprature = 31.9C, Humity = 39.2%
11.6 Modbus RTU服务器端编程与实验
要把瑞米派当做一个Modbus服务器(传感器)来使用,需要有另一个开发板作为客户端。为了方便实验,本课程使用同一个瑞米派开发板,它运行2个程序:一个模拟服务器,另一个模拟客户端。
本节源码位于如下目录:
硬件框图与连接如下所示。
11.6.1 硬件连接
硬件连接原理图如下:
实物连接图如下:
11.6.2 程序解析
代码在如下目录里:
“modbus_server.c”代码解析如下。
1. 初始化与连接
代码如下:
40 ctx = modbus_new_rtu(argv[1], 115200, 'N', 8, 1);
41 if (ctx == NULL) {
42 fprintf(stderr, "Unable to allocate libmodbus context\n");
43 return -1;
44 }
45
46 modbus_set_slave(ctx, SERVER_ID);
47
48 mb_mapping = modbus_mapping_new_start_address(0,
49 NB_BITS, /* 5 个 DO 寄存器,对应 beep1,beep2,led1,led2,led3 */
50 0,
51 NB_INPUT_BITS,
52 0,
53 NB_REGISTERS,
54 0,
55 NB_INPUT_REGISTERS); /* 2 个 AI 寄存器,对应温度和湿度 */
56 memset(mb_mapping->tab_bits, 0, NB_BITS);
57 memset(mb_mapping->tab_input_registers, 0, NB_INPUT_REGISTERS*2);
58
59 memset(old_bits, 0, NB_BITS);
60 memset(old_regs, 0, NB_INPUT_REGISTERS*2);
61
62 if (modbus_connect(ctx) == -1) {
63 fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno));
64 modbus_free(ctx);
65 return -1;
66 }
第40行:分配一个modbus_t结构体。
第46行:设置自己的传感器地址。
第48~55行:分配Modbus寄存器。
第56~57行:设置DO、AI寄存器初始值为0。
第59~60行:设置2个数组的值为0,这2个数组将用来跟Modbus寄存器进行比较,这样才能知道Client程序有没有修改这些值。
第62行:打开串口设备。
2. 等待Client程序发来请求
代码如下:
68 while (1)
69 {
70 do {
71 rc = modbus_receive(ctx, query);
72 /* Filtered queries return 0 */
73 } while (rc == 0);
第71行:读取请求。
3. 模拟温湿度传感器
代码如下:
82 /* 使用随机数模拟温度、湿度 */
83 mb_mapping->tab_input_registers[0] = rand() % 1000; /* 温度,单位:0.1C */
84 mb_mapping->tab_input_registers[1] = rand() % 1000; /* 湿度,单位:0.1% */
4. 回复数据给Client
代码如下:
86 rc = modbus_reply(ctx, query, rc, mb_mapping);
87 if (rc == -1) {
88 //break;
89 }
如果Client发来的请求是读取温湿度值的话,那么第86行就会回复数据给它。
5. 模拟蜂鸣器和LED操作
代码如下:
91 /* 根据 client 设置的数值,假装操作蜂鸣器和 LED */
92 if (mb_mapping->tab_bits[0] != old_bits[0])
93 {
94 printf("set beep1 %s\r\n", 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\r\n", 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\r\n", 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\r\n", 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\r\n", mb_mapping->tab_bits[4] ? "on" : "off");
119 old_bits[4] = mb_mapping->tab_bits[4];
120 }
代码比较简单,不再赘述。
11.6.3 上机实验
把代码上传到Ubuntu。
然后,在Ubuntu下执行如下命令进行编译:
$ source /opt/remi-sdk/environment-setup-aarch64-poky-linux
$ make
$ scp modbus_client root@192.168.5.9:/home/root
$ scp modbus_server root@192.168.5.9:/home/root
最后,在开发板上执行如下命令(先执行 modbus_server):
# ./modbus_server /dev/ttySC2 &
# ./modbus_client /dev/ttySC4 beep1 on
modbus_write_bit ok
# ./modbus_client /dev/ttySC4 beep1 off
modbus_write_bit ok
# ./modbus_client /dev/ttySC4 led1 on
modbus_write_bit ok
# ./modbus_client /dev/ttySC4 led1 off
modbus_write_bit ok
# ./modbus_client /dev/ttySC4 read
Temprature = 64.9C, Humity = 42.1%
Temprature = 36.2C, Humity = 2.7%
Temprature = 69.0C, Humity = 5.9%
需要产品及方案支持
请扫码登记
如您在使用瑞萨MCU/MPU产品中有任何问题,可识别下方二维码或复制网址到浏览器中打开,进入瑞萨技术论坛寻找答案或获取在线技术支持。
https://community-ja.renesas.com/zh/forums-groups/mcu-mpu/
未完待续
推荐阅读
libmodbus情景分析 - RZ MPU工业控制教程连载(39)
主设备读取回应与常用接口函数 - RZ MPU工业控制教程连载(40)
Modbus接口与数据处理 - RZ MPU工业控制教程连载(41)
需要产品及方案支持
请扫码登记