Как стать автором
Обновить

Комментарии 10

Желающим использовать Modbus могу подсказать две «неприятности», с которыми столкнулся на опыте.
Первая — отсутствие преамбулы. Точнее она есть, это задержка в 3.5 символа. Если же вы пытаетесь прочитать дамп Modbus/RTU данных из файла, или получаете их из ненадёжного по задержкам канала, или используете какие-то преобразователи типа 485 over TCP/IP, то эти 3.5 вы не найдёте. Или найдёте посередине половины пакетов. В итоге приходится как-то умно детектить границы пакетов по номеру устройства и CRC, что неприятно.
Вторая — отсутствие стандарта на Modbus\UDP, который на самом деле существует, но не стандартизирован.

А в остальном — хороший удобный протокол.
Есть Modbus ASCII, там все есть
Ну, это какая-то другая крайность.
Что за задержка? Не было такого. Не надо ничего «детектить»: там приходит ID, N функции, потом количество байт, что нужно прочитать, потом 2 байта CRC. Чтобы всё корректно работало и не обрывалось, нужно правильно настроить флаги COM порта: долго с этим мучился, пакеты обрывались не доходили, пока случайно не определил какие флаги выставлял сторонний клиент, который работал корректно. Вот пример для Linux-а, если кому надо, обратите внимание на флаг Ignore Break Condition — в нём проблема:

tcgetattr(fserial, &options);		// Получить текущие настройки порта
    cfsetispeed(&options, BaudRate);	// Установить скорость входящего потока
    cfsetospeed(&options, BaudRate);	// Установить скорость исходящего потока
    if(ParityOdd)
        options.c_cflag |= PARENB | PARODD; // Включить проверку четности (неч)
    else if(ParityEven)
        options.c_cflag |= PARENB & ~PARODD;// Включить проверку четности (чет)
    options.c_cflag &= ~CSTOPB;             // 1 стоп бит
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8 | CREAD | CLOCAL; // 8 бит, включить приемник
    if(bCTS)
        options.c_cflag |= CRTSCTS;     // включить аппаратный контроль потока
    else
        options.c_cflag &= ~CRTSCTS;    // отключить аппаратный контроль потока

    // Вместо ниже приведенных установок можно использовать
    // int cfmakeraw(struct termios *termios_p) для настройки необрабатываемого
    // драйвером последовательного порта ввода/вывода

    // Raw Input - ввод не обрабатывается драйвером порта
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    // Raw Output
    options.c_oflag &= ~OPOST;
    options.c_iflag = IGNBRK;   // Ignore Break Condition (без этого работало
                                // некорректно: CRC/Timeout errors)

    // Настройка таймаутов порта
    options.c_cc[VMIN] = 0;  // Минимальное количество символов
    options.c_cc[VTIME]= Timeout/100; // Таймаут ожидания каждого
                                      // символа *10 сек
    tcsetattr(fserial, TCSANOW, &options); // Записать новые настройки
                                           // (немедленно)
    if(MDebug > 1){
        cout << "Serial port options:" << endl;
        cout << "c_cflag: " << options.c_cflag << endl;
        cout << "c_lflag: " << options.c_lflag << endl;
        cout << "c_iflag: " << options.c_iflag << endl;
        cout << "c_oflag: " << options.c_oflag << endl;
        cout << "c_cc: " << options.c_cc << endl;
        cout << "c_ispeed: " << options.c_ispeed << endl;
        cout << "c_ospeed: " << options.c_ospeed << endl;
    }

Что за задержка? Не было такого.

https://www.modbus.org/docs/Modbus_over_serial_line_V1_02.pdf раздел 2.5.1.1

Детектить по ID и структуре пакета легко, когда у вас известен ID и надёжный канал связи, в котором не теряются и не искажаются куски пакетов. Хотя в таком канале и на задержки не стоило бы надеяться.

я же говорю, у меня эти задержки были пока не выставил правильный флаг на порте - потом они исчезли совсем и всё стало стабильно работать! Не нужно было проверять эти задержки.

RTU варианте поверх работы с Serial портом, или 485 интерфейса насколько это встречалось в моей практике.

RS485 — тоже serial интерфейс, противопоставление некорректно. Modbus RTU (и ASCII, который вы не упомянули) может работать по произвольному последовательному интерфейсу (RS232, RS422, RS485, UART, как примеры).


Сама же статья непонятно о чём. Вы научились писать/читать из serial интерфейса и считать crc16 с помощью внешней функции? Хорошо.


Вы ничего не написали про структуру фрейма, коды операций, структуру адресного пространства устройства (отличия discrete input, coil, input register, holding register, независимость адресации), два варианта адресации в клиентских библиотеках (десятичная запись адреса часто смещена на 1) и кучу других базовых аспектов.

«Краткость- сестра...» У вас, похоже, она родная…
Читайте спецификации на официальном сайте протокола.
Тогда зачем вообще этот пост? Показать что Вы наконец-то разобрались с довольно простым протоколом? Хотя бы ссылку на «официальный сайт протокола» привели бы что-ли.
Походу реальная цель этого опуса — засветить имя юзера (которое адрес сайта). Другой мысли нет.
Полностью поддерживаю, предыдущий комментарий, какая то бессмысленная статья получается,
непонятно какие цели преследует автор.
Абсолютно вырванные из контекста куски кода
crc.i = CRC16((unsigned char *)request, reqsize-2);
request[reqsize-2] = crc.ch[1];
request[reqsize-1] = crc.ch[0];


что за crc.i — что это за структура?
что за request откуда взялся, как объявлен?
какие еще функции в MB протоколе существуют?
какие есть коды ошибок и как их обрабатывать?
какая особенность расчета контрольной суммы именно для MB протокола?
отсылка к оф. докам, это не серьезно

После прочтения статьи образуется еще больше вопросов и это не следствие осознание проблемы.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории