Опыт организации диалога с модулями ICP DAS I-7000 по RS-485 используя только bash

в 11:17, , рубрики: bash, RS-485, интерфейсы, лень, периферия, Разработка под Linux
Опыт организации диалога с модулями ICP DAS I-7000 по RS-485 используя только bash - 1
Предыстория:

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

Дано:

Схема обычного тривиального подключения по RS-485
Схема обычного тривиального подключения по RS-485
  1. ICP DAS I-7080

  2. ICP DAS I-7019

  3. ICOP VDX2 на Vortex86 c RS-232, RS-485, USB2, VGA, PS/2 и промышленным накопителем на 4Gb

Проанализировав состояние накопителя, который уже потрудился 10 лет (установлена Windows XP, которая так и не стала нашей), было принято решение что следующей ОС будет Linux (дополнительная задача, это работа с модемом, отправка данных по стандартным протоколам и т.п.), тем более что она прекрасно работает с обычными флэш-накопителя и как оказалось ядро 586 совместимо с Vortex86. Но ложкой дегтя стала версия совместимого с платой дистрибутива Debian8, который уже можно причислять к ряду некро-, а также наличие всего 256 Мб ОЗУ на плате.
Ничто не предвещало проблем: было вывялено что rs485 расположен в /dev/ttyS1. Модуль I-7019 под адресом #1, I-7080 - #2, контрольная сумма не используется и документация на модули в сети есть. Через minicom модули отзывчиво давали результаты, единственное что это было не в классическом стиле листинга, а в одну строку. Сам minicom модифицирует настройки порта, которые можно увидеть командой
stty -a -F /dev/ttyS0

Тут для нас важным является отключение аппаратного управления потоком параметром
-crtsdts.

В моем варианте после определенных тестирований было принято использовать следующую строку инициализации порта:

stty 9600 min 1 time 10 -echo -crtscts ignbrk -brkint icrnl -imaxbel -opost -isig -icanon -iexten -echo echok -echoctl -echoke -F /dev/ttyS1

есть подозрение, что тут всё-таки есть неточность, из-за которой начались кривые отношения с модулями.

Стандартный вариант диалога с модулем по RS-485 в режиме команд:

#!/bin/bash
TTY="/dev/ttyS1"
SLEEP=6
RESULT=$(echo -e '#020r' > $TTY; sleep $SLEEP; kill %cat) //счит. значений счетчика 0 i7080
echo $result

И нельзя сказать, что это работает, так как модуль может просто «забить» на команду, путем опытных испытаний было выявлена «отзывчивость» модулей: I-7019 около 95%, а вот I-7080 около 75%. Были проведены эксперименты с разными значениями SLEEP от 1 до 12, лучшие результаты на значениях 3, 6 и 7.

Так не бывает!? Может порт проблемный? Чтобы исключить проблему с портом, был использован самый простой свисток с AliExpress на контроллере HL-340, который проявил себя лучше встроенного, доведя значения «отзывчивости» для I-7019 около 97%, а для I-7080 до 90%.

Без боязни «запачкать код нечистотами» было принято решение скидывать опрос модуля в файл, а потом уже обрабатывать результаты как промежуточный вариант средствами того же bash, дабы результаты начальству все таки нужны в срок.

Пример части скрипта:

…
fls1="/data/log.1";
#TTY="/dev/ttyS1"
TTY="/dev/ttyUSB1"
SLEEP4=4; #задержка
RESULT=$(cat $TTY >> $fls1 & # ответы пишем в файл
echo -e "ncount0.1 "`date +"%H:%M:%S"`"t*" >> $fls1; #пишем в файл время попытки
echo -e '#020r' > $TTY; sleep $SLEEP4; #первая попытка считать ответ
echo -e "ncount0.2 "`date +"%H:%M:%S"`"t*" >> $fls1; #пишем в файл время попытки
echo -e '#020r' > $TTY; sleep $SLEEP4; #вторая попытка считать в ответ
kill %cat) # принудительно завершаем диалог
…

Особенности обработки текстовых данных, в которых модуль упорно отправляет данные без переноса строки рассматривать в данной статье не будем, перейдем к следующим проблемам.

Кроме опроса счетчика его нужно обнулять, а это команда «$0260» для счетчика 0 и «$0261» для счетчика 1. Идея перенести опрос порта в отдельный файл и получать только строку с успешным результатом не покидала и поэтому код модифицировал до следующего:

#!/bin/bash
s_cmd=$1
case $s_cmd in # для примера перечислим несколько команд
data20) # получаем данные счетчика0 от модуля2
s_cmd ='#020r'
;;
reset20) # обнуляем данные счетчика0 от модуля2
s_cmd ='$0260r'
;;
data7019) # получаем данные АЦП от модуля1
s_cmd ='#01r'
;;
*)
s_cmd ='$02Mr' //выводим информацию о имени модуля2 если ничего нет
;;
esac
#TTY="/dev/ttyS1" – со встроенным портом нет адекватной работы
TTY="/dev/ttyUSB0"
SLEEP=5
RESULT=""
while [ ! -n "$RESULT" ]; do # пока модуль не ответит, «дергаем» его
RESULT=$(cat $TTY & echo -e "$s_cmd " > $TTY; sleep $SLEEP; kill %cat) #обычно на первый нет ответа
RESULT=$(cat $TTY & echo -e "$s_cmd " > $TTY; sleep $SLEEP; kill %cat)
done
echo "$RESULT"

ну и вызов, собственно, по типу:
 ./rs485.bash data20

Не покидала мысль что существующий метод «кривой», и все-таки нужно сделать по-другому. Я стал пробовать другие варианты, и случаем, читая информацию про очередные жалобы о проблемах общения с последовательным портом набрел на обсуждение >тут<. Увидел другой вариант, и оказалось, что встроенный контроллер дал вменяемую устойчивость адекватных ответов, и моя часть кода диалога по rs485 приобрела вид:

#блок выбора команд в s_cmd из вышестоящего примера…
…строки 1-16 вышестоящего примера…
TTY="/dev/ttyS1" #порт rs485 
fil="/tmp/ttyDump.dat"
rm $fil  #удаляем результаты предыдущего ответа
while [ ! -s $fil ]; do   # пока нет ответа модуля в файле
exec 3<$TTY               #перенаправляем ответ порта в файловый дескриптор
  cat <&3 > $fil &        #перенаправляем ответ порта из дескриптора в файл
  PID=$!                  #получаем PID для последующего забоя CAT
    echo -e $s_cmd > $TTY #отправляем команду в порт
    sleep 7
  kill $PID               #KILL наш CAT
  wait $PID 2>/dev/null   #ждем завершения CAT
exec 3<&-                 #освобождаем файловый дескриптор 3
done
cat $fil                  #показываем полученные данные
echo "" #делаем перенос строки для ответа скрипта

Возможно, мои мытарства кому-то позволят сэкономить время и облегчить труд bash программирования «на коленке» без использования сторонних библиотек.

По поводу советов что писать надо было сразу на C/C++ могу заметить, что львиная доля уже существующих обсуждений и вопросов по программированию rs485 на просторах сети, позволяют заподозрить что и там не все так гладко как хотелось бы, попадаются советы вплоть до перекомпиляции ядра. А эксперименты с Python учитывая устаревшую версию ОС сразу же закончились отсутвием поддержки современных библиотек. Что тут можно сказать? Будет время на эти задачи – попробуем (я не активный C/C++ программист). Пока существует масса других задач как по бэкенду, так и фронтенду.

Автор: Olexandr Kovalenko

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js