Конвертер MODBUS-TCP/IP MODBUS RTU

Попробуем сделать сегодня что-нибудь полезное. Думаю, по заголовку многие уже поняли, что именно. А если нет - напомню про существование такого интерфейса под названием RS/TIA/EIA-485. Несмотря на возраст, до сих пор ставится в массу датчиков, приборов и прочего оборудования. И, судя по всему, ставиться будет ещё долго. В качестве протокола верхнего уровня здесь чаще всего используют MODBUS-RTU, несмотря на его недостатки и ограничения. Более подробно об этом можно почитать, например, здесь. В попытке осовременить этого динозавра протокол была придумана его модификация MODBUS TCP/IP, в которой, как не сложно догадаться, типовые MODBUS-пакеты пропихиваются по TCP/IP сетям. Это позволило скрестить ежа с ужом подключить к современным сетям всю эту гору оборудования, не переделывая её начинку. И организовать доступ к данным хоть на край света по удалёнке через Internet! Достаточно взять специальную коробку-конвертер и немного модифицировать софт верхнего уровня.
Что же это за коробка такая? С одной стороны у неё ethernet, wi-fi или даже какой-нибудь 5G модем. А с другой - старые добрые порты RS-232, RS-422, RS-485.
b0ac5088a5d511e3908100155d0c1500_0d6c9d1af6cc11e68dc000155d0c1500.jpg
usr-tcp232-410s-serial-to-ethernet-converter-2c-support-modbus-rtu-and-tcp-09-500x500.png
S4cb49fd9902f495c9a2344a154c3295eS.jpg_480x480.jpg

Путём нехитрых преобразований коробка перебрасывает MODBUS-пакеты туда-обратно. Делают их сейчас все, кому не лень! Начиная от брендов, вроде MOXA или ICP-DAS до неведомых кетайцев вроде HI-FLYING, USR IOT, тысячи их, разной степени кривизны качества. Внутри такой коробки, как правило, прячется типичная SoC для интернетовских точек доступа, несколько драйверов интерфейса, да какая-нибудь обвязка по питанию.
1-8-Port-Industrial-Modbus-gateway-server-Modbus-TCP-to-MODBUS-RTU-ASCII-with-RS485-Ethernet.jpg
8681916208402

А раз так, быть может, и на основе неспециализированного компьютера можно поднять такой конвертер? Например, на...Репке?
И да, это возможно! Благодаря проекту MBUSD от Юрия Антоновича. 🧑‍🏫 Как несложно понять, это сервер под линуксовые (главным образом) машины. Несмотря на то, что проект, вроде как, ещё не выбрался из альфа-стадии, он вполне стабильный и универсальный. Достаточно, чтобы комп имел на борту необходимые порты (либо дооснастить его ими).

И универсальность его заключается, в том числе, вот в каком моменте:
Напомним, что RS-485 это полудуплексный интерфейс. В каждый момент времени он может либо принимать, либо передавать. И для чипов драйверов важно осуществлять своевременную коммутацию режима работы! Как же эту проблему решить?
Готовый вариант - взять кетайский переходник USB-RS485, вроде такого
O1CN019HRZmN1QGED2RKt3q_!!90831948.jpg_Q75.jpg_.webp
В нём схема коммутации уже встроена, той или иной степени надёжности. Пользователю в линуксах, после того, как её подхватят драйвера, достаточно использовать появившийся порт вроде /dev/ttyUSB0
Другой вариант - аппаратная схема автоматической коммутации. Бывают готовые специальные чипы драйверы, которые сами выбирают, в каком режиме им работать (например, MAX13487), бывают отдельные решения (например, вот эта схема хорошо работает).
Но MBUSD в состоянии и сам выполнять коммутацию приём-передача, что в теории, наверно, должно работать стабильнее и более предсказуемо. Для чего автор заложил в него три метода:
В первом случае он может использовать возможности линукс-драйвера под какие-то аппаратные реализации RS-485 для встраиваемых систем, наверно...в жизни такие мне не попадались.
Может он для коммутации задействовать вывод RTS, который присутствует в стандартном RS-232. Либо он может быть выведен в миникомпьютерах на пины для подключения шилд. Но есть ли у Репы такой выход? Смотрим на схему и убеждаемся, что ни в одной из "распиновок" его нет.:rolleyes:
Но не беда, есть ведь ещё и третий метод: MBUSD умеет дёргать GPIO-выходы через линксовую SysFS. В интернетах много бурлений о том, что, мол, SysFS - это жуткое легаси, хватит на нём сидеть, нормальные проггеры пользуют UAPI. Что тут скажешь, MODBUS сам по себе ещё то замшелое легаси.🥴 Для нас главное, что "Repka-OS" его поддерживает нормально. А также то, что на гребёнку выведены сигналы UART, хотя бы один GPIO и питание.
Именно по этому пути я и решил пойти, сделав свою "шилду". Здесь можно посмотреть схему данной поделки. Делал на том, что было под рукой, наверняка вы возьмёте другую элементную базу. По крайней мере, имеется гальваническая развязка и базовая защита выхода от статики. Подключаем плату к верхней части гребёнки (контакты 1...10) через шлейф. Разъём XP2 я сделал для себя, чтобы подключать ранее сделанную "шилду" с часами DS3232. XP3 и XP4 выводят питание, на всякий случай (например, к XP3 я подключаю корпусной вентилятор). Вот как это всё сейча выглядит:
P_20241103_193545.jpg
Да, надо будет хоть какую-нибудь коробочку под плату пристроить...
А теперь установим MBUSD. В репозитарии его похоже что нет, так что компиляем из исходников:
устанавливаем CMAKE, если у вас его вдруг ещё нет
Bash:
sudo apt-get install cmake
Дальше просто следуйте Installation instructions с Гитхаб. Установим, но запускать пока не будем.
Всё скомпилировалось быстро и в целом без ошибок. На всякий случай уточню, что у меня сейчас стоит ядро 5.19.16.
Если будете задействовать UART0, как у меня, то следует также отключить вывод отладочных сообщений в него: здесь смотрим пункт Отключение / включение отладочного терминала на UART0.
Настроим конфиг-файл. Для этого я в каталоге /etc/mbusd беру файл mbusd.conf.example и копирую его с именем mbusd-ttyS0.conf Как несложно заметить, его имя содержит название UART-а, который я собираюсь задействовать для своего переходника. Редактирую его следующим образом:
Bash:
#############################################
#                                           #
#    Sample configuration file for mbusd    #
#                                           #
#############################################

########## Logging settings #############

# Logging verbosity level
loglevel = 1

# Logfile (fully-qualified path, or filename [stored at /var/log/] or - for STDOUT only)
logfile = /var/log/mbusd.log

########## Serial port settings #############

# Serial port device name
device = /dev/ttyS0

# Serial port speed
speed = 19200

# Serial port mode
mode = 8n1

# Enable RS-485 support for given serial port device (Linux only)
enable_rs485 = no

# RS-485 data direction control type (addc, rts_0, rts/rts_1, sysfs_0, sysfs_1)
trx_control = sysfs_1

# Sysfs file to use to control data direction
trx_sysfile = /sys/class/gpio/gpio7/value

############# TCP port settings #############

# TCP server address to bind
address = 0.0.0.0

# TCP server port number
port = 502

# Maximum number of simultaneous TCP connections
maxconn = 32

# Connection timeout value in seconds
timeout = 60

######### Request/response settings #########

# Maximum number of request retries
retries = 3

# Pause between requests in milliseconds
pause = 20

# Response wait time in milliseconds
wait = 200

# Reply on Broadcast
replyonbroadcast = no

Обратите внимание на строку # Serial port speed, где задаётся скорость интерфейса.
Кроме того, задаём режим работы с SysFS trx_control = sysfs_1, а также путь к файлу, который будет в данном процессе задействован: trx_sysfile = /sys/class/gpio/gpio7/value. Как видно из схемы, я задействую GPIO7.
Теперь настало время подключить эту самую GPIO7 к SysFS, иначе чуда не произойдёт. Я решил сделать это по красоте, как некто предложил вот здесь:
В каталоге /etc/init.d/ создал скрипт под именем gpio_init:
Bash:
#!/bin/sh
### BEGIN INIT INFO
# Provides:          gpio_init
# Required-Start:    $local_fs $network $named $time $syslog
# Required-Stop:     $local_fs $network $named $time $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Description:       Initialize GPIO pins for the project
### END INIT INFO

SCRIPT=/usr/local/bin/gpio-init
RUNAS=root

PIDFILE=/var/run/gpio_init.pid
LOGFILE=/var/log/gpio_init.log

start() {
  if [ -f /var/run/$PIDNAME ] && kill -0 $(cat /var/run/$PIDNAME); then
    echo 'Service already running' >&2
    return 1
  fi
  echo 'Starting service…' >&2
  local CMD="$SCRIPT &> \"$LOGFILE\" & echo \$!"
  su -c "$CMD" $RUNAS > "$PIDFILE"
  echo 'Service started' >&2
}

stop() {
  if [ ! -f "$PIDFILE" ] || ! kill -0 $(cat "$PIDFILE"); then
    echo 'Service not running' >&2
    return 1
  fi
  echo 'Stopping service…' >&2
  kill -15 $(cat "$PIDFILE") && rm -f "$PIDFILE"
  echo 'Service stopped' >&2
}

uninstall() {
  echo -n "Are you really sure you want to uninstall this service? That cannot be undone. [yes|No] "
  local SURE
  read SURE
  if [ "$SURE" = "yes" ]; then
    stop
    rm -f "$PIDFILE"
    echo "Notice: log file is not be removed: '$LOGFILE'" >&2
    update-rc.d -f gpio_init remove
    rm -fv "$0"
  fi
}

case "$1" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  uninstall)
    uninstall
    ;;
  retart)
    stop
    start
    ;;
  *)
    echo "Usage: $0 {start|stop|restart|uninstall}"
esac
Меняем ему владельца на root.root и ставим права 755.
В /usr/local/bin/ создал скрипт вод именем gpio-init такого содержания:
Bash:
#! /bin/bash

function cleanup {
    echo 7 > /sys/class/gpio/unexport
    exit 0
}

# Clean up when exit
trap cleanup EXIT
trap cleanup SIGHUP
trap cleanup SIGQUIT
trap cleanup SIGINT
trap cleanup SIGTERM

echo 7 > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio7/direction
echo 0 > /sys/class/gpio/gpio7/value
sleep infinity

exit 0
Ставим ему права 755.
Активируем запуск скрипта при загрузке
Bash:
sudo update-rc.d gpio-init defaults
Перезагружаем Репку. Теперь при загрузке GPIO7 настроена выходом и по уполчанию выводит 0.

Осталось запустить MBUSD?
Bash:
sudo systemctl enable mbusd@ttyS0.service
sudo systemctl start mbusd@ttyS0.service
Вроде запустилось...да не тут-то было! Из локалки доступа к серверу как не было, так и нет.:cautious: Вводим sudo systemctl status mbusd@ttyS0.service и убеждаемся, что MBUSD запустился. Вводим команду ss -tlp и убеждаемся, что 502 порт всё-таки наш сервер прослушивает. Ну конечно, порт зарезал фаервол, в качестве которого у меня стоит nftables, но у вас может быть что-то иное. Разрешим в nftables.conf доступ:
Bash:
table inet filter {
  chain input {
    .
    .
    .
    .
    .
    .
    tcp dport 502 accept comment "Accept MODBUS TCP/IP Service"
  }
И вот, наконец, всё заработало. Теперь можно подключиться к Репе и считать данные, например, с регистров вольтметра
Modbus_poll.png
 
  • Like
Реакции: rs045
Для работы с MODBUS предлагаю вот такую маленькую программу: ModbusMaster v.2.2 от SkunkWorx. Уже не помню, где я её нашёл, но в ряде случаев она бывает весьма полезна. Например, когда надо быстро "бомбить" запросами девайс для проверки его устойчивости - она может это делать гораздо быстрее большинства другого софта.
ModbusMaster.png
 
  • Like
Реакции: rs045
Добавлю ещё пару слов:
Как видно из схемотехники адаптера, цифровой изолятор даёт не только гальваническую развязку, но и согласование уровней 3,3 - 5В. 4-канальный, разумеется, ставить не обязательно, достаточно 2+1 каналов.
DC-DC выдаёт несколько завышенное напряжение, которое потом "подрезается" на LDO до номинальных 5В.
mbud работает несколько умнее, чем простой конвертер, он умеет сам повторять запрос, если устройство с первого раза не ответило адекватно. По крайней мере, это работает для стандартных кодов запроса. Настройки режима повтора - в секции "Request/response settings".
Команда sudo systemctl enable mbusd@ttyS0.service у меня выполнилась правильно почему-то не сразу, а после перезагрузки. Возможно, надо будет вначале ещё выполнить sudo systemctl daemon-reload.
Почему-то у меня поначалу нормально проходил только первый MODBUS-запрос после перезапуска mbud, а потом таймауты. Но в какой-то момент всё заработало как надо и до сих пор работает. Склоняюсь к мысли, что был не очень хорошо вставлен шлейф, его с таким корпусом не очень удобно втыкать. Скорее всего, потом заменю на нём разъёмы на BLD-10, они должны вписаться лучше.
 
Последнее редактирование: