Появился дисплей покупателя. Интересно стало попробовать его использовать в качестве информационного табло для вывода информации о текущем дне, времени до конца рабочего дня/недели, информации о погоде, курсе валют.
При этом не хотел использовать ресурсоёмкие приложения и свой ПК. Остановился на связке мини-ПК Raspberry + Linux + Дисплей покупателя.


Необходимые замечания
Установку и настройку ОС Linux на устройство Raspberry в данном материале не рассматриваю.
Для редактирования текста в среде Linux использовал редакторы nano и mcedit.
Для доступа к мини-ПК на базе ОС Linux из среды Windows пользовался клиентами для удаленного доступа по протоколу SSH — KiTTY/PuTTY.
Для передачи файлов между ОС Windows и Linux использовал программу WinSCP.
Bash — командный интерпретатор (командная оболочка).
Bash — аббревиатура от "Bourne-Again Shell" ("возрождённая" оболочка). Ключевые слова, синтаксис и другие основные особенности языка были заимствованы из другого командного интерпретатора sh (сокращение от shell).
Bash — это ещё и мощный язык программирования.
Я занимаюсь сопровождением программных продуктов на базе 1С и для меня это было возможностью самому познакомится с программированием в среде Linux.
В меру своего понимания буду разъяснять выполняемые команды. Это сделано с целью большого охвата аудитории.
Что использовал?
- Одноплатный компьютер Raspberry Pi 2 Model B v1.1 с установленной ОС Raspbian GNU/Linux 9.4 (stretch).
- Дисплей покупателя POSUA LPOS-VFD USB.
- Командный интерпретатор bash.
1 этап. Подключение и настройка дисплея покупателя
После того как присоединили к USB-порту дисплей покупателя (ДП) выясним параметры подключенного устройства. В терминале выполним команду:
usb-devices
Получим список присоединенных USB устройств к Raspberry:
T: Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=480 MxCh= 1 D: Ver= 2.00 Cls=09(hub ) Sub=00 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=1d6b ProdID=0002 Rev=04.14 S: Manufacturer=Linux 4.14.69-v7+ dwc_otg_hcd S: Product=DWC OTG Controller S: SerialNumber=3f980000.usb C: #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=0mA I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=480 MxCh= 5 D: Ver= 2.00 Cls=09(hub ) Sub=00 Prot=02 MxPS=64 #Cfgs= 1 P: Vendor=0424 ProdID=9514 Rev=02.00 C: #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=2mA I: If#= 0 Alt= 1 #EPs= 1 Cls=09(hub ) Sub=00 Prot=02 Driver=hub T: Bus=01 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#= 3 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=ff(vend.) Sub=00 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=0424 ProdID=ec00 Rev=02.00 C: #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=2mA I: If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=ff Driver=smsc95xx T: Bus=01 Lev=02 Prnt=02 Port=01 Cnt=02 Dev#= 4 Spd=12 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 P: Vendor=0000 ProdID=0131 Rev=01.00 S: Manufacturer=www.posua.com S: Product=POSua LPOS-II-VFD USB CDC C: #Ifs= 2 Cfg#= 1 Atr=a0 MxPwr=16mA I: If#= 0 Alt= 0 #EPs= 3 Cls=02(commc) Sub=02 Prot=01 Driver=usbserial_generic I: If#= 1 Alt= 0 #EPs= 2 Cls=03(HID ) Sub=00 Prot=00 Driver=usbhid
Из информации полученной командой находим строку Product=POSua LPOS-II-VFD USB CDC. Это наш дисплей покупателя. В этой секции нам нужна строка Vendor=0000 ProdID=0131 Rev=01.00. А конкретно vendor=0000 prodID=0131. Так идентифицирует себя устройство.
Для корректной работы с ДП необходимо загрузить модуль работы с USB в ядро системы. Выполняем команду с повышенными правами:
sudo modprobe usbserial vendor=0x0000 product=0x0131
modprobe — программа для добавления модулей в ядро Linux. usbserial — модуль ядра который обеспечивает эмуляцию COM-порта на USB устройствах. 0x – означает шестнадцатеричный формат.
Так как у меня подключено одно USB-устройство, то в системе Linux автоматически получает файл ttyUSB0. Такова важная особенность взаимодействия с устройствами в ОС Linux – работа с устройством как с файлом. Файлы устройств хранятся в каталоге /dev.
Для корректной работы с ДП установим скорость передачи данных:
stty -F /dev/ttyUSB0 9600
stty — команда устанавливает параметры терминального ввода/вывода для устройства. -F — устройство. В нашем случае дисплей покупателя /dev/ttyUSB0. И для данного устройства устанавливается скорость 9600 бод.
Теперь можно попробовать вывести приветственное сообщение (пока на английском):
echo "Hello!" > /dev/ttyUSB0
Если всё сделали правильно, то на экране появится наше сообщение. Подробнее о команде ниже.
2 этап. Программирование
На предыдущем этапе мы вывели сообщение на английском языке поверх заставочного сообщения устройства. И это не очень красиво.
Для очистки экрана дисплея выполним команду:
echo -e -n "\x0c\x0b" > /dev/ttyUSB0
echo — команда вывода в терминал. Опция -e — включает поддержку вывода escape-последовательностей, -n — указывает, что не надо выводить перевод строки. Допускается запись -en.
Сочетания символов, состоящих из обратной косой черты \, за которой следует буква или набор цифр, называются escape-последовательностями.0с — очистка экрана дисплея и отмена строчного режима, 0b — перемещает курсор в верхнюю крайнюю левую позицию. Символ > — управление потоком (перенаправляет вывод). В данном случае в файл /dev/ttyUSB0 нашего устройства. Если просто выполнить команду echo "Hello!", то в окне терминала появится текст, указанный в кавычках.
Кстати, команду изменяющую скорость для передачи данных на устройство можно было записать и так:
stty 9600 < /dev/ttyUSB0
Ну и для вывода сообщений на русском языке выполним:
echo -n "Привет!" | iconv -f UTF-8 -t CP866 > /dev/ttyUSB0
| — перенаправляет вывод одной команды на вход другой (конвейер). В нашем случае последовательность символов "Привет!" не выводится в файл устройства сразу, а передаётся на "конвертирование" утилите iconv. iconv — преобразует из одной кодировки в другую.
Командный интерпретатор bash позволяет не только выполнять команды непосредственно в терминале, но и писать файлы-скрипты.
Скрипт — обычный текстовый файл с последовательностью выполняемых команд.
Для того, чтобы bash понимал, что это "его" в начале файла указывается #!/bin/bash. А для непосредственного выполнения скрипта надо выполнить команду:
sudo chmod u+x namefile.sh
Где namefile.sh — файл скрипта. Расширение sh — означает, что это файл-скрипт bash. chmod – программа для изменения прав доступа к файлам и каталогам. u+x — устанавливает право на выполнение файла для текущего пользователя.
Решение задачи выполним двумя скриптами. Первый скрипт — основной (dispos.sh). Он выводит всю необходимую информацию на дисплей покупателя. Второй — вспомогательный (parse.sh) получает данные погоды, сервисов котировок валют и записывает данные в промежуточные файлы. Промежуточные файлы с данными используются в первом скрипте.
Для того, чтобы скрипты могли выполняться, необходимо выполнить команды:
sudo chmod +x dispos.sh sudo chmod +x parse.sh
Обратите внимание, что используется просто +x. Это "укороченная" версия u+x.
Скрипты нужно запускать с определённой периодичностью. Для этого воспользуемся стандартным планировщиком crontab. Для редактирования служит команда:
crontab -e
В планировщик добавим две строки:
*/20 * * * * /home/pi/parse.sh */1 * * * * /home/pi/dispos.sh
Скрипт parse.sh выполняется каждые 20 минут, а скрипт dispos.sh каждую минуту.
Перед первоначальным выводом на дисплей покупателя прежде надо выполнить скрипт parse.sh который получит первичные данные о погоде и валюте.
./parse.sh
Далее я приведу полные тексты скриптов с короткими комментариями.
Описание файлов-скриптов
Файл dispos.sh
#!/bin/bash # Данный скрипт выводит информацию на дисплей покупателя POSua LPOS-VFD. # По-умолчанию порт ttyUSB0. # Для определения устройства как tty используется команда: # modprobe usbserial vendor=0x0000 product=0x0131. # Где 0x0000 и 0x0131 адрес устройства, определяемый командами # usb-devices, lsusb или dmesg. # Установка скорости передачи stty 9600 < /dev/ttyUSB0. # Получение данных о погоде и валюте реализовано отдельным скриптом parse.sh # Оба скрипта прописаны в crontab и выполняются с разными временными интервалами. # **************************************************************** # Глобальные переменные # ttyUSB - устройство на которое выводим информацию (POS-дисплей) DEV_DISPLAY="/dev/ttyUSB0" # Определение дня недели и конца рабочего дня # Все рабочие дни, кроме пятницы, заканчиваются в 18:00:00 # В пятницу в 17:00:00 TIME_OF_WORKDAY="18:00:00" if (( $(date "+%u") >= 5 )); then TIME_OF_WORKDAY="17:00:00" fi # Определяем сколько до конца недели (пятница 17:00:00) # с текущей даты в секундах DAY_OF_WEEKEND=`date +"%s" --date="friday 17:00:00"` # **************************************************************** # Функции для работы с дисплеем покупателя # Функция очистки дисплея disp_clear(){ echo -en "\x0c\x0b" > "${DEV_DISPLAY}" } # Функция перевода курсора на следующую строку disp_cr(){ echo -e "\x0b" > "${DEV_DISPLAY}" } # Функция непосредственного вывода строки на дисплей disp_print(){ echo -n $1 | iconv -f UTF-8 -t CP866 > "${DEV_DISPLAY}" } # **************************************************************** # Основная часть скрипта # 1. Вывод текущей даты disp_clear # Очистка дисплея disp_print "Сегодня: `date "+%A"`" disp_cr # Перевод на следующую строку disp_print " `date "+%d.%m.%Y %H:%M"`" sleep 8 # **************************************************************** # 2. Вывод информации до конца рабочего дня disp_clear disp_print " До конца раб. дня:" disp_cr HOURS=$(( ( $(date +%s --date=$TIME_OF_WORKDAY) - $(date +%s) ) / 3600 )) MINUTES=$(( (( $(date +%s --date=$TIME_OF_WORKDAY) - $(date +%s) ) - $HOURS * 3600) / 60 )) # Определяем закончился рабочий день или нет if (( $MINUTES > -1 )); then OUTPUT_TIME=" ${HOURS} час. ${MINUTES} мин." else OUTPUT_TIME=" ЗАКОНЧИЛСЯ!" fi # Вывод время до конца рабочего дня disp_print "${OUTPUT_TIME}" sleep 8 # **************************************************************** # 3. Вывод информации до конца рабочей недели disp_clear disp_print "До конца раб. недели:" disp_cr DAYS=$(( ($DAY_OF_WEEKEND-$(date +%s)) / (24*3600) )) HOURS=$(( (($DAY_OF_WEEKEND-$(date +%s)) - ($DAYS*24*3600)) / 3600 )) MINUTES=$(( (($DAY_OF_WEEKEND-$(date +%s)) - ($DAYS*24*3600) - ($HOURS*60*60)) / 60 )) # Определяем закончилась рабочая неделя или нет if (( $MINUTES > -1 )); then OUTPUT_TIME="${DAYS} дн. ${HOURS} час. ${MINUTES} мин" else OUTPUT_TIME=" ЗАКОНЧИЛАСЬ!" fi # Вывод времени до конца рабочей недели disp_print "${OUTPUT_TIME}" sleep 8 # **************************************************************** # 4. Вывод информации о погоде # 4.1. Считываем данные из временного файла LINE1=$(sed -n '1{p;q}' /tmp/weather.txt) DISPLAY_LINE1=${LINE1:0:19} DISPLAY_LINE2=${LINE1:19:19} # Вывод погоды на дисплей (2 строками) disp_clear disp_print "${DISPLAY_LINE1}" disp_cr disp_print "${DISPLAY_LINE2}" sleep 4 # 4.2. Вторая часть погоды LINE1=$(sed -n '2{p;q}' /tmp/weather.txt) DISPLAY_LINE1=${LINE1:0:14} DISPLAY_LINE2=${LINE1:14:19} # Вывод погоды на дисплей (2 строками) disp_clear disp_print "Ожид ${DISPLAY_LINE1}" disp_cr disp_print "${DISPLAY_LINE2}" sleep 8 # **************************************************************** # 5. Вывод информации о банковской валюте # Считываем данные из временного файла # Доллар DOLLAR=$(sed -n '1{p;q}' /tmp/ex.txt) DOLLAR=${DOLLAR//–/-} # Евро EURO=$(sed -n '2{p;q}' /tmp/ex.txt) EURO=${EURO//–/-} # Вывод валюты на дисплей disp_clear disp_print "Доллар: ${DOLLAR}" disp_cr disp_print "Евро: ${EURO}" sleep 8 # **************************************************************** # 6. Вывод информации о электронной валюте # Считываем данные из временных файлов # BTC while read line do BTC=${line:0:13} done </tmp/bitcoin.txt # ETH while read line do ETH=${line:0:13} done </tmp/ethereum.txt # Вывод валюты на дисплей # Заменяем разделитель точка на запятую disp_clear disp_print "BTC: ${BTC//./,}" disp_cr disp_print "ETH: ${ETH//./,}" #sleep 8 # **************************************************************** # 7. Вывод произвольной информации на дисплей # Здесь необходимо написать любую строку (макс. длина 20 символов) #DISPLAY_LINE1="С Новым Годом!" #DISPLAY_LINE2="С Днём России!" # Вывод произвольной информации на экран #disp_clear #disp_print "${DISPLAY_LINE1:0:19}" #disp_cr #disp_print "${DISPLAY_LINE2:0:19}"
Комментарии
Для вывода текущей даты служит команда date. Пример,
echo `date "+%d.%m.%Y %H:%M"`
После выполнения получаем дату вида: 20.05.2019 12:11.
Для расчёта времени до конца дня воспользуемся дополнительной переменной TIME_OF_WORKDAY и установим значение TIME_OF_WORKDAY="18:00:00". Ну а далее рассчитаем часы и минуты до конца рабочего дня:
HOURS=$(( ( $(date +%s --date=$TIME_OF_WORKDAY) - $(date +%s) ) / 3600 )) MINUTES=$(( (( $(date +%s --date=$TIME_OF_WORKDAY) - $(date +%s) ) - $HOURS * 3600) / 60 ))
Символ $ — указывает на то, что это переменная.
Символ # — комментарий.
date +%s — получаем текущую дату и время в секундах.
date +%s --date=$TIME_OF_WORKDAY — получаем время в секундах до TIME_OF_WORKDAY ("18:00:00").
Расчет времени до конца рабочей недели:
DAYS=$(( ($DAY_OF_WEEKEND-$(date +%s)) / (24*3600) )) HOURS=$(( (($DAY_OF_WEEKEND-$(date +%s)) - ($DAYS*24*3600)) / 3600 )) MINUTES=$(( (($DAY_OF_WEEKEND-$(date +%s)) - ($DAYS*24*3600) - ($HOURS*60*60)) / 60 ))
Где DAY_OF_WEEKEND=`date +"%s" --date="friday 17:00:00"` — время в секундах с текущего момента времени до пятницы 17:00:00.
Часть скрипта реализована с помощью функций. Например,
# Функция очистки дисплея disp_clear(){ echo -en "\x0c\x0b" > "${DEV_DISPLAY}" }
disp_clear() — название функции. В {} указываются выполняемые команды.
Переменная DEV_DISPLAY является "глобальной" и задаётся вначале скрипта и соответственно DEV_DISPLAY="/dev/ttyUSB0".
Чтение данных из файла, например конкретной строки (1):
LINE1=$(sed -n '1{p;q}' /tmp/weather.txt)
sed — это текстовый редактор, выполняющий операции редактирования над информацией в стандартном потоке ввода или файле. Параметр -n – выводит текущую выбранную строку. ‘1{p;q}’ — печатает 1 строку и выходит не читая остальные (p — печать, q — выход).
Ещё вариант чтения из файла (построчно):
while read line do BTC=${line:0:13} done </tmp/bitcoin.txt
А таким образом DISPLAY_LINE1=${LINE1:0:14} из строки LINE1 извлекаем подстроку длиной 14 символов начиная с 0.
Замена символов производится комбинацией //, например, так DOLLAR//–/-. Заменяется символ "–" на "-".
Файл parse.sh
#!/bin/bash # Чтение данных погоды в формате RSS с сайта http://rp5.ru/rss/1859/ru # 1859 - код города Брянск # Функция конвертирования текстовой строки с валютой conv(){ # Переворачиваем последовательность строк и читаем указанные строки CURRENCY=$(sed -n '1!G;h;$p' /tmp/ex.xml | sed -n "${1}{p;q}") CURRENCY=${CURRENCY//[^,^(^)^0-9^–^+]/} echo $CURRENCY } # Сохраняем файлы c данными во временный каталог # 1. Погода wget -q -O /tmp/rp5weather.xml http://rp5.ru/rss/1859/ru # 2. Получение курса доллара и евро wget -q -O /tmp/ex.xml http://currr.ru/rss/ # 3. Получение данных bitcoin/ethereum wget -q -O /tmp/bitcoin.json https://api.coinmarketcap.com/v1/ticker/bitcoin/ wget -q -O /tmp/ethereum.json https://api.coinmarketcap.com/v1/ticker/ethereum/ # Разбор погоды # Читаем построчно файл, находим маркер, подготавливаем строки погоды # и сохраняем в файл LINE31=$(sed -n '31{p;q}' /tmp/rp5weather.xml) LINE33=$(sed -n '33{p;q}' /tmp/rp5weather.xml) WEATHER1=${LINE31//"</title>"} WEATHER1=${WEATHER1//" °C"} WEATHER1=${WEATHER1//" было "} WEATHER1=${WEATHER1:29} WEATHER2=${LINE33##*ожидается} WEATHER2=${WEATHER2//"°"} echo "${WEATHER1}С" > /tmp/weather.txt echo ${WEATHER2%.*} >> /tmp/weather.txt # Получаем значение Bitcoin LINEBTC=$(sed -n '7{p;q}' /tmp/bitcoin.json) echo "${LINEBTC//[^.^0-9]/}" > /tmp/bitcoin.txt # Получаем значение Ethereum LINEETH=$(sed -n '7{p;q}' /tmp/ethereum.json) echo "${LINEETH//[^.^0-9]/}" > /tmp/ethereum.txt # Разбор валюты DOLLAR=$(conv 8) echo $DOLLAR > /tmp/ex.txt EURO=$(conv 6) echo $EURO >> /tmp/ex.txt
Комментарии
Команда wget позволяет скачивать из сети файлы, страницы и т.д. Опция -q — выводит минимум информации, -O — сохраняет в указанный файл.
В строках ниже производится запись в файл:
echo "${WEATHER1}С" > /tmp/weather.txt echo ${WEATHER2%.*} >> /tmp/weather.txt
Причем, если используется перенаправление потока вывода в файл >, то содержимое файла перезаписывается, а использование >> дозаписывает данные в файл.
Пример использования параметра в функции:
conv 6
Непосредственно в функции:
CURRENCY=$(sed -n '1!G;h;$p' /tmp/ex.xml | sed -n "${1}{p;q}")
Где {1} — параметр. Передаётся число 6.
Обратите внимание на сложную функцию замены подстроки, например:
LINEBTC//[^.^0-9]/
В строке остаются только символ "." и все цифры от 0 до 9.
Послесловие
В языке bash доступны практически все возможности "обычных" языков программирования. А некоторые команды, по сравнению с аналогами в 1С, удивляют своей лаконичностью и функциональностью.
На данный момент дисплей покупателя в качестве информационного табло стабильно работает больше полугода.
