2

我目前正在从事一个项目,我正在尝试以 Raspberry Pi 4 作为主机并控制多个执行器作为从机来实现 Modbus。为此,我为我的 Pi 购买了一个特殊的盾牌。我运行了一个演示测试程序,确认 Pi 可以使用它的新防护罩,但之后碰壁了。

Shield 用户手册- 在用户手册文件夹内。

掌握:

## To install dependencies:
## sudo pip3 install modbus-tk
##################################################################################################
import serial
import fcntl
import os
import struct
import termios
import array
#import modbus lib
import modbus_tk
import modbus_tk.defines as cst
import modbus_tk.modbus as modbus
#import modbus_tk.modbus_rtu as modbus_rtu
from modbus_tk import modbus_rtu

# RS485 ioctls define
TIOCGRS485 = 0x542E
TIOCSRS485 = 0x542F
SER_RS485_ENABLED = 0b00000001
SER_RS485_RTS_ON_SEND = 0b00000010
SER_RS485_RTS_AFTER_SEND = 0b00000100
SER_RS485_RX_DURING_TX = 0b00010000
# rs 485 port
ser1 = serial.Serial("/dev/ttySC0",9600)    
ser2 = serial.Serial("/dev/ttySC1",9600)

def rs485_enable():
    buf = array.array('i', [0] * 8) # flags, delaytx, delayrx, padding
    #enable 485 chanel 1
    fcntl.ioctl(ser1, TIOCGRS485, buf)
    buf[0] |=  SER_RS485_ENABLED|SER_RS485_RTS_AFTER_SEND
    buf[1]  = 0
    buf[2]  = 0
    fcntl.ioctl(ser1, TIOCSRS485, buf)

    #enable 485 chanel 2
    fcntl.ioctl(ser2, TIOCGRS485, buf)
    buf[0] |=  SER_RS485_ENABLED|SER_RS485_RTS_AFTER_SEND
    buf[1]  = 0
    buf[2]  = 0
    fcntl.ioctl(ser2, TIOCSRS485, buf)
#end of rs485_enable():


if __name__ == '__main__':

    logger = modbus_tk.utils.create_logger("console")

    rs485_enable()

    #set modbus master
    master = modbus_rtu.RtuMaster(
           serial.Serial(port= '/dev/ttySC0',
           baudrate=9600,
           bytesize=8,
           parity='N',
           stopbits=1,
           xonxoff=0)
       )

    master.set_timeout(5.0)
    master.set_verbose(True)
    logger.info("connected")

    logger.info(master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 4))

    #send some queries
    #logger.info(master.execute(1, cst.READ_COILS, 0, 10))
    #logger.info(master.execute(1, cst.READ_DISCRETE_INPUTS, 0, 8))
    #logger.info(master.execute(1, cst.READ_INPUT_REGISTERS, 100, 3))
    #logger.info(master.execute(1, cst.READ_HOLDING_REGISTERS, 100, 12))
    #logger.info(master.execute(1, cst.WRITE_SINGLE_COIL, 7, output_value=1))
    #logger.info(master.execute(1, cst.WRITE_SINGLE_REGISTER, 100, output_value=54))
    #logger.info(master.execute(1, cst.WRITE_MULTIPLE_COILS, 0, output_value=[1, 1, 0, 1, 1, 0, 1, 1]))
    #logger.info(master.execute(1, cst.WRITE_MULTIPLE_REGISTERS, 100, output_value=xrange(12)))

#end of if __name__ == '__main__': 

奴隶:

import sys

import serial
import fcntl
import os
import struct
import termios
import array
import time

import modbus_tk
import modbus_tk.defines as cst
import modbus_tk.modbus as modbus
#import modbus_tk.modbus_rtu as modbus_rtu
from modbus_tk import modbus_rtu
# RS485 ioctls
TIOCGRS485 = 0x542E
TIOCSRS485 = 0x542F
SER_RS485_ENABLED = 0b00000001
SER_RS485_RTS_ON_SEND = 0b00000010
SER_RS485_RTS_AFTER_SEND = 0b00000100
SER_RS485_RX_DURING_TX = 0b00010000
# rs 485 port
ser1 = serial.Serial("/dev/ttySC0",9600)    
ser2 = serial.Serial("/dev/ttySC1",9600)

def rs485_enable():
    buf = array.array('i', [0] * 8) # flags, delaytx, delayrx, padding
    #enable 485 chanel 1
    fcntl.ioctl(ser1, TIOCGRS485, buf)
    buf[0] |=  SER_RS485_ENABLED|SER_RS485_RTS_AFTER_SEND
    buf[1]  = 0
    buf[2]  = 0
    fcntl.ioctl(ser1, TIOCSRS485, buf)

    #enable 485 chanel 2
    fcntl.ioctl(ser2, TIOCGRS485, buf)
    buf[0] |=  SER_RS485_ENABLED|SER_RS485_RTS_AFTER_SEND
    buf[1]  = 0
    buf[2]  = 0
    fcntl.ioctl(ser2, TIOCSRS485, buf)
#end of def rs485_enable():


if __name__ == '__main__':

    logger = modbus_tk.utils.create_logger("console")

    rs485_enable()

    logger = modbus_tk.utils.create_logger(name="console", record_format="%(message)s")

    #Create the server
    server = modbus_rtu.RtuServer(serial.Serial('/dev/ttySC1'))

    try:
        logger.info("running...")
        logger.info("enter 'quit' for closing the server")

        server.start()

        slave_1 = server.add_slave(1)
        slave_1.add_block('0', cst.HOLDING_REGISTERS, 0, 100)
        while True:
            cmd = sys.stdin.readline()
            args = cmd.split(' ')

            if cmd.find('quit') == 0:
                sys.stdout.write('bye-bye\r\n')
                break

            elif args[0] == 'add_slave':
                slave_id = int(args[1])
                server.add_slave(slave_id)
                sys.stdout.write('done: slave %d added\r\n' % (slave_id))

            elif args[0] == 'add_block':
                slave_id = int(args[1])
                name = args[2]
                block_type = int(args[3])
                starting_address = int(args[4])
                length = int(args[5])
                slave = server.get_slave(slave_id)
                slave.add_block(name, block_type, starting_address, length)
                sys.stdout.write('done: block %s added\r\n' % (name))

            elif args[0] == 'set_values':
                slave_id = int(args[1])
                name = args[2]
                address = int(args[3])
                values = []
                for val in args[4:]:
                    values.append(int(val))
                slave = server.get_slave(slave_id)
                slave.set_values(name, address, values)
                values = slave.get_values(name, address, len(values))
                sys.stdout.write('done: values written: %s\r\n' % (str(values)))

            elif args[0] == 'get_values':
                slave_id = int(args[1])
                name = args[2]
                address = int(args[3])
                length = int(args[4])
                slave = server.get_slave(slave_id)
                values = slave.get_values(name, address, length)
                sys.stdout.write('done: values read: %s\r\n' % (str(values)))

            else:
                sys.stdout.write("unknown command %s\r\n" % (args[0]))
    finally:
        server.stop()

我计划使用的执行器是Linak LA36。我相信这些是我将使用的功能:

在此处输入图像描述

文档的第 21-22 页开始。

这堵墙只是简单地开始使用 Modbus。我已经研究了执行器的技术文档以确定要发送什么,但是我在编写程序方面迷失了方向。我曾希望也许能够修改演示程序以满足我的需要,但无法理解那里使用的代码。

在互联网上搜索,我试图找到一个教程或描述不同变量和函数的作用,以便更好地理解,但找不到类似的东西。我确实找到了演示代码的来源,但无法找到/理解任何可以帮助我的东西。

我已经看到有些程序应该使用 Raspberry Pi 启用 Modbus(如PyModbus),但我不确定我的情况是否不同,是否有特殊的屏蔽,以及这些程序是否适用于我的设置?

所以,最后,我在这里希望得到一些帮助。建议,说明,示例,在这一点上,欢迎任何事情来让我走得更远。也可能是使用演示代码作为基础是一个错误,有人可以为我指出不同的方向吗?

我很愿意尝试不同的事情,任何帮助表示赞赏。

先感谢您。

更新:

从那以后,我一直在寻找其他选项,并偶然发现了我正在尝试使用的minimummodbus 。RS485 屏蔽仍处于演示配置中...

RS485屏蔽

...我一直在尝试在 Python 解释器中执行从 minimummodbus 中找到的一些代码:

>>> import minimalmodbus
>>> instr = minimalmodbus.Instrument('/dev/ttySC0', 1)
>>> instr
minimalmodbus.Instrument<id=0xb7437b2c, address=1, close_port_after_each_call=False, debug=False, serial=Serial<id=0xb7437b6c, open=True>(port='/dev/ttySC0', baudrate=19200, bytesize=8, parity='N', stopbits=1, timeout=0.05, xonxoff=False, rtscts=False, dsrdtr=False)>
>>> instr.read_register(24, 1)
5.0
>>> instr.write_register(24, 450, 1)
>>> instr.read_register(24, 1)

我已将'/dev/ttyUSB0'(在原始代码中)更改为'/dev/ttySC0'。现在我坚持:

>>> instr
    minimalmodbus.Instrument<id=0xb7437b2c, address=1, close_port_after_each_call=False, debug=False, serial=Serial<id=0xb7437b6c, open=True>(port='/dev/ttySC0', baudrate=19200, bytesize=8, parity='N', stopbits=1, timeout=0.05, xonxoff=False, rtscts=False, dsrdtr=False)>

这给出了SyntaxError: invalid syntax highlighting minimummodbus

4

1 回答 1

3

我认为你需要从简单的事情开始,并在它们之上构建。在您的评论中,您声明您想使用minimummodbus,这很好,但让我们从演示代码开始,并尝试首先使用您的执行器。稍后您可以回到其他库,例如minimummodbuspymodbus

在我们进入代码之前,我认为您应该了解 Modbus 是什么。本质上 Modbus 使用串行端口(它可以通过 RS485 或更传统的 RS232 或 TTL 电平,但这只是物理层,用于传送信息的电平;您的帽子和您的帽子上已经有了 RS485 端口执行器也可以通过 RS485 工作,因此在这方面无需担心,只要您正确连接了总线(A 到 A,B 到 B)。

那么,除了串口,还有什么需要 Modbus 的呢?Modbus 在主从配置中工作。这意味着只有一个主设备(在您的情况下是您的 Raspberry Pi 计算机)和一个或多个从设备(您的执行器)。根据我们在上一段中所说的,您的 Modbus 在两线(具有 RS485 电平)总线上运行。在这种配置中,与具有三根线的更通用的 RS232 标准相反:RX、TX 和 GND,您不能进行全双工通信(只有主机或所有从机之一可以与总线通信,而所有其他设备收听,类似于对讲机无线电链接)。为了进一步扩展类比,就像您需要 WT 无线电上的 PTT(一键通)按钮一样,您需要 Modbus 上的主设备或任何从设备的信号到PTT当他们想说话的时候。一些RS485收发器具有硬件实现的此功能;在您的帽子上,没有详细检查电路并查看演示代码,似乎总线上的方向控制是在具有该rs485_enable()功能的软件中实现的。. 编辑:详细查看硬件我必须纠正自己:您的 RS485 帽子实际上是通过硬件进行方向控制,其 SPI 到双 UART SC16IS752。该芯片设计为向后兼容使用流控 RTS 信号作为方向控制的旧 UARTS(我们之前提到的 PTT 功能)。这就是为什么你需要rs485_enable().

足够的理论,现在是实际的部分。您似乎错过了一件重要的事情。在您链接的手册上,在第 21 页上,您有以下段落:

在集成到 MODBUS 系统之前,必须检查并最终更改执行器的一些参数。该准备工作是通过使用BusLink PC 工具完成的(该工具将在后面详细介绍),并保证执行器能够执行基本功能。可能需要进一步的微调来满足系统或应用程序的要求。

然后,如果您返回第 12 页的表格,您会看到他们所谓的寻址(通常称为从站 ID)默认为 24​​7(未分配)。因此,您需要做的第一件事是使用此 Buslink PC 工具将执行器上的地址设置为 1 到 246 之间的任意数字(如果您计划在总线上连接多个执行器,则必须设置不同的数字对于每个执行器)。有关详细信息,请参见第 28 页。

成功完成该配置后,您应该能够运行演示主代码。例如,如果您想将执行器移动 10 毫米,您可以尝试:

## To install dependencies:
## sudo pip3 install modbus-tk
##################################################################################################
import serial
import fcntl
import os
import struct
import termios
import array
#import modbus lib
import modbus_tk
import modbus_tk.defines as cst
import modbus_tk.modbus as modbus
#import modbus_tk.modbus_rtu as modbus_rtu
from modbus_tk import modbus_rtu

# RS485 ioctls define
TIOCGRS485 = 0x542E
TIOCSRS485 = 0x542F
SER_RS485_ENABLED = 0b00000001
SER_RS485_RTS_ON_SEND = 0b00000010
SER_RS485_RTS_AFTER_SEND = 0b00000100
SER_RS485_RX_DURING_TX = 0b00010000
# rs 485 port
ser1 = serial.Serial("/dev/ttySC0",19200)    
ser2 = serial.Serial("/dev/ttySC1",9600)

def rs485_enable():
    buf = array.array('i', [0] * 8) # flags, delaytx, delayrx, padding
    #enable 485 chanel 1
    fcntl.ioctl(ser1, TIOCGRS485, buf)
    buf[0] |=  SER_RS485_ENABLED|SER_RS485_RTS_AFTER_SEND
    buf[1]  = 0
    buf[2]  = 0
    fcntl.ioctl(ser1, TIOCSRS485, buf)

    #enable 485 chanel 2
    fcntl.ioctl(ser2, TIOCGRS485, buf)
    buf[0] |=  SER_RS485_ENABLED|SER_RS485_RTS_AFTER_SEND
    buf[1]  = 0
    buf[2]  = 0
    fcntl.ioctl(ser2, TIOCSRS485, buf)
#end of rs485_enable():


if __name__ == '__main__':

    logger = modbus_tk.utils.create_logger("console")

    rs485_enable()

    #set modbus master
    master = modbus_rtu.RtuMaster(
           serial.Serial(port= '/dev/ttySC0',
           baudrate=9600,
           bytesize=8,
           parity='N',
           stopbits=1,
           xonxoff=0)
       )

    master.set_timeout(5.0)
    master.set_verbose(True)
    logger.info("connected")

    logger.info(master.execute(1, cst.WRITE_SINGLE_REGISTER, 1, output_value=100))  #Write target position 10mm (1/10mm*100)
    logger.info(master.execute(1, cst.WRITE_SINGLE_REGISTER, 2, output_value=1))  #Move actuator

请注意,我只更改了演示代码的最后两行。第一个 1cst.WRITE_SINGLE_REGISTER必须是您使用 BusLink PC 工具设置的相同地址从站。紧随其后的数字(第一行中的 1,第二行中的 2)是您需要根据手册第 22 页编写的寄存器编号。最后,output_value是您需要在每个寄存器上写入的值。在寄存器号 1 上,您需要写下您想要将执行器从其参考移动的目标位置(以 0.1 毫米的倍数测量),并且在第二个位置上只写一个 1(再次参见手册第 22 页上的表格,步骤 2 和3)。

您可以通过读取输入寄存器 3 和 5 来完成步骤 4 的序列。请注意,要读取输入寄存器,功能代码是cst.READ_INPUT_REGISTERS

试一试,看看你能不能让它发挥作用。完成后,我们可以看一下minimummodbus

编辑:更好地了解您的硬件是如何工作的(参见上面的编辑),现在很明显您可以使用任何您喜欢的 Modbus 库,您只需将演示代码保留在上面并在开始发送数据之前#end of rs485_enable():调用某个地方。rs485_enable()

对于最小的modbus,你可以尝试这样的事情:

import serial
import fcntl
import os
import struct
import termios
import array
#Remove modbus-tk imports and add minimalmodbus
import minimalmodbus


# only /dev/ttySC0 will be used
# RS485 ioctls define
TIOCGRS485 = 0x542E
TIOCSRS485 = 0x542F
SER_RS485_ENABLED = 0b00000001
SER_RS485_RTS_ON_SEND = 0b00000010
SER_RS485_RTS_AFTER_SEND = 0b00000100
SER_RS485_RX_DURING_TX = 0b00010000
# rs 485 port
ser1 = serial.Serial("/dev/ttySC0",19200)    


def rs485_enable():
    buf = array.array('i', [0] * 8) # flags, delaytx, delayrx, padding
    #enable 485 chanel 1
    fcntl.ioctl(ser1, TIOCGRS485, buf)
    buf[0] |=  SER_RS485_ENABLED|SER_RS485_RTS_AFTER_SEND
    buf[1]  = 0
    buf[2]  = 0
    fcntl.ioctl(ser1, TIOCSRS485, buf)

#end of rs485_enable():


if __name__ == '__main__':

    actuator = minimalmodbus.Instrument('/dev/ttySC0', 1) # port name, slave address (in decimal), change according to actuator address

    rs485_enable()   #you need to keep this for your hat to work

    #minimalmodbus setup
    actuator.serial.port               # this is the serial port name
    actuator.serial.baudrate = 19200   # Baud rate
    actuator.serial.bytesize = 8
    actuator.serial.parity   = serial.PARITY_NONE
    actuator.serial.stopbits = 1
    actuator.serial.timeout  = 0.05   # seconds

    actuator.address     # this is the slave (actuator) address number
    actuator.mode = minimalmodbus.MODE_RTU   # rtu mode

    #write registers
    actuator.write_register(1, 100)  #write target distance to move
    actuator.write_register(2, 1)    #Move!
于 2019-12-06T15:48:46.390 回答