Ошибка Modbus: [Вход / Выход] Нет ответа от удаленного устройства
Я пытаюсь подключиться с моего ноутбука Mac к устройству Modbus ( MR-SI4), используя последовательное соединение с помощью преобразователя USB RS485, который "подключается" к /dev/cu.SLAB_USBtoUART
,
Это мой код:
import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)
from pymodbus.constants import Endian
from pymodbus.constants import Defaults
from pymodbus.payload import BinaryPayloadDecoder
from pymodbus.client.sync import ModbusSerialClient as ModbusClient
from pymodbus.transaction import ModbusRtuFramer
# settings for USB-RS485 adapter
SERIAL = '/dev/cu.SLAB_USBtoUART'
BAUD = 19200
# set Modbus defaults
Defaults.UnitId = 1
Defaults.Retries = 5
client = ModbusClient(method='rtu', port=SERIAL, stopbits=1, bytesize=8, timeout=3, baudrate=BAUD, parity='E')
connection = client.connect()
print "Readout started"
#result = client.read_discrete_inputs(0)
#result = client.read_holding_registers(12,19)
result = client.read_input_registers(0,1)
print(result)
Вывод в консоли:
$ sudo python test.py
Readout started
DEBUG:pymodbus.transaction:Running transaction 1
DEBUG:pymodbus.transaction:send: 0x1 0x4 0x0 0x0 0x0 0x1 0x31 0xca
DEBUG:pymodbus.client.sync:will sleep to wait for 3.5 char
DEBUG:pymodbus.transaction:recv:
DEBUG:pymodbus.transaction:getting transaction 1
Modbus Error: [Input/Output] No Response received from the remote unit
Я хотел бы немного помочь понять ошибку, которую я получаю. Я пробовал с настройкой pymodbus
а также с различными функциями, такими как read_discrete_inputs
или же read_holding_registers
, Любая помощь?
Спасибо
4 ответа
При работе с Modbus, даже если вы используете высокоуровневую библиотеку, всегда полезно иметь под рукой официальную документацию по Modbus. С помощью документации вы можете проверить, что означает каждый байт вашего кадра:
0x01 0x04 0x00 0x00 0x00 0x01 0x31 0xCA
Первый байт - это адрес устройства (0x01)
Второй байт - это код функции (0x04, Read Input Registers)
Третий и четвертый байты являются стартовой позицией (0x00, 0x00)
Пятый и шестой байты - это количество выходов (0x00, 0x01)
Последние 2 байта являются контрольной CRC (0x31, 0xCA)
Это означает, что вы просите (0x00, 0x01)
регистры из входных регистров (0x04)
с первой позиции в памяти (0x00, 0x00)
с устройства с адресом 1 (0x01)
,
Фрейм сам по себе правильный, и если программное обеспечение / прошивка устройства соответствует стандарту Modbus, у вас должен быть ответ: регистр, который вы запросили, ИЛИ кадр ошибки (0x01, 0x80, crc, crc)
,
Тем не менее, мы можем проверить, почему вы не получаете ответ от вашего устройства. Для этого, если вы не уверены в своем коде / в том, что вы спрашиваете / как ведет себя ваше устройство, вы можете использовать внешний инструмент для сравнения результатов. Я бы посоветовал вам что-то вроде Docklight, которое поможет вам установить соединение и отправить / получить кадр Modbus.
Первым делом я проверю параметры подключения:
client = ModbusClient(method='rtu', port=SERIAL, stopbits=1, bytesize=8, timeout=3, baudrate=BAUD, parity='E')
Метод правильный, так как это протокол, который вы запрашиваете.
Порт правильный, в противном случае возвращается системная ошибка.
Тайм-аут - это то, что вызывает ошибку: в течение определенного времени ответ не был получен. В любом случае, проблема, вероятно, не в этом, поскольку вы установили высокое значение для тайм-аута.
Стопбиты не должны мешать приему кадра.
Проблема может быть в скорости передачи и четности: ошибка здесь может вызвать ошибку, вызванную вашим кодом.
Если вы не знаете правильное значение скорости передачи и / или четности, вы можете попробовать использовать наибольшую скорость передачи данных и значения четности: 'N', 'E', 'O', 'M', 'S'
(означает: Нет, Четный, Нечетный, Марк, Пробел. По умолчанию Нет).
Если бы я сделал ставку, я бы начал с замены Четного паритета на Нет (parity = 'N'
).
Если у вас все еще есть проблема, адрес устройства (0x01
может быть ошибочным. Адрес может быть представлен значением от 0 (0x00
) до 255 (0xFF
).
По стандарту протокола, даже если начальный адрес (0x00, 0x00
) количество выходов (0x00, 0x01)
или crc (0x31, 0xCA
) не правы, устройство должно что-то отвечать, но это не всегда так: если вы думаете, что находитесь в такой ситуации, ищите документацию для конкретного устройства.
Последняя возможность - использовать низкоуровневую библиотеку, такую как PySerial, и вы определяете свой собственный протокол.
Я смог обойти эту проблему, установив постоянную RetryOnEmpty
в True
,
from pymodbus.constants import Defaults
Defaults.RetryOnEmpty = True
Также может быть полезно настроить время ожидания и некоторые повторные попытки.
Defaults.Timeout = 5
Defaults.Retries = 5
https://pymodbus.readthedocs.io/en/v1.3.2/library/constants.html
У меня также была аналогичная ошибка, когда не было ответа. Адрес моего устройства был неправильным. Это была 1, т.е. единица = 1. Предполагалось, что она равна нулю. то естьclient.read_holding_registers(500,1,unit=0)
import pymodbus
from pymodbus.client.sync import ModbusSerialClient
client = ModbusSerialClient( method = 'rtu', port = "COM4", stopbits = 1,
bytesize = 8, baudrate=9600 , parity= 'N')
client.connect()
result = client.read_holding_registers(500,1,unit=0)
print(result)
0x0 0x3 0x1 0xf4 0x0 0x1 0xc5 0xd5
В противном случае вывод ошибки был
0x1 0x3 0x1 0xf4 0x0 0x1 0xc5 0xd5
Проверьте это Stack_post.
Вы забыли unit
аргумент в .read_input_registers(0, 1, unit=<unit-ID>)
Вы должны иметь как этот фрагмент кода:
result = client.read_input_registers(0, 1, unit=<unit-ID>)
if not result.isError():
'''isError() method implemented in pymodbus 1.4.0 and above'''
print(result.registers) # Note.
else:
# Handle Error.
print('Unable to read or there is the connection problem.')
Вы также можете использовать из .read_holding_registers()
:
result = client.read_holding_registers(0, 1, unit=<unit-ID>)
[ПРИМЕЧАНИЕ]:
- Во многих случаях
unit
является1
в ведомой стороне по умолчанию. - Во многих случаях четность RTU отсутствует:
parity='N'
- Убедитесь в праве root на вашем последовательном порту (
/dev/cu.SLAB_USBtoUART
).