Введение
Если в качестве инструмента у Вас имеется лишь молоток, каждая проблема начинает напоминать гвоздь.
Абрахам Маслоу
Протокол Modbus широко хорошо известен как читателям хабра, так и читателям гиктаймс. Его применению посвящено множество публикаций, перечислять которые трудно из-за того что их очень много, и периодически то там, то тут появляются новые статьи.
Популярность данного протокола обусловлена его открытостью и простотой. Сфера применимости достаточно широка: от профессиональных промышленных систем автоматизации до любительских DIY-проектов распределенных управляющих систем, «умных» домов и так далее. Данный протокол был выбран и мной, когда моя команда занималась создание ПО тренажера электропоезда. Протокол Modbus RTU на физическом интерфейсе RS485 используется на данном тренажере для обеспечения ввода в управляющий компьютер данных с органов управления, смонтированных на пульте машиниста (не стоит думать что Modbus используется на настоящем подвижном составе!).
Не стоит говорить с какими трудностями сопряжена наладка ПО, взаимодействующего с сетью контроллеров, управляющих оборудованием. Особенно когда часть устройств уже существует в железе, а другая часть находится в процессе разработки и изготовления. При этом ПО верхнего уровня требуется писать с учетом его взаимодействия с эти железом. И желательно писать его так, чтобы создавать рабочий вариант системы сразу, без использования «костылей» которые всегда трудно вычищать из кода.
«Надо писать ПО, когда готовы рабочие прототипы всего железа» — скажете вы и будете правы, но… ха-ха-ха, в реальном мире такое случается редко. И вот тут нам на помощь приходят программные эмуляторы.
1. Кратко о Modbus RTU
Подробно рассказывать о протоколе не буду. Те, кого интересуют подробности могут воспользоваться поиском — протокол открыт, в сети доступна его официальная спецификация и масса информации. Скажу лишь, что в Modbus RTU описывает двоичный формат передаваемых данных и в качестве среды передачи использует дифференциальную витую пару стандарта RS485. Может использоваться и RS232, если в сети один передатчик и один приемник, или RS422 для однонаправленной передачи данных.
Нас будет интересовать именно RS485, который является полудуплексным интерфейсом, что допускает лишь одно передающее данные устройство в каждый момент времени. Арбитраж шины в Modbus осуществляется за счет выдержки обязательного интервала тишины длиной 3,5 символа при данной скорости передачи. Каждое сообщение должно начинаться и завершаться интервалом тишины. В сети существует одно ведущее устройство (master) и несколько ведомых устройств (slave) (до 31 в одном сегменте сети, без применения репитеров). Каждое ведомое устройство имеет уникальный идентификатор ID (от 1 до 31). Передача данных ведомым осуществляется лишь в том случае, если мастер послал запрос на получение данных с этого устройства.
Типичный запрос мастера выглядит так
ID | Код функции | Адрес данных | Количество данных (2 байта) | Данные | CRC16 |
CRC16 используется для контроля целостности передаваемых данных. Modus использует Big Endian нотацию представления данных: для значений размером 2 байта старший байт внутри сообщения идет первым). В протоколе используется четыре типа данных:
- Coils — дискретные выходы (1 бит) доступные для чтения/записи
- Discrete inputs — дискретные входы (1 бит) доступные для чтения
- Holding registers — регистры вывода (2 байта) доступные для чтения/записи
- Input registers — регистры ввода (2 байта) доступные для чтения
В ответ на запрос ведомое устройство отдает данные в следующем формате
ID | Код функции | Количество данных в байтах | Данные | CRC16 |
Сообщение, принятое от мастера попадает в приемный буфер всех устройств. Однако, если первый байт приемного буфера не совпадает с ID устройства, оно игнорирует принятые данные, очищая приемный буфер. Если сообщение предназначено данному устройству, то оно формирует ответ и, выдержав интервал тишины посылает его мастеру.
Как говорится, простенько, но со вкусом. Подробнее обо всем этом можно прочитать в официальной спецификации протокола. О методах реализации протокола на последовательном интерфейсе читаем здесь. Собрались мы здесь не за этим.
Разрабатывая ПО верхнего уровня (master реализуемый на базе ПК, например) для подобной сети, хорошо бы иметь набор программных средств, позволяющих реализовать такую концепцию
Смысл этой схемы в следующем. Допустим, у нас есть часть устройств, входящих в будущую сеть. Или пока нет ни одного такого устройства. Но есть горячее желание написать ПО для верхнего уровня управления, отладить его, с тем чтобы когда сеть будет таки реализована аппаратно нам не пришлось ничего переписывать. Для этого придется использовать физическую среду передачи, для чего используем девайс, подобный этому
Один из адаптеров используется для подключения ПО разрабатываемого мастера. Другой — для подключения эмулятора будущей сети слейвов. К отводу с белым коннектором подключаем ту часть сети, которая уже реализована аппаратно. Таким образом мы получаем возможность спокойно работать со штатным протоколом связи, постепенно вводя в работу реальную аппаратуру. К тому же, отдав объект заказчику мы не лишаемся возможность модифицировать его ПО в комфортной обстановке лаборатории без доступа к объекту. QSlave на схеме как раз таки часть сети, эмулируемая программно. Естественно, придется написать соответствующий софт, что и было сделано автором.
2. QSlave — эмуляция сети ведомых
QSlave — открытый кроссплатформенный эмулятор сети Modbus RTU. Получить его можно по лицензии GPL v2.0 на Github по вышеприведенной ссылке.
Приложение разработано на C++ с использованием фреймворка Qt. Qt, вообще говоря, имеет библиотеки для работы с Modbus, но специфика задачи — имитация сети слейвов а не одного слейва, привела к тому, что встроенные библиотеки Qt для Modbus тут не использовались. Для обработки данных, принимаемых с виртуального последовательного порта была создана самописная библиотека modbus. Код этой библиотеки реализован в виде отдельного проекта, совершенно не зависит от интерфейса пользователя и может быть использован для сознания программных имитаций с более продвинутым функционалом. Из-за того, что эмуляция Modbus отвязана от UI, конфигурирование сети происходит с применением конфигурационных файлов. Был выбран формат XML (мы часто его используем в своих проектах). Пример конфигурации доступен в коде проекта. Комплект конфигов состоит из главного файла с расширением *.net, который выглядит так
example.net
<?xml version="1.0" encoding="UTF-8" ?>
<Config>
<Slave>
<!-- Описание слейва, отображаемое в списке устройств -->
<Description>Traffic light</Description>
<!-- Идентификатор слейва -->
<id>1</id>
<!-- Имя XML-файла конфигурации (без расширения *.xml) -->
<config>traffic-light</config>
</Slave>
</Config>
и XML-файлов конфигурации для каждого из слейвов
traffic-light.xml
<?xml version="1.0" encoding="UTF-8" ?>
<Config>
<!-- Дискретные выходы -->
<Coil>
<address>16</address>
<description>Red signal</description>
<value>0</value>
</Coil>
<Coil>
<address>17</address>
<description>Yellow signal</description>
<value>0</value>
</Coil>
<Coil>
<address>18</address>
<description>Green signal</description>
<value>0</value>
</Coil>
<!-- Дискретные входы -->
<DiscreteInput>
<address>0</address>
<description>Ready</description>
<value>1</value>
</DiscreteInput>
<!-- Регистры вывода -->
<HoldingRegister>
<address>5</address>
<description>Signal activity time</description>
<value>15</value>
</HoldingRegister>
<!-- Регистры ввода -->
<InputRegister>
<address>2</address>
<description>Signals count</description>
<value>3</value>
</InputRegister>
</Config>
Последний файл содержит описание всех данных, доступных в устройстве. Для того, чтобы загрузить конфигурацию необходимо открыть файл *.net из меню программы QSlave (File → Open config). Все файлы конфигурации должны лежать в одном каталоге. Конфигурация примера описывает сеть из одного ведомого устройства, некий виртуальный дорожный светофор, дискретные выходы которого описывают сигналы, дискретный вход обозначает некий бит готовности устройства к работе (Ready), регистр ввода сообщает число сигналов светофора, а регистр вывода задает время, в течение которого горит каждый из сигналов.
Естественно, данный симулятор никак не имитирует внутреннюю логику работы устройства. Он позволяет лишь задавать значения ячеек памяти, доступных ведущему устройству. Любое из значений можно задать по своему усмотрению, отредактировав соответствующую ячейку таблицы.
При всей своей простоте, данный софт помогает нам работать над ПО тренажера (который уже сдан в эксплуатацию) не выходя из лаборатории.
Но никто не говорит, что нельзя создать более продвинутый эмулятор, имитирующий работу устройств виртуальной сети. Для его создания можно использовать код библиотеки modbus, доступный в комплекте поставки QSlave.
3. QMaster — эмуляция мастер-устройства
Для создания ведомых устройств, отладки их прошивок нужна имитация мастера. Существует ряд открытых эмуляторов, таких как например QModbus. Мы использовали его в своей работе, до тех пор, пока не решили увеличить скорость передачи данных до 250 кБит/с. QModbus этого не позволяет. Его удалось пересобрать из исходников под Linux, но наши электронщики сидят на Windows, а где сборка не пошла по ряду причин. Выяснилось, что это приложение написано на Qt 4, использует сторонную библиотеку libmodbus. Хотелось иметь кроссплатформенное решение на Qt5, тем более что Qt5 уже работает с Modbus «из коробки». Поэтому был написан свой аналог, использующий стек библиотек Qt Modbus - QMaster. Он тоже доступен на Github на тех же условиях.
Заключение
В заключении скажу, что работаю (на работе) в основном над закрытыми проектами. Однако, описанные инструменты разработаны лично мной в инициативном порядке в свободное время. К тому же они, в Windows-версии, статически линкованы с GPL-кодом Qt, поэтому я обязан передать их сообществу на тех же условиях, что и получил Qt. К тому же, эти инструменты могут быть полезны для читателя.
Благодарю за внимание!