Welcome
admin
admin

2025-06-21 16:15:39

世界杯误判
5800 948

13.3. libmodbus简介和测试¶

libmodbus是一个与使用modbus协议的设备进行数据 发送/接收 的库,

它包含各种后端(backends)通过不同网络进行通信

(例如,RTU模式下的串口、485总线或TCP / IPv6中的以太网)。

libmodbus还提供了较低通信层的抽象,并在所有支持的平台上提供相同的API。

libmodbus是开源的,它遵循 LGPL v2.1 开源协议,这个协议没有GPL协议那么严格,

简单来说,只要你不修改libmodbus库里面的东西(只调用、链接该库),你是可以闭源你的代码的,

你也可以用于商业用途,这是非常好的。

13.3.1. 使用libmodbus简单测试485通讯¶

开发准备,在开发板系统上安装libmodbus-dev和一些编译工具。

1

2sudo apt update

sudo apt install gcc git libmodbus-dev pkg-config

将开发板的串口和485连接,两个485连接:

13.3.2. 程序¶

1.服务端程序

stmp157_quick_start_guide_code/Source/485/test-server.c¶

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131 #include

#include

#include

#include

#include

#include

#include

#include

#define SERVER_GPIO_INDEX "403"

#define SERVER_ID 17

const uint16_t UT_REGISTERS_TAB[] = { 0x0A, 0x0E, 0x0A, 0x1B,0x0A};

static int _server_ioctl_init(void)

{

int fd;

//index config

fd = open("/sys/class/gpio/export", O_WRONLY);

if(fd < 0)

return 1;

write(fd, SERVER_GPIO_INDEX, strlen(SERVER_GPIO_INDEX));

close(fd);

//direction config

fd = open("/sys/class/gpio/gpio" SERVER_GPIO_INDEX "/direction", O_WRONLY);

if(fd < 0)

return 2;

write(fd, "out", strlen("out"));

close(fd);

return 0;

}

static int _server_ioctl_on(void)

{

int fd;

fd = open("/sys/class/gpio/gpio" SERVER_GPIO_INDEX "/value", O_WRONLY);

if(fd < 0)

return 1;

write(fd, "1", 1);

close(fd);

return 0;

}

static int _server_ioctl_off(void)

{

int fd;

fd = open("/sys/class/gpio/gpio" SERVER_GPIO_INDEX "/value", O_WRONLY);

if(fd < 0)

return 1;

write(fd, "0", 1);

close(fd);

return 0;

}

static void _modbus_rtu_server_ioctl(modbus_t *ctx, int on)

{

if (on) {

_server_ioctl_on();

} else {

_server_ioctl_off();

}

}

int main(int argc, char*argv[])

{

modbus_t *ctx;

modbus_mapping_t *mb_mapping;

int rc;

int i;

uint8_t *query;

/*设置串口信息*/

ctx = modbus_new_rtu("/dev/ttySTM1", 9600, 'N', 8, 1);

_server_ioctl_init();

/*设置从机地址,设置485模式*/

modbus_set_slave(ctx, SERVER_ID);

modbus_rtu_set_custom_rts(ctx, _modbus_rtu_server_ioctl);

modbus_rtu_set_rts(ctx, MODBUS_RTU_RTS_UP);

modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485);

query = malloc(MODBUS_RTU_MAX_ADU_LENGTH);

/*开启调试*/

modbus_set_debug(ctx, TRUE);

mb_mapping = modbus_mapping_new_start_address(0,0,0,0,0,5,0,0);

if (mb_mapping == NULL) {

fprintf(stderr, "Failed to allocate the mapping: %s\n",

modbus_strerror(errno));

modbus_free(ctx);

return -1;

}

/* 初始化值 */

for (i=0; i < 5; i++) {

mb_mapping->tab_registers[i] = UT_REGISTERS_TAB[i];

}

rc = modbus_connect(ctx);

if (rc == -1) {

fprintf(stderr, "Unable to connect %s\n", modbus_strerror(errno));

modbus_free(ctx);

return -1;

}

modbus_flush(ctx);

for (;;) {

do {

rc = modbus_receive(ctx, query);

} while (rc == 0);

rc = modbus_reply(ctx, query, rc, mb_mapping);

if (rc == -1) {

break;

}

}

modbus_mapping_free(mb_mapping);

free(query);

modbus_close(ctx);

modbus_free(ctx);

return 0;

}

2.客户端程序

stmp157_quick_start_guide_code/Source/485/test-client.c¶

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121 #include

#include

#include

#include

#include

#include

#include

#include

#define CLIENT_GPIO_INDEX "405"

#define SERVER_ID 17

static int _client_ioctl_init(void)

{

int fd;

//index config

fd = open("/sys/class/gpio/export", O_WRONLY);

if(fd < 0)

return 1 ;

write(fd, CLIENT_GPIO_INDEX, strlen(CLIENT_GPIO_INDEX));

close(fd);

//direction config

fd = open("/sys/class/gpio/gpio" CLIENT_GPIO_INDEX "/direction", O_WRONLY);

if(fd < 0)

return 2;

write(fd, "out", strlen("out"));

close(fd);

return 0;

}

static int _client_ioctl_on(void)

{

int fd;

fd = open("/sys/class/gpio/gpio" CLIENT_GPIO_INDEX "/value", O_WRONLY);

if(fd < 0)

return 1;

write(fd, "0", 1);

close(fd);

return 0;

}

static int _client_ioctl_off(void)

{

int fd;

fd = open("/sys/class/gpio/gpio" CLIENT_GPIO_INDEX "/value", O_WRONLY);

if(fd < 0)

return 1;

write(fd, "1", 1);

close(fd);

return 0;

}

static void _modbus_rtu_client_ioctl(modbus_t *ctx, int on)

{

if (on) {

_client_ioctl_on();

} else {

_client_ioctl_off();

}

}

int main(int argc, char *argv[])

{

modbus_t *ctx = NULL;

int i,rc;

uint16_t tab_rp_registers[5] = {0}; //定义存放数据的数组

/*创建一个RTU类型的变量*/

/*设置要打开的串口设备 波特率 奇偶校验 数据位 停止位*/

ctx = modbus_new_rtu("/dev/ttySTM3", 9600, 'N', 8, 1);

if (ctx == NULL) {

fprintf(stderr, "Unable to allocate libmodbus context\n");

return -1;

}

/*设置485模式*/

_client_ioctl_init();

modbus_rtu_set_custom_rts(ctx, _modbus_rtu_client_ioctl);

modbus_rtu_set_rts(ctx, MODBUS_RTU_RTS_DOWN);

modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485);

/*设置debug模式*/

modbus_set_debug(ctx, TRUE);

/*设置从机地址*/

modbus_set_slave(ctx, SERVER_ID);

/*RTU模式 打开串口*/

if (modbus_connect(ctx) == -1) {

fprintf(stderr, "Connection failed: %s\n", modbus_strerror(errno));

modbus_free(ctx);

return -1;

}

//读取多个连续寄存器

rc = modbus_read_registers(ctx, 0, 5, tab_rp_registers);

if (rc == -1)

{

fprintf(stderr,"%s\n", modbus_strerror(errno));

return -1;

}

for (i=0; i<5; i++)

{

//打印读取的数据

printf("reg[%d] = %d(0x%x)\n", i, tab_rp_registers[i], tab_rp_registers[i]);

}

modbus_close(ctx);

modbus_free(ctx);

return 0;

}

13.3.3. 编译和运行¶

将两个程序的源文件传输到开发板系统,然后使用下面命令编译:

1

2 gcc test-server.c -o test-server `pkg-config --cflags --libs libmodbus`

gcc test-client.c -o test-client `pkg-config --cflags --libs libmodbus`

打开两个终端,一个用于运行服务端一个用于运行客户端(ssh终端和串口终端)。如下所示:

服务终端¶

debian@lubancat:~$./test-server

客户终端¶

debian@lubancat:~$./test-client

在客户终端中最终打印

1

2

3

4

5

6

7

8

9

10

11

12 debian@lubancat:~$ ./utest-client

Opening /dev/ttySTM3 at 9600 bauds (N, 8, 1)

[11][03][00][00][00][05][87][59]

Sending request using RTS signal

Waiting for a confirmation...

<11><03><0A><00><0A><00><0E><00><0A><00><1B><00><0A><47>

reg[0] = 10(0xa)

reg[1] = 14(0xe)

reg[2] = 10(0xa)

reg[3] = 27(0x1b)

reg[4] = 10(0xa)

debian@lubancat:~$

在服务终端中最终打印

1

2

3

4

5

6

7

8 debian@lubancat:~$ ./test-server

Opening /dev/ttySTM1 at 9600 bauds (N, 8, 1)

Bytes flushed (0)

Waiting for a indication...

<11><03><00><00><00><05><87><59>

[11][03][0A][00][0A][00][0E][00][0A][00][1B][00][0A][E3][47]

Sending request using RTS signal

Waiting for a indication...

重要

简单测试485功能请按照13.2小节,使用libmodbus测试具体用法请读者自行研究。

参考资料:

https://github.com/stephane/libmodbus

https://libmodbus.org/documentation/