Pymodbus - Read input register of Energy meter ove

2019-06-10 19:29发布

I have one energy meter and i am trying to retrieve voltage, freq values from meter on raspberry pi uart over RS485

My connections for raspberry pi and rs485 are as follows Rs485 DI - Tx of raspberry pi Rs485 R0 - Rx of raspberry pi Rs485 DE/RE -Pin 7 of raspberry pi

my code is as follows:

import serial import RPi.GPIO as GPIO

from pymodbus.client.sync import ModbusSerialClient as ModbusClient
from pymodbus.register_read_message import ReadInputRegistersResponse

from pymodbus.register_read_message import ReadInputRegistersRequest

import logging

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

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

client= ModbusClient(method = 'rtu', port='/dev/ttyS0',stopbits = 1,timeout =0.3, bytesize = 8, parity = 'N', baudrate = '9600')

connection = client.connect()

print "Connection" print connection

while 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()

And i am receiving the output as follows

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条回答
聊天终结者
2楼-- · 2019-06-10 19:57

If I'm not getting it wrong, you're defining your GPIO pin correctly but you're never toggling it high and low. To be able to drive the DE/~RE signal on your RS485 chip you should take the GPIO high before you write on the bus and low right after to be able to read the answer from your meter.

Unfortunately, I'm afraid what you're trying to do is not possible with pyModbus out of the box. You can take a look at this link:

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

You might be able to tweak pyModbus and use the RTS alternative functions on your Pi (see here: https://github.com/mholling/rpirtscts), but I don't think this path will get you very far reliability wise.

As I wrote here: RS485: Inappropriate ioctl for device, you might be better off going for a hardware solution. If you can't get new hardware you could always try the 555 timer solution, at least as a temporary fix.

Good luck, and be sure to post your progress or any further ideas.

EDIT: Solution using libmodbus instead

The suggestion to use libmodbus was very successful. Follow these steps if you want to try it (tested with Raspberry Pi 3B):

1) Clone libmodbus fork with GPIO support to your Pi:

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

2) Configure, compile and install libmodbus library (same commands as for the main repo):

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

3) Go to rpi-test folder and compile the example:

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

4) Run test, you will need to change permissions or sudo it: sudo ./test

What you get is actually much better than I expected and probably good enough for most Modbus hardware:

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

In blue, you see the TX from the Pi's UART (pin number 8 on the connector) and in yellow you get your DE/~RE (pin number 11, GPIO17) that you should connect to your RS485 chip. As you can see there is a delay of 0.6 ms from the end of the Modbus data frame until the bus is free for the slave to answer. At the speed I was using (9600 bps) the minimum delay you need to comply with the Modbus specification is around 3 ms (3.5 chars), so it should be fine for most situations.

The only thing pending would be to add all these GPIO functions to the pylibmodbus wrapper but that should be quite easy. I'm planning to use this library from Python real soon in the field with my Pocket Chip computer to work as a Modbus handheld tester so if you or anybody else manage to find the time I'd be more than glad to test it.

As soon as I have more time I'll try libmodbus together with my FTDI serial port and take a couple of scope captures to compare hardware vs. software signaling.

I forgot to mention that the only changes I made to the test.c were:

Line 13: #define UART_PORT "/dev/serial0"

Line 14: #define BAUD_RATE 9600

The first one just to the name of the embedded serial port on my Pi and the second one is the speed I always use for testing purposes.

EDIT: Software vs. Hardware signalling

As promised I have tested the solution proposed with libmodbus but instead of the embedded UART on the Raspberry Pi I made it work together with my FTDI USB adaptor to compare the time it takes to release the bus.

*libmodbus* delay 500-600 microseconds*

FTDI TXEN hardware signal, delay 250 microseconds

You can see how the TXEN (magenta trace) manages to go low about 250 microseconds after the stop bit while the GPIO on the Pi (in blue) takes more or less the same time as in the capture above (500-600 microseconds).

So pending more extensive testing, my conclusion is libmodbus does a great job for UARTs where you don't have a TX enable signal available. I think it should be possible to have reliable Modbus communication for most scenarios.

查看更多
ら.Afraid
3楼-- · 2019-06-10 20:16

Solution with pylibmodbus

I wrote the missing functions for the pylibmodbus library.

See here: https://github.com/marcosgatcomputer/pylibmodbus

Once you have everything installed (the libmodbus branch with support for GPIO and pylibmodbus from the link above) you can try the test file:

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()

This code works with Rpi 3B. For Pocket Chip I had to modify libmodbus to account for the GPIO pin numbers (the original code was not able to write on the /sys/class/gpio/export file to create the gpio1015 device). This issue might happen for hardware with 4 digit numbers (if you see folders like gpiochipxxxx on your /sys/class/gpio/)

查看更多
登录 后发表回答