Pymodbus-在Raspberry Pi3的uart上通过rs485读取电能表的输入寄存器

时间:2019-05-08 08:30:31

标签: raspberry-pi3 uart modbus pymodbus

我有一个电能表,我正在尝试通过RS485从树莓派的电表中获取电压,频率值

我对树莓派和rs485的连接如下 Rs485 DI-树莓派的TX Rs485 R0-树莓派Rx Rs485 DE / RE-树莓派的第7针

我的代码如下:

  导入序列      将RPi.GPIO导入为GPIO      

从pymodbus.client.sync导入ModbusSerialClient作为ModbusClient
    从pymodbus.register_read_message导入ReadInputRegistersResponse

     

从pymodbus.register_read_message导入ReadInputRegistersRequest

     

导入日志记录

     

logging.basicConfig()日志= logging.getLogger()   log.setLevel(logging.DEBUG)

     

GPIO.setmode(GPIO.BOARD)GPIO.setup(7,GPIO.OUT,initial = GPIO.LOW)

     

client = ModbusClient(方法='rtu',port ='/ dev / ttyS0',停止位= 1,超时= 0.3,字节大小= 8,奇偶校验='N',波特率='9600')

     

connection = client.connect()

     

打印“连接”打印连接

     

同时1:

volt=0     

freq=0   

if connection:  

    try:  

        voltage1= client.read_input_registers(0x000,4,unit=0x03)  
        print voltage1

    except:
        print "Error: No message Received"

client.close()

我正在收到如下输出

DEBUG:pymodbus.transaction:Current transaction state - TRANSACTION_COMPLETE
DEBUG:pymodbus.transaction:Running transaction 4
DEBUG:pymodbus.transaction:SEND: 0x3 0x4 0x0 0x0 0x0 0x4 0xf0 0x2b
DEBUG:pymodbus.framer.rtu_framer:Changing state to IDLE - Last Frame End - None, Current Time stamp - 1557304284.88
DEBUG:pymodbus.client.sync:New Transaction state 'SENDING'
DEBUG:pymodbus.transaction:Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
DEBUG:pymodbus.transaction:Changing transaction state from 'WAITING FOR REPLY' to 'PROCESSING REPLY'
DEBUG:pymodbus.transaction:RECV: 0x7b 0x20 0x31 0x20 0x31 0x20 0x32 0x36 0x2e 0x33 0x35 0x20 0x31
DEBUG:pymodbus.transaction:Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
Modbus Error: [Input/Output] No Response received from the remote unit/Unable to decode response
DEBUG:pymodbus.transaction:Current transaction state - TRANSACTION_COMPLETE
DEBUG:pymodbus.transaction:Running transaction 5
DEBUG:pymodbus.transaction:Clearing current Frame : - 0x7b 0x20 0x31 0x20 0x31 0x20 0x32 0x36 0x2e 0x33 0x35 0x20 0x31
DEBUG:pymodbus.framer.rtu_framer:Resetting frame - Current Frame in buffer - 0x7b 0x20 0x31 0x20 0x31 0x20 0x32 0x36 0x2e 0x33 0x35 0x20 0x31
DEBUG:pymodbus.transaction:SEND: 0x3 0x4 0x0 0x0 0x0 0x4 0xf0 0x2b
DEBUG:pymodbus.framer.rtu_framer:Changing state to IDLE - Last Frame End - None, Current Time stamp - 1557304284.98
DEBUG:pymodbus.client.sync:New Transaction state 'SENDING'
WARNING:pymodbus.client.sync:Cleanup recv buffer before send: 0x37 0x2e 0x35 0x35 0x20 0x33
DEBUG:pymodbus.transaction:Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
DEBUG:pymodbus.transaction:Incomplete message received, Expected 13 bytes Recieved 7 bytes !!!!
DEBUG:pymodbus.transaction:Changing transaction state from 'WAITING FOR REPLY' to 'PROCESSING REPLY'
DEBUG:pymodbus.transaction:RECV: 0x2e 0x30 0x36 0x20 0x7d 0xd 0xa
DEBUG:pymodbus.transaction:Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
Modbus Error: [Input/Output] No Response received from the remote unit/Unable to decode response

2 个答案:

答案 0 :(得分:0)

如果我没弄错的话,则说明您正确定义了GPIO引脚,但从未将其高低切换。为了能够驱动RS485芯片上的DE /〜RE信号,您应该在写入总线之前将GPIO设为高电平,然后在读取仪表的答案之后将GPIO设为低电平。

不幸的是,恐怕开箱即用pyModbus无法实现您要执行的操作。您可以查看以下链接:

https://github.com/riptideio/pymodbus/issues/33

您也许可以调整pyModbus并在Pi上使用RTS替代功能(请参见此处:https://github.com/mholling/rpirtscts),但我认为这条路线不会给您带来很大的可靠性。

正如我在这里写的:RS485: Inappropriate ioctl for device,您最好选择硬件解决方案。如果您无法获得新的硬件,则可以随时尝试使用555计时器解决方案,至少作为一种临时解决方案。

祝您好运,并确保发布您的进度或任何其他想法。

编辑使用 libmodbus 的解决方案

使用 libmodbus 的建议非常成功。如果要尝试(通过Raspberry Pi 3B测试),请按照以下步骤操作:

1)将具有GPIO支持的libmodbus分支克隆到您的Pi:

git clone https://github.com/dhruvvyas90/libmodbus

2)配置,编译和安装libmodbus库(与主库相同的命令):

./autogen.sh && ./configure --prefix=/usr && make && sudo make install

3)转到rpi-test文件夹并编译示例:

gcc -o test -I/usr/include/modbus test.c -lmodbus

4)运行测试,您将需要更改权限或对其进行伪装:sudo ./test

您得到的实际上比我预期的要好得多,并且可能对于大多数Modbus硬件来说已经足够好了:

Scope capture, blue is TX line (pin number 8 on Pi), yellow is GPIO17 (pin number 11)

蓝色,表示从Pi的UART发送的TX(连接器上的引脚号8),黄色,表示应连接到RS485芯片的DE /〜RE(引脚号11,GPIO17)。如您所见,从Modbus数据帧的末尾到总线空闲,从站可以应答之前有0.6 ms的延迟。以我使用的速度(9600 bps),您需要符合Modbus规范的最小延迟约为3毫秒(3.5个字符),因此对于大多数情况来说应该没问题。

唯一要做的就是将所有这些GPIO函数添加到pylibmodbus包装器中,但这应该很容易。我计划不久后在我的Pocket Chip计算机上使用来自Python real的该库,将其用作Modbus手持式测试仪,因此,如果您或其他人设法找到时间,我将非常高兴对其进行测试。 / p>

一旦我有更多的时间,我将尝试将libmodbus与FTDI串行端口一起使用,并进行几次示波器捕获以比较硬件和软件信号。

我忘了提到我对test.c所做的唯一更改是:

第13行:#define UART_PORT "/dev/serial0"

第14行:#define BAUD_RATE 9600

第一个只是我Pi上嵌入式串行端口的名称,第二个就是我一直用于测试目的的速度。

编辑软件与硬件信令

正如我所承诺的,我已经测试了使用 libmodbus 提出的解决方案,但我不是将Raspberry Pi上的嵌入式UART与我的FTDI USB适配器一起使用,以比较释放总线所需的时间

*libmodbus* delay 500-600 microseconds*

FTDI TXEN hardware signal, delay 250 microseconds

您可以看到TXEN(洋红色迹线)如何在停止位后约250微秒处变低,而Pi上的GPIO(蓝色)花费的时间与上述捕获时间大致相同(500-600)微秒)。

因此,在进行更广泛的测试之前,我的结论是 libmodbus 对于没有可用的TX使能信号的UART发挥了出色的作用。我认为在大多数情况下应该有可靠的Modbus通信。

答案 1 :(得分:0)

使用 pylibmodbus

的解决方案

我为 pylibmodbus 库编写了缺少的功能。

参见此处:https://github.com/marcosgatcomputer/pylibmodbus

一旦安装完所有组件( libmodbus 分支,它从上面的链接支持GPIO和 pylibmodbus ),您可以尝试测试文件:

from pylibmodbus import ModbusRtu


#Define Modbus RTU client (Python 2.x)
client=ModbusRtu(device="/dev/serial0", baud=19200, parity="N", data_bit=8, stop_bit=1)
# For Python 3.x you have to explicitly indicate ASCII enconding
#client=ModbusRtu(device="/dev/serial0".encode("ascii"), baud=19200, parity="N".encode("ascii"), data_bit=8, stop_bit=1)

#Read and set timeout
timeout_sec = client.get_response_timeout()
client.set_response_timeout(timeout_sec+1)

#Connect
client.connect()

SERVER_ID=0
BCM_PIN_DE=17
BCM_PIN_RE=9

#Set Slave ID number
client.set_slave(SERVER_ID)

#Enable RPi GPIO Functions
client.enable_rpi(1)

#Define pin numbers to be used as Read Enable (RE) and Drive Enable (DE)
client.configure_rpi_bcm_pins(BCM_PIN_DE,BCM_PIN_RE)

#Export pin direction (set as outputs)
client.rpi_pin_export_direction()

#Write Modbus registers, 10 starting from 0
client.write_registers(0, [0]*10)

#Read 10 input registers starting from number 0
result=(client.read_registers(0, 10))

#Show register values
print result

#Release pins and close connection
client.rpi_pin_unexport_direction()
client.close()

此代码适用于Rpi 3B。对于Pocket Chip,我必须修改 libmodbus 来说明GPIO引脚号(原始代码无法在/ sys / class / gpio / export文件中写入以创建gpio1015设备)。具有4位数字的硬件(如果您在/ sys / class / gpio /上看到类似gpiochipxxxx的文件夹)可能会发生此问题