modbus
Modbus是Modicon(施耐德)公司于1979年开发的串行通信协议。它最初设计用于公司的可编程逻辑控制器(PLC)。 Modbus是一种开放式协议,支持使用RS232/RS485/RS422协议的串行设备,同时还支持调制解调器。 它的简单性以及制造商可以免费将其纳入其产品的事实使其成为连接工业电子设备的最流行的方法。
Modbus解决了通过串行线路在电子设备之间发送信息的问题。该协议在遵循该协议的体系结构中实现主/从模型 Modbus主站(Master)负责从其他设备(Slave)请求信息。
ps:简单比喻为Master就是客户端,Slave就是服务器,客户端获取服务器状态信息以及控制。
相关网站
modbus协议分为4种
- Modbus ASCII
- Modbus RTU
- Modbus Plus
可忽略,遇到再说
- Modbus TCP
运行在tcp/ip协议之上
Modbus ASCII
当设备设置为使用ASCII(美国信息交换标准代码)模式在MODBUS串行线上进行通信时,消息中的每个8位字节将作为两个ASCII 4位字符发送。当物理通信链路或设备的功能不允许符合RTU计时器管理要求时,使用此模式。 所以此模式的效率不如RTU,因为每个字节需要两个字符。示例:字节0x7D编码为两个字符:0x35和0x42(在ASCII表中为0x37 =‘7’,而0x44 =‘D’)。
Modbus RTU
Modbus RTU是一种紧凑的,采用二进制表示数据的方式;因为使用二进制编码和CRC错误检查的结合使得Modbus RTU适用于工业应用,因为它比ASCII字符的替代方案更有效地传输。 在Modbus RTU与ASCII之间进行选择时,如果考虑性能,则RTU是首选。
Modbus Plus
Modbus有一个扩展版本Modbus Plus(Modbus+或者MB+),不过此协议是Modicon专有的,和Modbus不同。 它需要一个专门的协处理器来处理类似HDLC的高速令牌旋转。它使用1Mbit/s的双绞线,并且每个节点都有转换隔离设备,是一种采用转换/边缘触发而不是电压/水平触发的设备。连接Modbus Plus到计算机需要特别的接口,通常是支持ISA(SA85),PCI或者PCMCIA总线的板卡。
Modbus TCP
Modbus TCP 是在TCP/IP网络上运行的Modbus的实现,旨在允许Modbus ASCII / RTU协议在基于TCP / IP的网络上传输。Modbus / TCP将Modbus消息嵌入TCP / IP帧内。尽管实现起来非常简单,但是与网络相关的特性增加了一些挑战。例如,由于Modbus主机期望并要求在一定时间范围内对其轮询做出响应,因此必须考虑TCP / IP网络的不确定性(和其他方面)。 Modbus ASCII和Modbus TCP之间的主要区别在于,Modbus ASCII所需的LRC错误检查由IP层执行。
对于libmodbus可以使用vcpkg进行安装,自行源码编译也不是不行。我们可以使用ModbusTool进行主机以及从机测试,modbus的一些功能码,比如读取线圈,写入线程之类的。
关于libmodbus读取从设备寄存器数值的示例
#include <iostream>
#include <modbus-tcp.h>
int main() {
std::cout << "Hello World!" << std::endl;
// 创建modbus tcp
modbus_t* ctx = ::modbus_new_tcp("127.0.0.1", 1502);
if (!ctx) {
std::cout << "new tcp failed" << std::endl;
return 0;
}
// 设置连接等待时间,这里为无限等待
if (::modbus_set_indication_timeout(ctx, 0, 0) == -1)
std::cerr << "failed set timeout " << ::modbus_strerror(errno) << std::endl;
// 连接slave服务器
if (::modbus_connect(ctx) == -1) {
std::cerr << "connect modbus tcp failed" << std::endl;
::modbus_free(ctx);
return -1;
}
// 设置读取从站设备数据时等待的超时时间
if (::modbus_set_response_timeout(ctx, 20, 0) == -1)
std::cerr << "failed set timeout " << ::modbus_strerror(errno) << std::endl;
uint16_t datas[64] = {0};
// 设置要读取的slave id 默认 为 0xFF
::modbus_set_slave(ctx, 1);
// 读取slave寄存器
int regNum = ::modbus_read_registers(ctx, 0, 64, datas);
std::cout << "register " << regNum << std::endl;
if (regNum != -1) {
for (int i = 0; i < regNum; i++) {
std::cout << datas[i] << ' ';
}
std::cout << std::endl;
} else {
std::cerr << "register failed: " << ::modbus_strerror(errno) << std::endl;
}
// 关闭连接
::modbus_close(ctx);
// 释放上下文
::modbus_free(ctx);
std::cout << "quit finish" << std::endl;
}