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

Вывод информации на дисплей покупателя

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

Появился дисплей покупателя. Интересно стало попробовать его использовать в качестве информационного табло для вывода информации о текущем дне, времени до конца рабочего дня/недели, информации о погоде, курсе валют.


При этом не хотел использовать ресурсоёмкие приложения и свой ПК. Остановился на связке мини-ПК Raspberry + Linux + Дисплей покупателя.


@Дисплей покупателя с информацией | center | 700x0


@Raspberry Pi 2 | center | 300x0


Необходимые замечания


Установку и настройку ОС 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-последовательностями.

— очистка экрана дисплея и отмена строчного режима, 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


файл 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


файл 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С, удивляют своей лаконичностью и функциональностью.


На данный момент дисплей покупателя в качестве информационного табло стабильно работает больше полугода.


Список ресурсов


  1. Страница дисплея покупателя LPOS-VFD
  2. Программируем символы валют для дисплея покупателя
  3. Основы BASH (часть 1)
  4. Основы BASH (часть 2)
  5. Как пользоваться PuTTY
  6. Текстовый редактор nano в Linux для новичков
  7. Инструкция для пользователей WinSCP
Теги:
Хабы:
+19
Комментарии 13
Комментарии Комментарии 13

Публикации

Истории

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн