【modbus开发】入门教学与协议简介

STEP1(下载):

libmodbus官网: https://libmodbus.org/

想在Win32下使用的用户,别急着去网上找各种渠道,其实很方便。进: https://github.com/stephane/libmodbus

下载项目(zip格式)。


STEP2(编译):

解压后,在src/win32/ 中,找到configure.js,双击。然后用VS201X打开modbus-9.sln,生成->重新生成XXX。

然后在src/win32/ 中,就有了modbus.dll 和 modbus.lib。


STEP3(配置):

在你的C++工程中,进入“属性管理器”,新建一张属性表“modbus.prop”。

1.VC++目录,包含目录,把XXX/XXX/src路径加进去。

2.VC++目录,库目录,把XXX/XXX/src/win32路径加进去。

3.链接器,输入,附加依赖项,写入modbus.lib


STEP4(测试):

新建一个test.cpp文件。

#include <modbus.h>

配置成功的话,就不会报错。


STEP5(学习资源选择):

虽然看协议的大段大段的文字比较标准,但这里推荐直接拿 XXX/tests 中的

random-test-client.c

random-test-server.c

测试用例作为学习资源。

同时推荐一篇博客: https://www.cnblogs.com/duanweishi/p/9351916.html

与一篇API中文翻译博客: https://blog.csdn.net/qq_23670601/article/details/82155378

(在此翻译文中,CTRL+F搜索“0x16”, 这里翻译写错了,应该改为“0x10”,可以看英文原版确认)

英文原版doc:https://libmodbus.org/documentation/


STEP6(创建两个项目):

步骤5中已经挑了两个.c文件,分别为这两个文件创建win32控制台工程,这里取名为modbus-server 与 modbus-client

各自将步骤3中创建的"modbus.prop"属性表添加进属性管理器。

可能遇到找不到"unistd.h"头文件,别慌。

自己新建一个unistd.h,把下面这段话复制进去。

#ifndef _UNISTD_H
#define _UNISTD_H
#include <io.h>
#include <process.h>
#endif /* _UNISTD_H */

然后把那个 #include <unistd.h> 改为 #include "unistd.h"


STEP7(运行测试例):

首先,在server.c中,找到

close(s);

将其注释掉。

(这里我只是为了防止其报错,如果你有更好的办法,欢迎留言告诉我,我对这个报错不是很懂。)

这里是tcp的测试,所以

先启动server。

再启动client。

希望你能在client最后看到 SUCCESS。

如果不是,尝试把

for (addr = ADDRESS_START; addr <= ADDRESS_END; addr++) {
...
}

中的 "<=" 改为 "<"

再重新试一下。

看到一堆的数字总感觉很乱,出于学习的目的,你可以先把ADDRESS_END定义得小一点,我设了3,随便你。

#define ADDRESS_END     3

但是这样输出的东西还是有点看着累,容易看叉,你可以自己加上点文字输出。

我的版本如下,不嫌弃的话可以拷进去:

// ...client.cpp
// ...省略部分同原文件

int main(void)
{
	modbus_t *ctx;
	int rc;
	int nb_fail;
	int nb_loop;
	int addr;
	int nb;
	uint8_t *tab_rq_bits;
	uint8_t *tab_rp_bits;
	uint16_t *tab_rq_registers;
	uint16_t *tab_rw_rq_registers;
	uint16_t *tab_rp_registers;

	/* RTU */
	/*
	ctx = modbus_new_rtu("/dev/ttyUSB0", 19200, 'N', 8, 1);
	modbus_set_slave(ctx, SERVER_ID);
	*/

	/* TCP */
	ctx = modbus_new_tcp("127.0.0.1", 1502);
	modbus_set_debug(ctx, TRUE);

	if (modbus_connect(ctx) == -1) {
		fprintf(stderr, "Connection failed: %s\n",
			modbus_strerror(errno));
		modbus_free(ctx);
		return -1;
	}

	/* Allocate and initialize the different memory spaces */
	nb = ADDRESS_END - ADDRESS_START;

	tab_rq_bits = (uint8_t *)malloc(nb * sizeof(uint8_t));
	memset(tab_rq_bits, 0, nb * sizeof(uint8_t));

	tab_rp_bits = (uint8_t *)malloc(nb * sizeof(uint8_t));
	memset(tab_rp_bits, 0, nb * sizeof(uint8_t));

	tab_rq_registers = (uint16_t *)malloc(nb * sizeof(uint16_t));
	memset(tab_rq_registers, 0, nb * sizeof(uint16_t));

	tab_rp_registers = (uint16_t *)malloc(nb * sizeof(uint16_t));
	memset(tab_rp_registers, 0, nb * sizeof(uint16_t));

	tab_rw_rq_registers = (uint16_t *)malloc(nb * sizeof(uint16_t));
	memset(tab_rw_rq_registers, 0, nb * sizeof(uint16_t));

	nb_loop = nb_fail = 0;
	while (nb_loop++ < LOOP) {
		for (addr = ADDRESS_START; addr < ADDRESS_END; addr++) {
			int i;

			/* Random numbers (short) */
			for (i = 0; i < nb; i++) {
				tab_rq_registers[i] = (uint16_t)(65535.0*rand() / (RAND_MAX + 1.0));
				printf("tab_rq_registers[%d]: %d    0x%X\n", i, tab_rq_registers[i], tab_rq_registers[i]);
				tab_rw_rq_registers[i] = ~tab_rq_registers[i];
				printf("tab_rw_rq_registers[%d]: %d    0x%X\n", i, tab_rw_rq_registers[i], tab_rw_rq_registers[i]);
				tab_rq_bits[i] = tab_rq_registers[i] % 2;
				printf("tab_rq_bits[%d]: %d    0x%X\n", i, tab_rq_bits[i], tab_rq_bits[i]);
			}
			printf("\n");
			nb = ADDRESS_END - addr;

			/* WRITE BIT */
			printf("Start write bit...\n");
			rc = modbus_write_bit(ctx, addr, tab_rq_bits[0]);
			if (rc != 1) {
				printf("ERROR modbus_write_bit (%d)\n", rc);
				printf("Address = %d, value = %d\n", addr, tab_rq_bits[0]);
				nb_fail++;
			}
			else {
				printf("Start read bits...1bit...\n");
				rc = modbus_read_bits(ctx, addr, 1, tab_rp_bits);
				if (rc != 1 || tab_rq_bits[0] != tab_rp_bits[0]) {
					printf("ERROR modbus_read_bits single (%d)\n", rc);
					printf("address = %d\n", addr);
					nb_fail++;
				}
			}

			/* MULTIPLE BITS */
			printf("Start write bits...\n");
			rc = modbus_write_bits(ctx, addr, nb, tab_rq_bits);
			if (rc != nb) {
				printf("ERROR modbus_write_bits (%d)\n", rc);
				printf("Address = %d, nb = %d\n", addr, nb);
				nb_fail++;
			}
			else {
				printf("Start read bits...%dbits\n", nb);
				rc = modbus_read_bits(ctx, addr, nb, tab_rp_bits);
				if (rc != nb) {
					printf("ERROR modbus_read_bits\n");
					printf("Address = %d, nb = %d\n", addr, nb);
					nb_fail++;
				}
				else {
					for (i = 0; i < nb; i++) {
						if (tab_rp_bits[i] != tab_rq_bits[i]) {
							printf("ERROR modbus_read_bits\n");
							printf("Address = %d, value %d (0x%X) != %d (0x%X)\n",
								addr, tab_rq_bits[i], tab_rq_bits[i],
								tab_rp_bits[i], tab_rp_bits[i]);
							nb_fail++;
						}
					}
				}
			}

			/* SINGLE REGISTER */
			printf("Start write register...\n");
			rc = modbus_write_register(ctx, addr, tab_rq_registers[0]);
			if (rc != 1) {
				printf("ERROR modbus_write_register (%d)\n", rc);
				printf("Address = %d, value = %d (0x%X)\n",
					addr, tab_rq_registers[0], tab_rq_registers[0]);
				nb_fail++;
			}
			else {
				printf("Start read registers...1register...\n");
				rc = modbus_read_registers(ctx, addr, 1, tab_rp_registers);
				printf("\n%d...\n", tab_rp_registers[0]);

				if (rc != 1) {
					printf("ERROR modbus_read_registers single (%d)\n", rc);
					printf("Address = %d\n", addr);
					nb_fail++;
				}
				else {
					if (tab_rq_registers[0] != tab_rp_registers[0]) {
						printf("ERROR modbus_read_registers single\n");
						printf("Address = %d, value = %d (0x%X) != %d (0x%X)\n",
							addr, tab_rq_registers[0], tab_rq_registers[0],
							tab_rp_registers[0], tab_rp_registers[0]);
						nb_fail++;
					}
				}
			}

			/* MULTIPLE REGISTERS */
			printf("Start write registers...%dregisters...\n", nb);
			rc = modbus_write_registers(ctx, addr, nb, tab_rq_registers);
			if (rc != nb) {
				printf("ERROR modbus_write_registers (%d)\n", rc);
				printf("Address = %d, nb = %d\n", addr, nb);
				nb_fail++;
			}
			else {
				printf("Start read registers...%dregisters...\n", nb);
				rc = modbus_read_registers(ctx, addr, nb, tab_rp_registers);
				if (rc != nb) {
					printf("ERROR modbus_read_registers (%d)\n", rc);
					printf("Address = %d, nb = %d\n", addr, nb);
					nb_fail++;
				}
				else {
					for (i = 0; i < nb; i++) {
						if (tab_rq_registers[i] != tab_rp_registers[i]) {
							printf("ERROR modbus_read_registers\n");
							printf("Address = %d, value %d (0x%X) != %d (0x%X)\n",
								addr, tab_rq_registers[i], tab_rq_registers[i],
								tab_rp_registers[i], tab_rp_registers[i]);
							nb_fail++;
						}
					}
				}
			}
			/* R/W MULTIPLE REGISTERS */
			printf("Start write_read registers...%dregisters...\n", nb);
			rc = modbus_write_and_read_registers(ctx,
				addr, nb, tab_rw_rq_registers,
				addr, nb, tab_rp_registers);
			if (rc != nb) {
				printf("ERROR modbus_read_and_write_registers (%d)\n", rc);
				printf("Address = %d, nb = %d\n", addr, nb);
				nb_fail++;
			}
			else {
				for (i = 0; i < nb; i++) {
					if (tab_rp_registers[i] != tab_rw_rq_registers[i]) {
						printf("ERROR modbus_read_and_write_registers READ\n");
						printf("Address = %d, value %d (0x%X) != %d (0x%X)\n",
							addr, tab_rp_registers[i], tab_rw_rq_registers[i],
							tab_rp_registers[i], tab_rw_rq_registers[i]);
						nb_fail++;
					}
				}

				printf("Start read registers...%dregister...\n", nb);
				rc = modbus_read_registers(ctx, addr, nb, tab_rp_registers);
				if (rc != nb) {
					printf("ERROR modbus_read_registers (%d)\n", rc);
					printf("Address = %d, nb = %d\n", addr, nb);
					nb_fail++;
				}
				else {
					for (i = 0; i < nb; i++) {
						if (tab_rw_rq_registers[i] != tab_rp_registers[i]) {
							printf("ERROR modbus_read_and_write_registers WRITE\n");
							printf("Address = %d, value %d (0x%X) != %d (0x%X)\n",
								addr, tab_rw_rq_registers[i], tab_rw_rq_registers[i],
								tab_rp_registers[i], tab_rp_registers[i]);
							nb_fail++;
						}
					}
				}
			}
		}

		printf("Test: ");
		if (nb_fail)
			printf("%d FAILS\n", nb_fail);
		else
			printf("SUCCESS\n");
	}

	/* Free the memory */
	free(tab_rq_bits);
	free(tab_rp_bits);
	free(tab_rq_registers);
	free(tab_rp_registers);
	free(tab_rw_rq_registers);

	/* Close the connection */
	modbus_close(ctx);
	modbus_free(ctx);


	system("pause");
	return 0;
}
// ...server.cpp
// ...省略部分同原文件

int main(void)
{
	int s = -1;
	modbus_t *ctx;
	modbus_mapping_t *mb_mapping;

	ctx = modbus_new_tcp("127.0.0.1", 1502);
	/* modbus_set_debug(ctx, TRUE); */

	mb_mapping = modbus_mapping_new(500, 500, 500, 500);
	if (mb_mapping == NULL) {
		fprintf(stderr, "Failed to allocate the mapping: %s\n",
			modbus_strerror(errno));
		modbus_free(ctx);
		return -1;
	}

	s = modbus_tcp_listen(ctx, 1);

	printf("listening: %s\n", modbus_strerror(errno));
	printf("s:	%d\n", s);

	modbus_tcp_accept(ctx, &s);

	printf("acceptted: %s\n", modbus_strerror(errno));
	printf("s:	%d\n", s);
	for (;;) {
		uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
		int rc;

		rc = modbus_receive(ctx, query);

		for (int i = 0; i < rc; ++i) {
			printf("query[%d]: %d\n", i, query[i]);
		}
		if (rc > 0) {
			/* rc is the query size */
			modbus_reply(ctx, query, rc, mb_mapping);
		}
		else if (rc == -1) {
			/* Connection closed by the client or error */
			break;
		}

		printf("\n");
	}

	printf("Quit the loop: %s\n", modbus_strerror(errno));

	if (s != -1) {
		// close(s);
	}
	modbus_mapping_free(mb_mapping);
	modbus_close(ctx);
	modbus_free(ctx);

	system("pause");
	return 0;
}

STEP8(看结果,学协议):

主要分析的是client这个页面上的输出。这些输出呢,主要是靠

modbus_set_debug(ctx, TRUE);

这行代码。具体可参阅doc。

以“Connecting to 127.0.0.1:1502”称为第1行。

结合代码,不难得出,我们得到了3个随机数,分别是81,36933,12667。分别位于第2行,第5行,第8行。

还有其反码,以及 %2 求余的结果(3个都是奇数,所以求余都是1)。


STEP9(开始看API相关的输出):

我们看到Start write bit...这一段。以"Start write bit..."为第1行。

看第2行,[00][01][00][00][00][06][FF][05][00][00][FF][00]

字节byte 与 位bit 大家应该要有底。16进制,10进制,2进制别懵了。

这里总共12字节。我用颜色已经对其进行了区分。

[00][01]:消息号。就像快递单号。

[00][00]:强制,指明为modbus协议。

[00][06]:指明后面有 6 个字节([FF][05][00][00][FF][00]),是6个吧

[FF]:站号。00~FF都可以,这里API给我们设置的是FF,先不用管。

[05]:功能码。这是不同命令的区分依据。

(我们打开API的翻译doc,或者英文doc。找到 modbus_write_bit函数。可以看到内部调用0x05命令。)

[00][00]:将被写入数据的地址。

[FF][00]: modbus_write_bit 中,设置的是线圈的值。FF00是设为通,0000是设为断。


然后第3行是Waiting for a confirmation...

我们看一眼server的那个命令行,里面 modbus_receive 函数收到的就是client发送的码。然后再用 modbus_reply 函数做出应答。

对于 write 操作,返回的码没什么意思,取收到的码的前12个字节,返回给client。

于是在client中的第4行中,又显示了一遍。

至此,modbus_write_bit 的发送与应答就完成了。


下一个在client中执行的是 modbus_read_bits,

流程跟上面一样,我们看第6行。

[00][02][00][00][00][06][FF][01][00][00][00][01]

server端对于不同的功能码,有不同的解析。

[00][02]:消息号。

[00][00]:强制,指明为modbus协议。

[00][06]:指明后面有 6 个字节([FF][01][00][00][00][01]),是6个吧

[FF]:站号。00~FF都可以,这里API给我们设置的是FF,先不用管。

[01]:功能码。这是不同命令的区分依据。

(我们打开API的翻译doc,或者英文doc。找到 modbus_read_bits 函数。可以看到内部调用0x01命令。)

[00][00]:要读取数据的地址。

[00][01]:要读取数据的长度。


切换到server端窗口,这里modbus_receive 收到的就是client发过来的[00][02][00][00][00][06][FF][01][00][00][00][01]。

然后 modbus_reply 函数做出应答。

对于 read 操作,server会解析,并把指定地址的指定长度的数据发送至client。

我们看client第8行,这里接收到的是[00][02][00][00][00][04][FF][01][01]][01]

[00][02]:消息号。

[00][00]:强制,指明为modbus协议。

[00][04]:指明后面有 4 个字节([FF][01][01]][01]),是4个吧

[FF]:站号。

[01]:功能码。

[01]:读取到的数据的长度。

[01]:数据。对于单个线圈,01就是通,00就是断。为什么地址0000这里是01呢,因为这是刚才那个 modbus_write_bit 干的。

至此,modbus_read_bits 的发送与应答就完成了。


这里我们分析了单线圈的写与读。分别对应功能码0x05 与 0x01。

以下的各个API的分析方法类似,参考那篇入门详解与doc,大致应该就能学会了吧。

祝各位快速上手成功。

  • 7
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值