Как решить исключение Pymodbus, когда мастер запрашивает хранение значений реестра ведомого устройства Arduino

В настоящее время я пытаюсь получить данные от моего ведомого устройства Arduino на свой компьютер. Мне удалось создать ведомое устройство Arduino. Однако, когда я пытаюсь получить данные со своего компьютера с помощью библиотеки Pymodbus, мой код не может получить данные от Arduino и вызывает исключение ModbusIOException. Для спецификаций моего проекта я пытаюсь создать Modbus RTU с Arduino для имитации датчика со случайными числами в качестве показаний. Код Arduino использует библиотеку Modbus-Arduino Андре Сарменто.

https://github.com/andresarmento/modbus-ардуино

Я уже проверил свой ведомый Arduino, если он работает. Я попытался прочитать данные через эмулятор Modbus Master (QModMaster), и он работал нормально. Это может доказать, что сама проблема связана с кодом Мастера. Кроме того, последовательное соединение работает нормально, так как self.client.connect() возвращает True.

Это скриншоты конфигураций QModMaster.

Ведомые конфигурацииКонфигурации последовательного порта

Код Python для мастера:

class ModbusRTU:
    def __init__(self, graph_name, port, baudrate=9600, 
                 stopbits=1, bytesize=8, parity='N', 
                 timeout=1):                                       
        self.graph_name = graph_name
        self.client = ModbusSerialClient(method='rtu', 
                                         port=port, 
                                         baudrate=baudrate, 
                                         parity=parity, 
                                         timeout=timeout)
        self.connection = self.client.connect()
        result = self.client.read_holding_registers(address=0, 
                                                  count=2, 
                                                  unit=1)
        print(result.registers) 

if __name__ == '__main__':
    modbus = ModbusRTU(graph_name='/dev/ttyACM0', 
                       port='/dev/ttyACM0', baudrate=9600, 
                       stopbits=1, bytesize=8, parity='N', 
                       timeout=1)
    print(modbus.check_connection())

Код Arduino для моделируемого ведомого устройства и датчика:

#include <Modbus.h>
#include <ModbusSerial.h>

ModbusSerial mb;
const int READING = 0;
const int DECIMAL = 1;

void setup() {
  mb.config(&Serial, 9600, SERIAL_8N1);
  mb.setSlaveId(1);
  mb.addHreg(READING);
  mb.addHreg(DECIMAL);
}

void loop() {
  mb.task();
  mb.Hreg(READING, random(1, 201));
  mb.Hreg(DECIMAL, random(0, 4));
}

После печати results.registers это предположительно список целых чисел. Однако он просто вызывает исключение ModbusIOException с сообщением:

'ModbusIOException' object has no attribute 'registers'
  File "/home/kebaranas/PythonProjects/ThirsyWell/tools/utilities.py", line 21, in __init__
    print(result.registers)
  File "/home/kebaranas/PythonProjects/ThirsyWell/tools/utilities.py", line 29, in <module>
    timeout=1)

Это также дает это сообщение.

Modbus Error: [Input/Output] Modbus Error: [Invalid Message] Incomplete message received, expected at least 2 bytes (0 received)
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
2 053
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Перед print(result.registers) попробуйте следующий фрагмент кода:

if not result.isError():
    print(result.registers)
else:
    print("error: {}".format(result))

Также заполните другой аргумент ModbusSerialClient().

Вот обновленный фрагмент вашего кода:

from pymodbus.client.sync import ModbusSerialClient

class ModbusRTU:
    def __init__(self, graph_name, port, baudrate=9600,
                 stopbits=1, bytesize=8, parity='N',
                 timeout=1):
        self.graph_name = graph_name
        self.client = ModbusSerialClient(
            method='rtu',
            port=port,
            baudrate=baudrate,
            parity=parity,
            timeout=timeout,
            stopbits=stopbits,
            bytesize=bytesize
        )
        self.connection = self.client.connect()
        result = self.client.read_input_registers(address=1,
                                                  count=2,
                                                  unit=1)
        if not result.isError():
            print(result.registers)
        else:
            print("error: {}".format(result))

if __name__ == '__main__':
    modbus = ModbusRTU(
        graph_name='/dev/ttyACM0',
        port='/dev/ttyACM0', baudrate=9600,
        stopbits=1, bytesize=8, parity='N',
        timeout=1
    )

Консоль выдала это сообщение: ошибка: ответ на исключение (129, 1, IllegalAddress)

kebaranas 30.05.2019 15:37

вы уверены в address=1?

Benyamin Jafari 30.05.2019 15:41

Насколько мне известно, адрес определяется первым регистром временного хранения, который можно определить с помощью const int READING = 1; и mb.addHreg(ЧТЕНИЕ); из кода Ардуино. Основываясь на том, что я прочитал, метод Pymodbus автоматически добавляет 40000, что является фактическим адресом регистра хранения. Между тем, идентификатор устройства совпадает с идентификатором подчиненного устройства, который определяется с помощью mb.setSlaveId(1);. Когда адрес и идентификатор совпадают, консоль выдает следующее сообщение об ошибке: Ошибка Modbus: [Ввод/вывод] Ошибка Modbus: [Недопустимое сообщение] Получено неполное сообщение, ожидается не менее 2 байтов (получено 0)

kebaranas 30.05.2019 16:42

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

result = self.client.read_input_registers(address=1, count=2, unit=1)

К:

result = self.client.read_holding_registers(address=1, count=2, unit=1)

Имейте в виду, что спецификация Modbus определяет два разных типа регистров: временные и входные, в зависимости от области памяти, в которой они расположены.

Простите мою ошибку, чтобы снова изменить метод. Однако я уже пытался использовать метод read_holding_register, и консоль просто снова написала это сообщение: Ошибка Modbus: [Ввод/вывод] Ошибка Modbus: [Неверное сообщение] Получено неполное сообщение, ожидается не менее 2 байтов (получено 0)

kebaranas 30.05.2019 16:36

Без проблем. Ошибка, которую вы видите, означает, что вы не получаете никаких данных обратно. Единственное, о чем я могу думать, это настройка тайм-аута, но она уже достаточно высока, 1 секунда. Может быть, есть способ определить его и для Arduino? Вы делаете подключение напрямую по TTL? или ты хоть RS485 собираешься? Какое оборудование у вас есть на стороне компьютера?

Marcos G. 30.05.2019 17:20

Я не думал об определении тайм-аута на стороне Arduino. На данный момент я просто подключаюсь через USB-разъем Arduino.

kebaranas 30.05.2019 19:37

OK. Я не вижу причин, чтобы вы могли подключаться к QModMaster, а не к Pymodbus. Возможно, вы можете попробовать pylibmodbus вместо github.com/стефан/pylibmodbus В противном случае, возможно, вы можете опубликовать скриншот с настройками, которые у вас есть в QModMaster.

Marcos G. 30.05.2019 19:48

Обновил пост скриншотами настроек QModMaster. В настоящее время я пытаюсь использовать вместо этого pylibmodbus.

kebaranas 31.05.2019 08:07

Понятно... Вы используете Windows для QModMaster, но Linux для pymodbus? Вы уверены, что используете тот же порт? может быть, запустить «dmesg | grep tty» сразу после подключения USB-разъема и посмотреть, что сообщает ядро?

Marcos G. 31.05.2019 08:28

Да, я использую Windows для QModMaster и Linux для pymodbus. Он написал это сообщение: [ 0.000000] консоль [tty0] включена [ 1.459900] tty ptmx: совпадения хэша [ 2573.361290] cdc_acm 3-3:1.0: ttyACM0: устройство USB ACM [10973.937479] cdc_acm 1-1.2:1.1: ttyACM1: USB ACM устройство [10981.955749] cdc_acm 1-1.2:1.1: ttyACM1: USB-устройство ACM [11009.840820] cdc_acm 1-1.2:1.1: ttyACM1: USB-устройство ACM

kebaranas 31.05.2019 09:08

Таким образом, у вас действительно есть два порта: /dev/ttyACM0 и /dev/ttyACM1. Я немного смущен этим. Вы пробовали pymodbus с обоими?

Marcos G. 31.05.2019 09:13

Насколько я могу судить, оба могут относиться к Arduino Mega. Однако прямо сейчас программное обеспечение Arduino идентифицирует Mega только как /dev/ttyACM0, а не /dev/ttyACM1. Я попробовал оба, Pymodbus смог подключиться к /dev/ttyACM0 (возвращает True), а /dev/ttyACM1 — нет. Я также смущен, почему /dev/ttyACM1 должен быть там, поскольку у меня есть только 1 Mega, подключенная к моему ПК.

kebaranas 31.05.2019 09:26

Исправление! /dev/ttyACM1 на самом деле мой телефон, подключенный к моему компьютеру, который я только что подключил. Каким-то образом, когда я пытаюсь повторно выполнить команду, имя порта остается, даже если я удалил его с ПК.

kebaranas 31.05.2019 09:43

Хорошо, это имеет больше смысла. Однако иногда у вас есть устройства с более чем одним последовательным портом. Я полагаю, вы полностью уверены, что код на вашем Arduino работает, верно? Вы все еще получаете IllegalAddress? или опять не регистрирует (ModbusIOException)?

Marcos G. 31.05.2019 10:26

После применения решения Беньямина Джафари я получаю более конкретное сообщение об ошибке, подобное этому: Ошибка Modbus: [Ввод/вывод] Ошибка Modbus: [Неверное сообщение] Получено неполное сообщение, ожидается не менее 2 байтов (получено 0). Тем не менее, это то же старое исключение ModbusIOException, когда я не применяю его решение.

kebaranas 31.05.2019 13:28

Это ошибка, которую вы обычно получаете, когда у вас есть проблемы с вашим оборудованием, но это не ваш случай, если у вас нет такой же проблемы в Windows ... Должно быть что-то, что мы упускаем. Я постараюсь воспроизвести вашу настройку, как только смогу

Marcos G. 31.05.2019 13:48

Я протестировал несколько разных конфигураций (на моей Teensy 3.6 у меня нет Arduino) и могу подтвердить, что проблема не в вашем коде Python. Я не могу скомпилировать библиотеку Modbus, которую вы используете на своем Arduino, с последовательным портом USB (&Serial), она работает только для аппаратных последовательных портов (не USB, таких как &Serial1). Я думаю, это связано с моим оборудованием. На USB у меня работает это: github.com/стефан/модбусино, возможно, вы можете попробовать, но имейте в виду, что это весьма ограничено...

Marcos G. 01.06.2019 18:03

Что действительно ускользает от меня, так это то, как вы заставили это работать с QModMaster, а не с Python, это то, что я не смог воспроизвести. Все конфигурации, с которыми я работал, вели себя одинаково с любым клиентом Modbus.

Marcos G. 01.06.2019 18:06

Я пробовал Modbusino и раньше. Я не работал ни для QModMaster, ни для своего кода Python.

kebaranas 01.06.2019 18:25

Ясно... Думаю, это просто аппаратная проблема. Я не думаю, что смогу помочь вам без тестирования на той же платформе, но я буду следить, если вы найдете решение. Удачи!

Marcos G. 01.06.2019 18:39

Мне пришло в голову еще кое-что: проверяли ли вы свой последовательный порт Arduino на Linux? Может быть, вы можете взять простой скетч, который выводит числа в консоль, чтобы убедиться, что все работает: arduino.cc/en/Серийный номер/Печать, но обязательно протестируйте его под Linux (вы можете использовать stty или minicom)

Marcos G. 01.06.2019 19:24

Я использовал pylibmodbus, и это сработало. Спасибо! Я обновлю свой собственный вопрос об этом позже.

kebaranas 01.06.2019 21:42

отличный. Я никогда не сталкивался с другим поведением при использовании libmodbus или pymodbus, уж точно не что-то подобное.

Marcos G. 01.06.2019 21:55
Ответ принят как подходящий

Я уже нашел решение для этого, спасибо за помощь нескольким людям. QModMaster использует библиотеку libmodbus. Поскольку смоделированное ведомое устройство и датчик Arduino работали с QModMaster, было бы проще изменить предыдущую библиотеку и вместо этого использовать libmodbus. К счастью, для libmodbus существует эквивалент Python — pylibmodbus. Это ссылка на библиотеку https://github.com/stephane/pylibmodbus.

    from pylibmodbus import ModbusRtu


    class ModbusRTU:
        def __init__(self, port, baudrate=9600, databit=8, parity='None', 
                     stopbit=1, timeout=1000):
            self.parity = {'Odd': 'O', 'Even': 'E', 'None': 'N'}
            self.modbus = ModbusRtu(device=port.encode('ascii'),   
                                    data_bit=databit, baud=baudrate,
                                    parity=self.parity[parity] \
                                           .encode('ascii'), 
                                    stop_bit=stopbit)
            self.modbus.set_response_timeout(timeout/1000)
            self.modbus.connect()
            self.modbus.set_slave(1)
            result = self.modbus.read_registers(0, 2)
            print(result)
            self.modbus.close()


    if __name__ == '__main__':
        main = ModbusRTU('/dev/ttyACM0', baudrate=9600, databit=8, 
                         parity='None', stopbit=1)

Другие вопросы по теме