Modbus RTU получает исключение 2 (недопустимый адрес данных), когда не запрашивает полный блок - PullRequest
0 голосов
/ 06 февраля 2020

У меня есть датчик, с которым осуществляется связь через Modbus RTU. Датчик хранит свой серийный номер, закодированный как 16-разрядные целочисленные значения, в 8 регистрах:

Серийный номер в виде ASCII-кода находится по адресу регистра чтения 30001-30008 (16 бит на адрес) .

(Источник: EE872 Руководство пользователя , раздел 5.2.1)

Когда я использую код функции 0x04 (чтение входных регистров), чтобы прочитать все 8 регистров одновременно, запрос завершается успешно .

Когда я использую код функции 0x04 для чтения регистров, либо по отдельности, либо как частичная группа (например, только первые 7 из 8 ), датчик возвращает код исключения 2 ( недопустимый адрес данных ).

Вопрос

Почему возникло это исключение, если Я не читаю весь блок?

Есть ли плохая реализация Modbus RTU в этом датчике? Или это стандартное поведение для устройств Modbus?


Что я нашел

Ошибка Modbus "Недопустимый адрес данных"

Вопрос в этой статье, похоже, намекает на похожую проблему, но ответ был неопределенным.


Пример полного кода

Ввод

#!/usr/bin/env python3


import sys
import logging
from collections import namedtuple
from typing import List

from pymodbus.pdu import ModbusResponse
from pymodbus.client.sync import ModbusSerialClient
from pymodbus.register_read_message import ReadInputRegistersResponse
from pymodbus.pdu import ExceptionResponse


log = logging.getLogger()
log.addHandler(logging.StreamHandler(sys.stdout))
log.setLevel(logging.DEBUG)


ModbusInputReg = namedtuple("ModbusInputRegister", ["name", "address", "value"])

serial_num = ModbusInputReg("serial number", 0x0, None)

PORT = "COM6"
BAUD_RATE = 9600
SLAVE_ADDR = 1


def construct_serial_num(serial_num_arr: List[int]) -> str:
    str_ = ""
    for num in serial_num_arr:
        if num == 0:
            continue
        str_ += bytes.fromhex(f"{num:x}").decode("utf-8")
    return str_


modbus_client = ModbusSerialClient(
    method="rtu",
    port=PORT,
    baudrate=BAUD_RATE,
    strict=False,  # I found CO2 sensor fails if True
)
modbus_client.connect()

COUNT_1 = 7
encoded_sn_1 = modbus_client.read_input_registers(
    address=serial_num.address, count=COUNT_1, unit=SLAVE_ADDR
)  # type: ExceptionResponse
print(f"\nencoded_sn_1 = {encoded_sn_1}.")
print(f"exception code = {encoded_sn_1.exception_code}.\n")

COUNT_2 = 8
encoded_sn_2 = modbus_client.read_input_registers(
    address=serial_num.address, count=COUNT_2, unit=SLAVE_ADDR
)  # type: ReadInputRegistersResponse
print(f"\nencoded_sn_2 = {encoded_sn_2}.")
serial_num = construct_serial_num(encoded_sn_2.registers)
print(f"decoded serial number = {serial_num}.")

Выход

Current transaction state - IDLE
Running transaction 1
SEND: 0x1 0x4 0x0 0x0 0x0 0x7 0xb1 0xc8
New Transaction state 'SENDING'
Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
Changing transaction state from 'WAITING FOR REPLY' to 'PROCESSING REPLY'
RECV: 0x1 0x84 0x2 0xc2 0xc1
Getting Frame - 0x84 0x2
Factory Response[132]
Frame advanced, resetting header!!
Adding transaction 1
Getting transaction 1
Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'

encoded_sn_1 = Exception Response(132, 4, IllegalAddress).
exception code = 2.

Current transaction state - TRANSACTION_COMPLETE
Running transaction 2
SEND: 0x1 0x4 0x0 0x0 0x0 0x8 0xf1 0xcc
Changing state to IDLE - Last Frame End - 1580942727.093152, Current Time stamp - 1580942727.093152
Waiting for 3.5 char before next send - 4.01 ms
New Transaction state 'SENDING'
Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
Changing transaction state from 'WAITING FOR REPLY' to 'PROCESSING REPLY'
RECV: 0x1 0x4 0x10 0x31 0x39 0x35 0x30 0x31 0x38 0x30 0x30 0x30 0x30 0x32 0x36 0x42 0x32 0x0 0x0 0x7b 0xd4
Getting Frame - 0x4 0x10 0x31 0x39 0x35 0x30 0x31 0x38 0x30 0x30 0x30 0x30 0x32 0x36 0x42 0x32 0x0 0x0
Factory Response[ReadInputRegistersResponse: 4]
Frame advanced, resetting header!!
Adding transaction 1
Getting transaction 1
Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'

encoded_sn_2 = ReadRegisterResponse (8).
decoded serial number = 195018000026B2.

Особенности устройства

  • Устройство: E + E Датчик CO2 Elektronik, серия EE872
  • Modbus RTU, 9600/8-N-1, подчиненный 1
  • Руководство пользователя (раздел 5.2 содержит информацию Modbus RTU)
  • Устройство подключено к Windows машина, на которой я запускаю этот Python скрипт

Пакеты

Я использую Python 3.6 на Windows 10.

pymodbus==2.3.0

В сторону: извините за многословность этого вопроса, я просто хотел убедиться, что я развел потенциальные вопросы заранее.

...