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

Сервер Modbus TCP для Simatic S7-1200 / S7-1500

Время на прочтение6 мин
Количество просмотров21K

Первая спецификация протокола Modbus была опубликована в 1979 году. Протокол предназначен для опроса подчиненных устройств по принципу «запрос-ответ». Modbus RTU (Remote Terminal Unit) работает по последовательному интерфейсу передачи данных (RS-232, RS-485, RS-422). Сегодня речь пойдет о немного измененном протоколе, Modbus TCP, работающий на прикладном уровне стека протоколов TCP/IP.

Для начала посмотрим, как настраивается (программируется, если быть точнее) серверная часть. Modbus TCP Server — аналог Modbus RTU Slave, то есть, является подчиненным устройством. Это важно, не путайте. Сервер лишь отвечает на запросы, но не генерирует их.

В данном примере применяется CPU S7-1516 с версией прошивки 2.6. Серия S7-1200 программируется аналогично.

Для начала разместим в OB1 экземпляр функционального блока MB_SERVER (Instructions → Communications → Others → MODBUS TCP).

Далее необходимо сделать три вещи. Во-первых, подать что-нибудь на вход MB_HOLD_REG. Этот входной пин экземпляра ФБ должен содержать область памяти, которая выделяется на регистры хранения (holding registers).

Небольшое отступление. В версиях библиотек Modbus TCP до 5.0 переменные «дискретные входы» (Discrete inputs), т.е. те двоичные переменные, которые можно только читать — это непосредственно все BOOL'евые переменные из области процесса %I. Coils, «катушки» — дискретные переменные, которые можно и читать, и записывать, это область %Q. Input Registers, «входные регистры» — это слова данных из области %I, точнее %IW. Проще говоря, все дискретные переменные протокола Modbus и аналоговые входа являются переменными областей памяти I или Q. Это дает возможность читать непосредственно значение входов, а так же записывать значения дискретных выходов напрямую. С моей точки зрения нелогично отдавать возможность управлять дискретными выходами какой-нибудь сторонней системе, пусть даже и теоретическую, поскольку в зависимости от построения прикладного ПО контроллера, на эти выхода будет приходить «правильное» с точки зрения системы значение. Для того, чтобы ограничить клиентам Modbus TCP возможность прямого обращения к выходам, в экземпляре функционального блока есть несколько переменных.

Нас интересуют все переменные, которые начинаются на IB и QB. Указав в качестве значений QB_Count, QB_Read_Count и IB_Count нули, вместо значения по умолчанию 65535, мы запрещаем полностью чтение/запись входов/выходов напрямую.

Для чтения/записи регистров хранения, в свою очередь, необходимо отдельно вручную задать область данных. Мой личный опыт показывает, что наиболее удобный способ — это структура, объявленная в глобальном блоке данных со стандартным (а не оптимизированным) доступом. Я сейчас продемонстрирую, «как надо», а под конец данной заметки мы «пройдемся по граблям» и посмотрим типичные ошибки, которые возникнут, если заполнить данное поле неправильно.

В версии библиотеки, начиная с 5.0 (требуется прошивка 2.5 для S7-1500 и 4.2 для S7-1200) можно иначе переназначать входные дискреты, катушки и прочие переменные модбас. Например — завести все в битовые переменные глобального блока данных. Необходимо дополнительная конфигурация, которая описана в пункте «Access to data areas in DBs instead of direct access to MODBUS addresses as of version V5.0» встроенной справки.

Итак, добавляем глобальный блок данных с «неоптимизированным» доступом в картинках.

Нажать Add new block

Выбрать «Data block» и дать ему осмысленное имя, далее нажать ОК

Вызвать свойства свежедобавленного блока данных

Найти в атрибутах блока галочку «Оптимизированный доступ», снять ее, подтвердить снятие и нажать ОК

Открыть в редакторе блок данных и создать в нем отдельную структуру

Заполнить поля этой структуры. Имеет смысл сразу в комментариях делать пометки о номере(адресе) регистра хранения. Откомпилировать блок данных.

Подать созданную структуру на входной пин MB_HOLD_REG

Во-вторых, требуется создать и заполнить структуру типа TCON_IP_v4 или TCON_Configured. Данная структура содержит некоторые подробности для коммуникации контроллера. Лично я предпочитаю первый способ, он мне кажется более аскетичным, а кроме того — он не требует загрузки Hardware, в отличии от второго. В связи с тем, что структура относится к «настроечной» части протокола Modbus, ее можно разместить в уже созданном блоке данных (хотя, никто не запрещает объявить ее, где угодно).

Добавление структуры типа TCON_IP_v4

Поле InterfaceID заполним чуть позже, а сейчас пройдемся по остальным полям.

ID — внутренний идентификатор соединения. Допустимые значения от 1 до 4096. Каждое соединение (экземпляр блока MBSERVER, хотя на самом деле все немного сложнее). должно иметь свой уникальный идентификатор. Ставлю равным 1.

ConnectionType — тип соединения. По умолчанию стоит 11 (0B в шестнадцатеричной системе): TCP. Его и оставляем.

ActiveEstablished — оставляем false, в данном случае сервер не является инициатором связи, инициатором связи являются клиенты.

RemoteAddress — если оставить нули, то к серверу сможет подключиться любой клиент. Если задать удаленный IP-адрес конкретно, то к серверу может обратиться только один явно заданный клиент. Оставляем нули.

RemotePort — оставляю ноль, не ограничиваю и номер порта со стороны клиента

LocalPort — номер TCP порта, по которому будет отвечать сервер. В соответствии со старой-доброй традицией (и RFC) протокол Modbus TCP работает на порту 502 (а игра Doom — по порту 666, но это совсем другая история). Порт 502 я указываю явно.

В итоге получаем следующее:

Осталось задать лишь ID интерфейса. Это присвоение я делаю в программное коде, разместив network с присвоением (MOVE) до вызова блока Modbus. Идентификаторы интерфейсов уже созданы в Step 7 автоматически, необходимо лишь найти нужную переменную. В моем случае Modbus будет работать на интерфейсе X1. Его я и нахожу в списке переменных, выпадающем автоматически.

Можно так же подсмотреть название нужной переменной, зайдя в аппаратную конфигурацию, выбрав нужный интерфейс и вкладку System Constants этого интерфейса. Привожу скриншот всего экрана, чтобы было понятно, где искать.

Можно так же просто указать значение 64 для переменной "ModbusData".CONNECT_Struct.InterfaceId

Далее подаем на вход CONNECT заполненную структуру и получаем следующую программу:

И, наконец, в третьих, не забываем про переменные экземпляра MB_SERVER_DB. Я о них говорил выше, описание этих переменных можно почитать во встроенной справке. Если их не трогать, то обмен, все равно, будет работать, это уже вопрос «доводки» обмена и доступа ко «входам» и «выходам».

Компилируем программу, загружаем ее в контроллер и выходим Online:

Статус 7002 не означает ошибку, он говорит о том, что соединение устанавливается. Обязательно почитайте описание возможных значений поля STATUS, пригодится. Перед тем, как начать читать/записывать данные при помощи стороннего Modbus-клиента, дадим переменным ненулевые значения (разумеется, мои любимые — «число зверя» и «три топора»).

В качестве Modbus-клиента можно использовать любой проверенный софт. Главное — правильно сформировать запрос со стороны клиента. В нашем случае объявлено всего 5 регистров хранения, и если запросить 10, то сервер Modbus вернет ошибку, и будет прав. Второй немаловажный момент — не забывайте про порядок байт в слове: если little endian отображать, как big endian, или наоборот, то вместо разумных чисел на экране будет ерунда. На данном скриншоте клиент настроен на опрос 5 регистров хранения, представление данных, как float, настроено «переворачивание» байт в словах:

Чуть выше я говорил, что дискретные выхода контроллера (точнее, биты области %Q) — это и есть «койлы» с точки зрения протокола Modbus, и что при настройках по умолчанию клиент получит возможность как читать сигналы напрямую, так и записывать их. Давайте в этом убедимся. Для начала на модуле дискретных выходов я объявляю переменную, для дальнейшего удобства:

Нулевой бит восьмого байта выходной области. Номер 64, если считать с нуля (8 * 8 + 0 = 64). Задам в контроллере значение «истина» и прочитаю в Modbus-клиенте:

Вижу значение «истина» (читаю один койл с начальным смещением 64). Изменю это значение на «ложь» со стороны modbus:

Значение, разумеется, так же изменилось и в Step 7, и в контроллере, и на выходе модуля (это одно и то же):

Как я уже говорил, нехорошо давать прямой доступ к управлению всяким сторонним системам. Для формирования выходных сигналов есть внутренняя логика программы, продуманные и прописанные алгоритмы управления, защит и блокировок. Поэтому снизим до нуля количество доступных к записи «койлов» (байт выходного пространства, если точнее):

После этого изменения в блоке данных (прямо в online, без перезаливки и перезагрузки контроллера) клиент протокола modbus в ответ на требование записи «катушки» показал табличку «Illegal data address», а именно такое сообщение и вернул сервер. Дополнительную информацию читаем в справке: Restriction of read access to process images as of version V5.0.

Теперь давайте посмотрим, что происходит при некорректном назначении области памяти от регистров хранения. В первую очередь достаем встроенную справку Step 7 и читаем:

В качестве регистров хранения применять глобальный блок данных с оптимизированным доступом или битовую область. И вот тут очень интересно. Потому что справка в части «MBHOLDREG parameter» выглядит следующим образом:

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

Эксперимент №1. Регистры хранения — это структура в блоке данных с оптимизированным доступом (почти, как сделано в этом примере). В этом случае получаем ошибку 8187 : The MBHOLD_REG parameter has an invalid pointer. Data area is too small.

Эксперимент №2. Массив переменных типа WORD, объявленный в «оптимизированном» блоке данных. Работает, со стороны клиента переменные меняются, ошибок нет.

Эксперимент №3. Меркерная область. Работает, с клиента удалось внести значения, ошибок нет.

С моей точки зрения, в документации недостаточно ясно. Должно быть написано «используйте блок данных со стандартным доступом или битовую (меркерную) память», а не «оптимизированным доступом». В случае оптимизированного доступа вполне подойдут массивы слов. И с моей точки зрения самым удобным способом является способ, описанный в изначальном примере. Эксперимент №2 в принципе тоже работоспособен (и тому есть объяснение), но с моей точки зрения неудобен для работы.

В следующий раз мы займемся клиентом Modbus TCP.

Теги:
Хабы:
+3
Комментарии19

Публикации