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

Для протокола существуют открытые спецификации на официальном сайте, где подробно разбирается Modbus по составляющим, правда на английском. Modbus нужен, как правило, чтобы читать регистры с устройств, или записывать в них данные, даже по 1 биту, и еще ряд дополнительных свойств. Это может обеспечить взаимодействие программы и устройства, чтобы считать измеренные устройством данные (заряд батареи, показания вольтметра, температуры) и произвести настройку устройства.

Кратко покажу что такое modbus rtu:

unsigned char* response;
response = new unsigned char[8];
ReadFile(hSer, (char*)response, 8, &size, 0);

Так можно прочитать приходящие на сервер запросы modbus rtu. Структура 8 байт. FF это байт.
Может прийти сообщение вида: 02 03 A0 28 00 04 93 2A

Расшифрую сообщение. 02 — это номер устройства, к которому идет обращение. 03 — номер функции, т.е. 3 — читать регистры, а бывает еще записывать, регистры, коилы по 1 биту и т.д.

A0 28 если перевести в десятичную систему будет 41000 — номер регистра, 00 04 — значит еще 4 регистра нужно считать от 41000. Последние 2 числа — 2 байта кода CRC16.

Раз такое сообщение пришло, соответственно так же нужно заполнить массив на стороне отправки, и переправить либо через tcp сокеты, либо через com порт, или другим способом.

Ответ так же заполняется, но там будет уже сообщение вида: 02 03 08 00 01 00 02 00 03 00 04 95 В8 т.е. код устройства, код функции, количество передаваемых байт, 1234 данных (это как бы значения регистров). 2 байта код CRC.

Код CRC рассчитывается от сообщения не включая 2 байта кода CRC. Это для функции 3. В коде:

unsigned char* request
request = new unsigned char[reqsize];

Заполнить данными и подсчитать CRC:

crc.i = CRC16((unsigned char *)request, reqsize-2);
request[reqsize-2] = crc.ch[1];
request[reqsize-1] = crc.ch[0];

И отправить по Serial порту:

BOOL iRet = WriteFile(hSer, (char*)request, reqsize, &dwBytesWritten, NULL );

Таким образом отправили и ответили на функцию 3 (чтение нескольких регистров) по протоколу Modbus.

Спасибо за внимание! Читайте спецификации на официальном сайте протокола.
Поделиться с друзьями
-->

Комментарии (7)


  1. ProstoTyoma
    30.12.2016 18:03
    +1

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

    А в остальном — хороший удобный протокол.


    1. A1ien
      30.12.2016 22:52
      +1

      Есть Modbus ASCII, там все есть


      1. ProstoTyoma
        02.01.2017 01:27

        Ну, это какая-то другая крайность.


  1. grossws
    30.12.2016 18:04
    +1

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

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


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


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


  1. GreyPhantom
    30.12.2016 19:14
    +6

    «Краткость- сестра...» У вас, похоже, она родная…

    Читайте спецификации на официальном сайте протокола.
    Тогда зачем вообще этот пост? Показать что Вы наконец-то разобрались с довольно простым протоколом? Хотя бы ссылку на «официальный сайт протокола» привели бы что-ли.


  1. timka05
    30.12.2016 22:09
    +4

    Походу реальная цель этого опуса — засветить имя юзера (которое адрес сайта). Другой мысли нет.


  1. sozercanie_kosmosa
    02.01.2017 11:16

    Полностью поддерживаю, предыдущий комментарий, какая то бессмысленная статья получается,
    непонятно какие цели преследует автор.
    Абсолютно вырванные из контекста куски кода

    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 протокола?
    отсылка к оф. докам, это не серьезно

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