1. Обязательно представиться на русском языке кириллицей (заполнить поле "Имя").
  2. Фиктивные имена мы не приветствуем. Ивановых и Пупкиных здесь уже достаточно.
  3. Не писать свой вопрос в первую попавшуюся тему - вместо этого создать новую тему.
  4. За поиск, предложение и обсуждение пиратского ПО и средств взлома - бан без предупреждения.
  5. Рекламу и частные объявления "куплю/продам/есть халтура" мы не размещаем ни на каких условиях.
  6. Перед тем как что-то написать - читать здесь и здесь, а студентам - обязательно здесь.
  7. Не надо писать в ЛС администраторам свои технические вопросы. Администраторы форума отлично знают как работает форум, а не все-все контроллеры, о которых тут пишут.

Странности libmodbus

RS-485, ProfiBUS, 4-20 mA, Wi-Fi, GSM и так далее
Ответить

Автор темы
plastictown
здесь недавно
здесь недавно
Сообщения: 5
Зарегистрирован: 19 апр 2018, 13:20
Имя: Михаил
Страна: Россия
город/регион: СПб
Благодарил (а): 2 раза

Странности libmodbus

Сообщение plastictown »

Доброго времени суток, коллеги! Вопрос по программированию. Пишу программу для опроса нескольких слейвов Modbus RTU на одной линии RS-485 с использованием libmodbus. Железо на линии совершенно разное. Каждое устройство по отдельности опрашивается весьма быстро (около 10-15 мс), однако при вызове modbus_set_slave() для смены адресата опроса вся картина сильно портится, контроллеры не отвечают многократно и все очень медленно.

тестовый код:

Код: Выделить всё

  for (int i = 0; i < 100; i++)
  {
    if(i%2)
      modbus_set_slave(ctx, 1);
    else
      modbus_set_slave(ctx, 2);

    int rd8 = modbus_read_bits(ctx, 20, 10, d8.data());
    if (rd8 == -1) cerr << "read bits failed" << endl;

    int rd16 = modbus_read_input_registers(ctx, 50, 5, d16.data());
    if (rd16 == -1) cerr << "read regs failed" << endl;

    for (auto& u8 : d8)
    {
      cout << (int)u8 << " ";
    }
    cout << endl;

    for (auto& u16 : d16)
    {
      cout << (int)u16 << " ";
    }
    cout << endl;
  }
Сама по себе функция modbus_set_slave() судя по исходникам библиотеки только меняет одно число в запросе (в принципе, логично), так что совершенно неясно почему так. Экспериментировал с modbus_set_response_timeout() и modbus_set_error_recovery() - безрезультатно. Буду рад любым идеям. Спасибо!
Аватара пользователя

hell_boy
почётный участник форума
почётный участник форума
Сообщения: 1746
Зарегистрирован: 18 янв 2009, 12:25
Имя: Дмитрий
Страна: Россия
город/регион: Москва
Благодарил (а): 6 раз
Поблагодарили: 132 раза

Странности libmodbus

Сообщение hell_boy »

Следующий трюк:
modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485);

Код будет выглядеть примерно так

Код: Выделить всё

modbus_t *ctx;
uint16_t input_reg[128];  //number of registers required

ctx =  modbus_new_rtu('/dev/ttyUSB0', 19200, 'N', 8, 1);    //where /dev/ttyUSB0 is your serial port, 19200 is your buad rate, No parity, 8 data bits and 1 stop bit
modbus_rtu_set_serial_mode(ctx, 1); This is the same as your MODBUS_RTU_RS485 but I had issues using that in early versions hence the 1.
if (modbus_connect(ctx) == -1) 
  // handle error here
modbus_rtu_set_rts(ctx, MODBUS_RTU_RTS_NONE);  // not really needed unless you want to use RTS such as RTS_UP or RTS_DOWN

response_timeout.tv_sec = 0;   //set some default timeouts in secs
response_timeout.tv_usec = 500000;  //set some default timeouts in usec
modbus_set_response_timeout(ctx, &response_timeout);

modbus_set_debug(ctx, TRUE);   //turn on debug if needed

while (1) {
   //poll your slave devices here....
   modbus_flush(ctx);  //flush any previous data
   modbus_set_slave(ctx, address);    //set slave address to be used

   rc = modbus_read_input_registers(ctx, start_reg, no_of_reg, input_reg);  //Read some Input Registers
}

modbus_close(ctx);
modbus_free(ctx);
взято отсюда https://groups.google.com/forum/#!topic ... discussion
"Умные люди обсуждают идеи, средние - события, а глупые - людей" Л.Н. Толстой

Автор темы
plastictown
здесь недавно
здесь недавно
Сообщения: 5
Зарегистрирован: 19 апр 2018, 13:20
Имя: Михаил
Страна: Россия
город/регион: СПб
Благодарил (а): 2 раза

Странности libmodbus

Сообщение plastictown »

hell_boy писал(а): 20 апр 2018, 11:41 Следующий трюк:
modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485);
Спасибо, но это не помогает. Сегодня пробовал менять топологию на звезду, экспериментировал с таймаутами, в итоге ошибка все та же:
Waiting for a confirmation...
ERROR Unknown error: select
В лучшем случае долетает 80% пакетов, но это редкость. Обычно не больше 50.
Небольшой кусок вывода:

Код: Выделить всё

Bytes flushed (0)
[03][01][00][14][00][0A][FD][EB]
Waiting for a confirmation...
ERROR Unknown error: select
0: first - fail
Bytes flushed (0)
[03][01][00][14][00][0A][FD][EB]
Waiting for a confirmation...
ERROR Unknown error: select
0: first - fail
Bytes flushed (0)
[03][01][00][14][00][0A][FD][EB]
Waiting for a confirmation...
ERROR Unknown error: select
0: first - fail
Bytes flushed (0)
[03][01][00][14][00][0A][FD][EB]
Waiting for a confirmation...
ERROR Unknown error: select
0: first - fail
Bytes flushed (0)
[03][01][00][14][00][0A][FD][EB]
Waiting for a confirmation...
ERROR Unknown error: select
0: first - fail
Bytes flushed (0)
[03][01][00][14][00][0A][FD][EB]
Waiting for a confirmation...
ERROR Unknown error: select
0: first - fail
Не знаю в чем еще может быть причина.

Sergey_P
здесь недавно
здесь недавно
Сообщения: 63
Зарегистрирован: 26 мар 2018, 13:36
Имя: Сергей Поминовский
Страна: Украина
город/регион: Киев
Благодарил (а): 12 раз
Поблагодарили: 17 раз

Странности libmodbus

Сообщение Sergey_P »

Попробуйте добавить перед каждой посылкой запроса задержку порядка 20-50 мс.

Автор темы
plastictown
здесь недавно
здесь недавно
Сообщения: 5
Зарегистрирован: 19 апр 2018, 13:20
Имя: Михаил
Страна: Россия
город/регион: СПб
Благодарил (а): 2 раза

Странности libmodbus

Сообщение plastictown »

Sergey_P писал(а): 20 апр 2018, 18:29 Попробуйте добавить перед каждой посылкой запроса задержку порядка 20-50 мс.
Да пробовал уже. И перед и после и больше и меньше. Магия какая-то:)
Аватара пользователя

hell_boy
почётный участник форума
почётный участник форума
Сообщения: 1746
Зарегистрирован: 18 янв 2009, 12:25
Имя: Дмитрий
Страна: Россия
город/регион: Москва
Благодарил (а): 6 раз
Поблагодарили: 132 раза

Странности libmodbus

Сообщение hell_boy »

Это не ошибка libmodbus. У одного или нескольких слейвов в сети RS-485 кривая реализация modbus. В моей практике был похожий случай. В итоге выяснилось, что нормально можно опрашивать только по 80 бит или 10 байт. Если опрашиваешь меньше, то слейв все равно выдает столько. Если - больше, то - ничего не выдает. Еще был прикол с одним из электросчетчиков, который перезагружался, получив запрос с чужим адресом.
ERROR Unknown error: select
возникает только по таймауту ответа или неправильному дескриптору ctx. Попробуйте после каждого modbus_set_slave делать modbus_flush(ctx); . В ctx что-то остается от предыдущего запроса (длина ответа) и он начинает обрабатывать несуществующий ответ. Еще полезно для первого запроса слейва поставить таймаут запроса достаточно большим, вдруг слейв пошел перезагружаться.

Код: Выделить всё

tv_sec = 60;
tv_usec = 0;
modbus_set_response_timeout(ctx, tv_sec,tv_usec); 
"Умные люди обсуждают идеи, средние - события, а глупые - людей" Л.Н. Толстой
Аватара пользователя

MuadDib
частый гость
частый гость
Сообщения: 462
Зарегистрирован: 31 июл 2010, 09:12
Имя: Павел
Страна: РФ
Благодарил (а): 8 раз
Поблагодарили: 13 раз

Странности libmodbus

Сообщение MuadDib »

plastictown писал(а): 19 апр 2018, 13:37 Доброго времени суток, коллеги! Вопрос по программированию. Пишу программу для опроса нескольких слейвов Modbus RTU на одной линии RS-485 с использованием libmodbus. Железо на линии совершенно разное. Каждое устройство по отдельности опрашивается весьма быстро (около 10-15 мс), однако при вызове modbus_set_slave() для смены адресата опроса вся картина сильно портится, контроллеры не отвечают многократно и все очень медленно.
А вы уверены, что дело не в таймаутах? Если у вас таймаут существенно меньше, чем время ответа прибора, может получиться что-то такое:
  • Идет первый запрос на прибор, на вашей стороне валится по таймауту. Но прибор получил запрос и начал готовить ответ.
  • Идет второй запрос на прибор, валится по таймауту. Прибор поставил запрос в очередь.
  • ...
  • Идет запрос N. И тут прибор наконец разродился ответом (на самый первый запрос), и ваш мастер получает ожидаемую информацию.
Если прибор только один, то вы заметите проблему только в начале работы мастера (несколько запросов не выполнятся по таймауту). То что "порядковый номер" запроса не соответствует ответу, проверить в Modbus RTU невозможно (если все запросы одинаковые). Но вот если приборов больше одного, вы сразу же увидите проблему при переключении. Вы отправляете запрос на прибор 2, но читаете из линии ответ от "тормозного" прибора 1 на один из предыдущих запросов, в то время как прибор 2 еще думает над ответом. И это сразу же идентифицируется модбасом как ошибка.

Мораль: попробуйте сильно (до 1000 мс) увеличить таймаут и посмотреть, изменится ли ситуация.

Отправлено спустя 4 минуты 54 секунды:
http://libmodbus.org/docs/v3.0.6/modbus ... meout.html

Автор темы
plastictown
здесь недавно
здесь недавно
Сообщения: 5
Зарегистрирован: 19 апр 2018, 13:20
Имя: Михаил
Страна: Россия
город/регион: СПб
Благодарил (а): 2 раза

Странности libmodbus

Сообщение plastictown »

MuadDib писал(а): 23 апр 2018, 06:17 А вы уверены, что дело не в таймаутах?
С учетом вышесказанного: пробовал таймауты и больше, но все равно первые один-два запроса остаются неудачными, но потом все становится нормально и очень быстро. Таймауты больше 1с никак не влияют на ситуацию, а меньше 1с делают картину намного хуже. Тем не менее, картина остается ужасной: если на линии. скажем 120 девайсов, опрос займет не менее двух минут, а это никуда не годится.

Вот такой код:

Код: Выделить всё

    this_thread::sleep_for(std::chrono::milliseconds{ 10 });
    modbus_flush(ctx);
    this_thread::sleep_for(std::chrono::milliseconds{ 10 });
    for (int j = 0; j < 3; j++)
    {
      int rd8 = modbus_read_bits(ctx, 20, 10, d8.data());
      if (rd8 == -1) { cout << i % 2 << ": first - fail" << endl; modbus_flush(ctx);  continue; }
      else { ctr[i % 2]++; cout << i % 2 << ": first - ok" << endl; break; }
    }
Дает наилучший результат, хотя результат все равно так себе. У меня уже идеи закончились:)
Аватара пользователя

MuadDib
частый гость
частый гость
Сообщения: 462
Зарегистрирован: 31 июл 2010, 09:12
Имя: Павел
Страна: РФ
Благодарил (а): 8 раз
Поблагодарили: 13 раз

Странности libmodbus

Сообщение MuadDib »

plastictown писал(а): 23 апр 2018, 13:56 С учетом вышесказанного: пробовал таймауты и больше, но все равно первые один-два запроса остаются неудачными, но потом все становится нормально и очень быстро.
Это существенная информация. Проблема может быть в том, что девайсы отвечают очень медленно (как я написал в предыдущем посте), а может быть и в чем-то другом. ИМХО, надо выяснить, почему первые запросы фейлят. Рекомендую следующий эксперимент:

1. Ваш мастер выдает единственный модбас-запрос и ждет ответа до бесконечности.
2. Когда ответ приходит, вы фиксируете время, которое понадобилось слейву для ответа, и всю информацию об ошибке от библиотеки libmodbus.
3. Одновременно с этим надо смотреть, что происходит в линии связи с помощью модбас сниффера или обычного сниффера последовательного порта. Какие байты ушли от вашего мастера, какие байты пришли от слейва, почему получаем ошибку...

По результатам можно будет ввести коррективы в код. Ну и будет информация на случай, если имеются косяки на стороне слейва (с ней можно обратиться к производителю за советом или патчем). Также, узнав точное время ответа вашего слейва, вы сможете определить, сколько времени нужно на опрос всех ваших устройств.
plastictown писал(а): 23 апр 2018, 13:56 Тем не менее, картина остается ужасной: если на линии. скажем 120 девайсов, опрос займет не менее двух минут, а это никуда не годится.
Ни разу не видел на практике, чтобы 120 устройств опрашивались по одной шине RS-485. Обычно стараются ограничить количество устройств на одной шине парой десятков. Если нужно больше слейвов, следует закладывать больше последовательных портов и вести опрос параллельно.
Аватара пользователя

ipaSoft
здесь недавно
здесь недавно
Сообщения: 5
Зарегистрирован: 13 апр 2018, 10:02
Имя: Иванов Павел Александрович
Страна: Казахстан
город/регион: Актау
Поблагодарили: 1 раз

Странности libmodbus

Сообщение ipaSoft »

Добрый день. Можно попробовать подключить Modbus poll (или другой заведомо исправный софт) к шине и попеременно переключать slave_id прямо в процессе. Он покажет потерянные пакеты, время ответа. Так можно понять, на какой стороне проблема: в харде шины или в ПО мастера. Если потерь не будет, значит проблема в разрабатываемой программе и/или библиотеке.

Автор темы
plastictown
здесь недавно
здесь недавно
Сообщения: 5
Зарегистрирован: 19 апр 2018, 13:20
Имя: Михаил
Страна: Россия
город/регион: СПб
Благодарил (а): 2 раза

Странности libmodbus

Сообщение plastictown »

ipaSoft писал(а): 24 апр 2018, 05:47 Добрый день. Можно попробовать подключить Modbus poll (или другой заведомо исправный софт) к шине и попеременно переключать slave_id прямо в процессе. Он покажет потерянные пакеты, время ответа. Так можно понять, на какой стороне проблема: в харде шины или в ПО мастера. Если потерь не будет, значит проблема в разрабатываемой программе и/или библиотеке.
modbus_poll выдал те же результаты поначалу. Я начал копать в сторону настроек железяки и обнаружил там некую настройку "request timeout", выставленную в 1000мс. После уменьшения это значения до 100 мс modbus_poll перестал запарывать пакеты после переключения слейвов, кроме первого раза, однако моя программа ведет себя все так же и обрабатывает 100% пакетов только при таймауте в 1000 мс и плюс к этому я добавил задержку в 1000мс после отправки пакета. Это, конечно, слишком медленно. Отсюда вывод: есть что-то такое в modbus_poll, чего нет у меня, отчего она работает лучше. Буду выяснять.
Ответить

Вернуться в «Интерфейсы, протоколы, связь»