1. 前言
- modbus_tk是使用python语言实现的modbus协议栈,该库函数及支持主机也支持从机,同时支持RTU串口通信和TCP范式通信。
2. 准备工作
- 查阅modbus相关资料
- 下载modbus-tcp tester,这里面有server-tester和client-tester,能加速测试程序过程
3. modbus_tk安装
- 环境:python2.7, win10 & 树莓派
- 操作:直接 pip install modbus_tk 或者采用 easy_install
4. 基础示例
- 从机程序,需要先运行从机程序,才能启动master
'''
作者:raphael
时间:2017/3/10
简介:modbus协议从机测试脚本
'''
import sys
import logging
import threading
import modbus_tk
import modbus_tk.defines as cst
import modbus_tk.modbus as modbus
import modbus_tk.modbus_tcp as modbus_tcp
LOGGER = modbus_tk.utils.create_logger(name="console", record_format="%(message)s")
if __name__ == "__main__":
try:
SERVER = modbus_tcp.TcpServer(address="192.168.1.111", port=1100)
LOGGER.info("running...")
LOGGER.info("enter 'quit' for closing the server")
SERVER.start()
SLAVE1 = SERVER.add_slave(1)
SLAVE1.add_block('A', cst.HOLDING_REGISTERS, 0, 4)
SLAVE1.add_block('B', cst.HOLDING_REGISTERS, 4, 14)
SLAVE2 = SERVER.add_slave(2)
SLAVE2.add_block('C', cst.COILS, 0, 10)
SLAVE2.add_block('D', cst.HOLDING_REGISTERS, 0, 10)
SLAVE1.set_values('A', 0, 4)
SLAVE1.set_values('B', 4, [1, 2, 3, 4, 5, 5, 12, 1232])
SLAVE2.set_values('C', 0, [1, 1, 1, 1, 1, 1])
SLAVE2.set_values('D', 0, 10)
while True:
CMD = sys.stdin.readline()
if CMD.find('quit') == 0:
sys.stdout.write('bye-bye\r\n')
break
else:
sys.stdout.write("unknown command %s\r\n" % (args[0]))
finally:
SERVER.stop()
#!/usr/bin/env python
# -*- coding: utf_8 -*-
'''
作者:raphael
时间:2017/3/10
简介:modbus协议主机测试脚本
'''
import sys
import logging
import modbus_tk
import modbus_tk.defines as cst
import modbus_tk.modbus_tcp as modbus_tcp
LOGGER = modbus_tk.utils.create_logger("console")
if __name__ == "__main__":
try:
#连接从机地址,这里要注意端口号和IP与从机一致
MASTER = modbus_tcp.TcpMaster(host="192.168.1.111", port=1100)
MASTER.set_timeout(5.0)
LOGGER.info("connected")
#读取从机1的0-4保持寄存器
LOGGER.info(MASTER.execute(1, cst.READ_HOLDING_REGISTERS, 0, 4))
#读取从机1的4-14保持寄存器,因为寄存器独立分块了,所以不能直接连通读取,强行结果是会出现数据越界
LOGGER.info(MASTER.execute(1, cst.READ_HOLDING_REGISTERS, 4, 14))
# 需要按照execute格式
LOGGER.info(MASTER.execute(1, cst.WRITE_MULTIPLE_REGISTERS, 0, output_value=[0, 1, 2]))
LOGGER.info(MASTER.execute(1, cst.READ_HOLDING_REGISTERS, 0, 4))
LOGGER.info(MASTER.execute(2, cst.READ_COILS, 0, 8))
LOGGER.info(MASTER.execute(2, cst.WRITE_MULTIPLE_COILS, 0, output_value=[1, 0, 0, 0, 1]))
LOGGER.info(MASTER.execute(2, cst.READ_COILS, 0, 8))
LOGGER.info(MASTER.execute(2, cst.READ_HOLDING_REGISTERS, 0, 4))
# 线圈和寄存器地址不是同一区块的
except modbus_tk.modbus.ModbusError, err:
LOGGER.error("%s- Code=%d" % (err, err.get_exception_code()))
5.结果截图
6. 相关函数说明
- add_slave(self, slave_id, unsigned=True, memory=None)
def add_slave(self, slave_id, unsigned=True, memory=None):
"""Add a new slave with the given id"""
with self._lock:
if (slave_id <= 0) or (slave_id > 255):
raise Exception("Invalid slave id {0}".format(slave_id))
if slave_id not in self._slaves:
self._slaves[slave_id] = Slave(slave_id, unsigned, memory)
return self._slaves[slave_id]
else:
raise DuplicatedKeyError("Slave {0} already exists".format(slave_id))
- set_values(self, block_name, address, values)
- If values is a number, only one value is written,对于这个函数, values是我已开始没清楚的
def set_values(self, block_name, address, values):
"""
Set the values of the items at the given address
If values is a list or a tuple, the value of every item is written
If values is a number, only one value is written
"""
with self._data_lock:
block = self._get_block(block_name)
offset = address-block.starting_address
size = 1
if isinstance(values, list) or isinstance(values, tuple):
size = len(values)
if (offset < 0) or ((offset + size) > block.size):
raise OutOfModbusBlockError(
"address {0} size {1} is out of block {2}".format(address, size, block_name)
)
if isinstance(values, list) or isinstance(values, tuple):
block[offset:offset+len(values)] = values
else:
block[offset] = values
- execute(
self, slave, function_code, starting_address, quantity_of_x=0, output_value=0, data_format=”“, expected_length=-1)
参数 |
注释 |
备注 |
slave |
从机地址 |
modbus_tcp时可以忽略 |
function_code |
功能码 |
看定义 |
starting_address |
寄存器初始地址 |
要注意块界限 |
quantity_of_x |
寄存器或者线圈数量 |
|
output_value |
输出内容 |
注意写的类型 |
@threadsafe_function
def execute(
self, slave, function_code, starting_address, quantity_of_x=0, output_value=0, data_format="", expected_length=-1):
"""
Execute a modbus query and returns the data part of the answer as a tuple
The returned tuple depends on the query function code. see modbus protocol
specification for details
data_format makes possible to extract the data like defined in the
struct python module documentation
"""
7.modbus 功能代码: defines.py
- modbus 异常代码
- ILLEGAL_FUNCTION = 1 功能代码不合法
- ILLEGAL_DATA_ADDRESS = 2 数据地址不合法
- ILLEGAL_DATA_VALUE = 3 数据值不合法
- SLAVE_DEVICE_FAILURE = 4 slave设备失败
- COMMAND_ACKNOWLEDGE = 5 命令已收到
- SLAVE_DEVICE_BUSY = 6 slave设备忙
- MEMORY_PARITY_ERROR = 8 内存奇偶误差
- supported modbus 功能代码
- READ_COILS = 1 读线圈
- READ_DISCRETE_INPUTS = 2 读离散输入
- READ_HOLDING_REGISTERS = 3 【读乘法寄存器】
- READ_INPUT_REGISTERS = 4 读输入寄存器
- WRITE_SINGLE_COIL = 5 写单一线圈
- WRITE_SINGLE_REGISTER = 6 写单一寄存器
- WRITE_MULTIPLE_COILS = 15 写多个线圈 【强制多点线圈】
- WRITE_MULTIPLE_REGISTERS = 16 写多寄存器 【写乘法寄存器】
- supported block types 支持的块类型
- COILS = 1 线圈
- DISCRETE_INPUTS = 2 离散输入(数字量输入)
- HOLDING_REGISTERS = 3 乘法寄存器
- ANALOG_INPUTS = 4 模拟量输入
8.参考网址
- http://blog.csdn.net/gaoxuefeng/article/details/7382122
- http://blog.csdn.net/xukai871105/article/details/21884065
- http://blog.csdn.net/u010189918/article/details/9814319