В настоящее время я пытаюсь получить данные от моего ведомого устройства 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)
Перед 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
)
вы уверены в address=1
?
Насколько мне известно, адрес определяется первым регистром временного хранения, который можно определить с помощью const int READING = 1; и mb.addHreg(ЧТЕНИЕ); из кода Ардуино. Основываясь на том, что я прочитал, метод Pymodbus автоматически добавляет 40000, что является фактическим адресом регистра хранения. Между тем, идентификатор устройства совпадает с идентификатором подчиненного устройства, который определяется с помощью mb.setSlaveId(1);. Когда адрес и идентификатор совпадают, консоль выдает следующее сообщение об ошибке: Ошибка Modbus: [Ввод/вывод] Ошибка Modbus: [Недопустимое сообщение] Получено неполное сообщение, ожидается не менее 2 байтов (получено 0)
Вы определяете регистры хранения в подчиненном устройстве, но пытаетесь прочитать их как регистры ввода, попробуйте изменить эту строку:
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)
Без проблем. Ошибка, которую вы видите, означает, что вы не получаете никаких данных обратно. Единственное, о чем я могу думать, это настройка тайм-аута, но она уже достаточно высока, 1 секунда. Может быть, есть способ определить его и для Arduino? Вы делаете подключение напрямую по TTL? или ты хоть RS485 собираешься? Какое оборудование у вас есть на стороне компьютера?
Я не думал об определении тайм-аута на стороне Arduino. На данный момент я просто подключаюсь через USB-разъем Arduino.
OK. Я не вижу причин, чтобы вы могли подключаться к QModMaster, а не к Pymodbus. Возможно, вы можете попробовать pylibmodbus вместо github.com/стефан/pylibmodbus В противном случае, возможно, вы можете опубликовать скриншот с настройками, которые у вас есть в QModMaster.
Обновил пост скриншотами настроек QModMaster. В настоящее время я пытаюсь использовать вместо этого pylibmodbus.
Понятно... Вы используете Windows для QModMaster, но Linux для pymodbus? Вы уверены, что используете тот же порт? может быть, запустить «dmesg | grep tty» сразу после подключения USB-разъема и посмотреть, что сообщает ядро?
Да, я использую 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
Таким образом, у вас действительно есть два порта: /dev/ttyACM0 и /dev/ttyACM1. Я немного смущен этим. Вы пробовали pymodbus с обоими?
Насколько я могу судить, оба могут относиться к Arduino Mega. Однако прямо сейчас программное обеспечение Arduino идентифицирует Mega только как /dev/ttyACM0, а не /dev/ttyACM1. Я попробовал оба, Pymodbus смог подключиться к /dev/ttyACM0 (возвращает True), а /dev/ttyACM1 — нет. Я также смущен, почему /dev/ttyACM1 должен быть там, поскольку у меня есть только 1 Mega, подключенная к моему ПК.
Исправление! /dev/ttyACM1 на самом деле мой телефон, подключенный к моему компьютеру, который я только что подключил. Каким-то образом, когда я пытаюсь повторно выполнить команду, имя порта остается, даже если я удалил его с ПК.
Хорошо, это имеет больше смысла. Однако иногда у вас есть устройства с более чем одним последовательным портом. Я полагаю, вы полностью уверены, что код на вашем Arduino работает, верно? Вы все еще получаете IllegalAddress? или опять не регистрирует (ModbusIOException)?
После применения решения Беньямина Джафари я получаю более конкретное сообщение об ошибке, подобное этому: Ошибка Modbus: [Ввод/вывод] Ошибка Modbus: [Неверное сообщение] Получено неполное сообщение, ожидается не менее 2 байтов (получено 0). Тем не менее, это то же старое исключение ModbusIOException, когда я не применяю его решение.
Это ошибка, которую вы обычно получаете, когда у вас есть проблемы с вашим оборудованием, но это не ваш случай, если у вас нет такой же проблемы в Windows ... Должно быть что-то, что мы упускаем. Я постараюсь воспроизвести вашу настройку, как только смогу
Я протестировал несколько разных конфигураций (на моей Teensy 3.6 у меня нет Arduino) и могу подтвердить, что проблема не в вашем коде Python. Я не могу скомпилировать библиотеку Modbus, которую вы используете на своем Arduino, с последовательным портом USB (&Serial), она работает только для аппаратных последовательных портов (не USB, таких как &Serial1). Я думаю, это связано с моим оборудованием. На USB у меня работает это: github.com/стефан/модбусино, возможно, вы можете попробовать, но имейте в виду, что это весьма ограничено...
Что действительно ускользает от меня, так это то, как вы заставили это работать с QModMaster, а не с Python, это то, что я не смог воспроизвести. Все конфигурации, с которыми я работал, вели себя одинаково с любым клиентом Modbus.
Я пробовал Modbusino и раньше. Я не работал ни для QModMaster, ни для своего кода Python.
Ясно... Думаю, это просто аппаратная проблема. Я не думаю, что смогу помочь вам без тестирования на той же платформе, но я буду следить, если вы найдете решение. Удачи!
Мне пришло в голову еще кое-что: проверяли ли вы свой последовательный порт Arduino на Linux? Может быть, вы можете взять простой скетч, который выводит числа в консоль, чтобы убедиться, что все работает: arduino.cc/en/Серийный номер/Печать, но обязательно протестируйте его под Linux (вы можете использовать stty или minicom)
Я использовал pylibmodbus, и это сработало. Спасибо! Я обновлю свой собственный вопрос об этом позже.
отличный. Я никогда не сталкивался с другим поведением при использовании libmodbus или pymodbus, уж точно не что-то подобное.
Я уже нашел решение для этого, спасибо за помощь нескольким людям. 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)
Консоль выдала это сообщение: ошибка: ответ на исключение (129, 1, IllegalAddress)