Arduino Nano в качестве мастера modbus rtu
- Войдите на сайт для отправки комментариев
Втр, 17/12/2019 - 12:16
Добрый день!
Использовал библиотеку https://github.com/4-20ma/ModbusMaster
Последовательный порт для вывода результатов не использую
Вывод на дисплей работает нормально
Подключение:
RS485 - Arduino
TX - DI
RX - RO
RE/DE - D8
Соответственно 485 и устройство А к А, В к В
Питание у "устройства" свое, плата преобразователя 485 питается от 5В Arduino
Ведомое устройство, к сожалению, назвать не могу? *секрет*.. но вот его параметры:
Скорость 38400, 8N1
Slave ID 4
Всего нужно считать 22 регистра с 1000 по 1021 (параметр точно 0х03), данные в формате HEX
Все стандартно: не работает, на первой строке дисплея выдает сообщение из else (Error get data)
Посмотрите, пожалуйста, скетч (он не мой, нашел на каком-то форуме, там у товарища все работает. Я только привел его визуально в порядок, сделал комментарии и т.д.)
При этом если подключить "устройство" к ПК через USB-485 конвертер и воспользоваться программой https://sourceforge.net/projects/qmodmaster/ то там все прекрасно работает и считывается.
Физическое подключение вроде бы верное, так как при снятии питания с "устройства" перебор регистров на дисплее сильно замедляется, так же происходит если отключить от него 485..
// Подключаем библиотеки #include <ModbusMaster.h> #include <LiquidCrystal.h> // Макроопределения пинов // Определяем пины для подключения LCD дисплея и инициализируем библиотеку LiquidCrystal.h const int rs = 2, en = 3, d4 = 4, d5 = 5, d6 = 6, d7 = 7; LiquidCrystal lcd(rs, en, d4, d5, d6, d7); // Автоматический выбор приема/передачи RS-485 #define MAX485_DE 8 // Светодиод индикации успешных пакетов #define LED_SUCCESS 13 // Создаем экземпляр объекта ModbusMaster ModbusMaster node; void preTransmission(){ digitalWrite(MAX485_DE, true); } void postTransmission(){ digitalWrite(MAX485_DE, false); } void setup() { // Указываем количество столбцов и строк на LCD дисплее lcd.begin(16, 2); // Конфигурируем пины на выход pinMode(MAX485_DE, OUTPUT); pinMode(LED_SUCCESS, OUTPUT); // Устанавливаем RS485 на прием digitalWrite(MAX485_DE, false); // Запускаем Modbus на скорости 38400 Serial.begin(38400); // Определяем Modbus ведомый ID 4, используем Serial node.begin(4, Serial); // Обратные вызовы позволяют нам правильно настроить приемопередатчик RS485 node.preTransmission(preTransmission); node.postTransmission(postTransmission); } // какая то хрень для светодиода void led_blink(int blk) { for (int j=0; j < blk; j++) { digitalWrite(LED_SUCCESS, HIGH); delay(100); digitalWrite(LED_SUCCESS,LOW); delay(100); } } void loop() { // Поочередно перебираем регистры с 1000 по 1021 (с каждым новым циклом +1 к значению) for (int holdreg = 0x3E8; holdreg < 0x3FD; holdreg++) { // Выводим номер текущего регистра на LCD дисплей lcd.setCursor(0,0); lcd.print("Reg: "); lcd.print(holdreg); delay(100); // Объявляем переменную и пишем в нее значение считанного регистра uint8_t result = node.readHoldingRegisters(holdreg, 1); // Если успешно считали регистр то выполняем if, иначе else if (result == node.ku8MBSuccess) { led_blink(5); // что это и для чего lcd.print("Value for:"); lcd.print(holdreg); lcd.setCursor(0,1); lcd.print(node.getResponseBuffer(0)); delay(1500); digitalWrite(LED_SUCCESS,LOW); } else { led_blink(1); // что это и для чего lcd.setCursor(0,1); lcd.print("Error get data"); } } }
Убрал конструкцию с if - else, в каждом из регистров якобы лежит FFFF
Вообще фигня какая-то, что есть такого в программе под винду, чего нет в скетче
Вообще фигня какая-то, что есть такого в программе под винду, чего нет в скетче
В библиотеке некорректно идёт переключение на приём RS-485. Конкретно в этих строчках:
Переключаться надо не когда софтовый сериал буфер опустел, а когда последний байт полностью ушёл из UDR. Автор тестировал на MAX488, а он полнодуплексный. Так что у вас последний байт обрезается, и слэйв вас не понимает.
Переключаться надо не когда софтовый сериал буфер опустел, а когда последний байт полностью ушёл из UDR. Автор тестировал на MAX488, а он полнодуплексный. Так что у вас последний байт обрезается, и слэйв вас не понимает.
Для меня это очень сложно, сможете мне в этом помочь?
Скажите, пожалуйста, что нужно сделать, чтобы все заработало?
Простейший способ для вас - вставить задержку в текст коллбэка окончания передачи, до переключения на приём. На 38400 один байт идёт где-то 260 мкс. Ставить можно на период до 4.5 байт, в Modbus есть период тишины между пакетами. Например, delayMicroseconds(1000). Хотя и задержка, но не такая уж большая.
Есть решение изящнее, но попробуйте так пока.
Простейший способ для вас - вставить задержку в текст коллбэка окончания передачи, до переключения на приём
Это первый скетч по данной теме, мало чего осознаю, делал все по чужой инструкции не вникая в детали и единственное, что пришло в голову это влепить задержку перед строкой:
Само собой это не помогло :)
Само собой это не помогло :)
Порылся в папке библиотеки, если в файле ModbusMaster.h
В этих строках попробовать залезть, то не компилируется
Ок, полез в ModbusMaster.cpp
Там влепил задержку, компилируется, но не помогает
Не не там, только здесь:
Попробуйте разные задержки. И выведите в блоке else что вам возвращает библиотека, какую ошибку, на LCD.
Не не там, только здесь:
Попробовал несколько, вплоть до 10мс, только регистры на дисплее медленнее стали перебираться..
выведите в блоке else что вам возвращает библиотека, какую ошибку, на LCD.
Как это сделать?
Как это сделать?
Выведите result на LCD.
Выведите result на LCD.
Блин, я то уж начал монструозые конструкции искать типа таких:
Вывел result, на всех регистрах говорит "224"
Это ku8MBInvalidSlaveID. Типа ответил не тот, кого спрашивали.
Типа ответил не тот, кого спрашивали.
Ух ты, а где можно увидеть остальные коды ошибок? Сколько лазал по мануалам разработчика библиотеки не видел этого.. А то лишний раз хорошим людям голову по мелочам забиваю :)
Второй вопрос, а как это лечить если на линии больше никого нет?
Ух ты, а где можно увидеть остальные коды ошибок? Сколько лазал по мануалам разработчика библиотеки не видел этого.. А то лишний раз хорошим людям голову по мелочам забиваю :)
Второй вопрос, а как это лечить если на линии больше никого нет?
В файле ModbusMaster.h прописаны коды ошибок.
Лечить сложно сказать как. Может быть электрическая проблема - искажение данных в линии. Может быть прием обрезанной с головы посылки. Может быть со скоростью проблемы. Но поскольку ваше устройство точно работает, ошибка точно на вашей стороне, и это сильно упрощает дело.
Попробуйте ещё библиотеку поменять. Мне вот эта больше нравится: https://github.com/BlackBrix/Simple-Modbus-Master. Написана получше. И у меня работал Slave этого автора из коробки.
Типа ответил не тот, кого спрашивали.
Ух ты, а где можно увидеть остальные коды ошибок?
Так, с этим вроде разобрался, 224 это 0xE0, поиском в ModBusMaster.h нашел:
В файле ModbusMaster.h прописаны коды ошибок.
Да, спасибо, пока писал и вы об этом же сказали))
это сильно упрощает дело
Хорошо, попробую заменить железную часть, может быть действительно есть с этим проблемы. Однако, пробовал простенький скетч, который выводит всякую чушь в Serial, железо, собственно, использовал это же, но там насколько я знаю, линии RE/DE у 485 не использовались, так как это передача в одну сторону, от arduino к 485-USB и в ПК.
Мне вот эта больше нравится
Видел ее, очередной клон от некого Juan.. сколько примеров в сети и ни в одном нормально не описана моя задача, а сам я, как вы уже, думаю, заметили, не шибко прошарен в данной теме :)
А вы говорили об каком-то "изящном" способе, может быть с ним чего-то получится?
А вы говорили об каком-то "изящном" способе, может быть с ним чего-то получится?
Просто имел ввиду ту же задержку не делать блокирующей. Это не поможет.
Сейчас попробовал скормить скетчу заведомо неверный slave ID
Выдает ошибку E2, типа: "Ответ не был получен в выделенный период ожидания".
Буду пробовать менять железо, а может и библиотеку, хотя с этой даже как-то подружился немного :)
В любом случае, спасибо за помощь! Хорошего вечера!
Сейчас попробовал скормить скетчу заведомо неверный slave ID
Выдает ошибку E2, типа: "Ответ не был получен в выделенный период ожидания".
Буду пробовать менять железо, а может и библиотеку, хотя с этой даже как-то подружился немного :)
В любом случае, спасибо за помощь! Хорошего вечера!
Это говорит о том, что устройство вас слышит и понимает, и отвечает, но ваш мастер его не понимает. Копайте в эту сторону. Ну и смена библиотеки. То, что я рекомендовал - на порядок лучше вашей, проверено. Там автор очень хороший сишник.
Ну и смена библиотеки
Попробовал использовать библиотеку, что вы предложили
Посмотрите, пожалуйста, скетч.. на дисплей выводятся нули, что-то делаю не так?
Разобрался, дело было не в бобине, как говорится..
Сам модуль преобразователя какой-то бракованный попался, не знаю, что с ним не так.. ведь при обычном обмене с компьютером проблем не было, а вот с modbus появились. Поменял на другой такой же и пакеты посыпались.
В конечном счете заработал первый скетч, по совету ув. Schwarz78, обязательно проверю другую библиотеку.
Большое спасибо за помощь!
У меня такое было, когда девайс пожёг китайский модуль RS485 - что-то так подал в линию, что микросхема впала в горячку. После чего часть пакетов начали приходить битыми.
Товарищи, есть еще вопрос в продолжение этой темы.
При считывании регистра (два байта), получаю, например, A26, а хотелось бы 0A26..
Куда пропал ноль? Это довольно важно, так как, например, первые два регистра в моем случае - это серийный номер устройства.
Должен быть номер: 1234 0567, а приходит 1234 567.. где опять потерялся ноль?
Товарищи, есть еще вопрос в продолжение этой темы.
При считывании регистра (два байта), получаю, например, A26, а хотелось бы 0A26..
Куда пропал ноль? Это довольно важно, так как, например, первые два регистра в моем случае - это серийный номер устройства.
Должен быть номер: 1234 0567, а приходит 1234 567.. где опять потерялся ноль?
А как именно получаете? Приведите код, иначе непонятно.
Подозреваю, что библиотека вашего LCD жрёт лидирующий ноль просто напросто. Даже скорее всего. Lcd.print - это форматированный вывод, конечно. Берите сырое значение, и делайте с ним что угодно. Нули не пропадают, их может скрывать функция вывода на экран)
Приведите код, иначе непонятно.
Библиотека из стандартных примеров Arduino. Вот так спрашиваю:
Как отвечает, думаю, вы поняли из предыдущего сообщения
Подозреваю, что библиотека вашего LCD жрёт лидирующий ноль просто напросто
А может сам дисплей быть "не такой как все"?
Берите сырое значение, и делайте с ним что угодно
Это как? Через lcd.write?
Получается для разных типов данных использовать разные варианты вывода?
Но ведь это будет не String? Как бы хуже не сделать..
Вы используете форматированный вывод, он убирает лидирующие нули с экрана, для читаемости. Из реальных значений нули никуда не пропадают. Попробуйте почитать это для начала: https://cpp.com.ru/shildt_spr_po_c/. Функция printf(). Там всё написано очень простым языком.
Но ведь это будет не String? Как бы хуже не сделать..
Рано вам о String думать) Думайте о char[], то есть об обычных строках символов.
Вот ещё посмотрите: http://arduino.ru/Reference/Serial/Print
Ваш lcd.print явно наследует от этого общего с print родителя. Но вам точно рано об этом думать. Подумайте, как вам получить целое 16-разрядное число от вашего "устройства", вам больше и не надо.
Как в ModbusRtu https://github.com/smarmengol/Modbus-Master-Slave-for-Arduino задавать стартовый регистр для входящих пакетов?
Как вывести в print шестнадцатеричные числа в десятичной системы в формате 001? Числа от 1 до 999 кодируются в два байта 0х0000, к примеру число 3 выводиться в принт как 3, а нужно в трехзначном виде 003 или число 25 как 025. Не соображу как это сделать.
выводить лидирующие нули, если <=9 то два, если <=99 то один
if (a[i] < 10)Serial.print('0');
if (a[i] < 100)Serial.print('0');
таки да )))
Скетч использует 262108 байт (25%) памяти устройства. Всего доступно 1044464 байт.
Глобальные переменные используют 26912 байт (32%) динамической памяти, оставляя 55008 байт для локальных переменных. Максимум: 81920 байт.
против твоих
Скетч использует 262104 байт (25%) памяти устройства. Всего доступно 1044464 байт.
Глобальные переменные используют 26900 байт (32%) динамической памяти, оставляя 55020 байт для локальных переменных. Максимум: 81920 байт.
Понял, уже что-то получается)
Как указать диапазон чисел от и до? После 1000 выводит и до 100 выводит, а вот как от 100 до 1000 указать не знаю.
тебе разжевали и в рот положили, обе строки задействуй при разборе значения до вывода
Понял. if (au16data[1] > 100 && au16data[1] < 1000)u8g2.print(au16data[1]);
Понял. if (au16data[1] > 100 && au16data[1] < 1000)u8g2.print(au16data[1]);
с логикой у тебя совсем не айс...Садман жеж подсказал оптимальный алгоритм
Ещё можно открыть для себя snprintf().
Я не совсем правильно вопрос задал. С установкой нулей я сделал как Садман подсказал. Остальное для того чтоб числа с нулями не пересекались на дисплее.
Собрал строчку, но что то длинная портянка получилась, а всего то одна строчка на дисплее "-9999.9999", точка зафиксирована на дисплее.
Есть вариант проще, подсказали в разделе программирования:
Но строку типа данных float нельзя отцентровать по точке, строка плавающая в зависимости от количества чисел или можно?
Добрый день.
Можно ли редактировать в этой библиотеке (https://github.com/4-20ma/ModbusMaster) параметры связи, такие как четность, стоповые биты и длина данных? В тексте программы я этой возможности не нашел. как можно решить эту проблему?
Спасибо.
Добрый день.
Можно ли редактировать в этой библиотеке (https://github.com/4-20ma/ModbusMaster) параметры связи, такие как четность, стоповые биты и длина данных? В тексте программы я этой возможности не нашел. как можно решить эту проблему?
Спасибо.
@param &serial reference to serial port object (Serial, Serial1, ... Serial3)
ua6em : @param &serial reference to serial port object (Serial, Serial1, ... Serial3)
Не понял, что это. Как этим воспользоваться для задания четности, стоповых бит и длины данных?
К модбасу эти параметры не относятся. Смотри как настраивается сериал. https://www.arduino.cc/reference/en/language/functions/communication/serial/begin/
Cпасибо.
Добрый день. Schwarz78
Я попробовал рекомендованную Вами библиотеку https://github.com/BlackBrix/Simple-Modbus-Master.
Задача у меня простая - прочитать с датчика температуры и влажности (Slave addr 1, 9600, Serial_8N2) значения температуры (HR0) и влажности (HR1). Однако читаются нули. Датчик исправен (проверен с помощью другого Modbus мастера). Привожу текст скетча:
Что не так?
Спасибо
В обычной SoftwareSerial не было возможности назначать параметры, посмотрели, в вашей есть?
Да, работает.
Скажите, почему задача прочитать два регистра, а количество регистров 1 (строка 18)?
В данном случае читается только значение температуры = 0. Я устанавливал 2 - читались два параметра, равные нулю.
SoftwareSerial на 115200 - это безудержный оптимизм. 9600 ставьте.
Да бог с ним, с SoftwareSerial на 115200 (хотя он работает). Как насчет моей проблемы?
Спасибо.