Хабрахабр

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

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

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

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

@Raspberry Pi 2 | center | 300x0

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

Установку и настройку ОС Linux на устройство Raspberry в данном материале не рассматриваю.

Для редактирования текста в среде Linux использовал редакторы nano и mcedit.
Для доступа к мини-ПК на базе ОС Linux из среды Windows пользовался клиентами для удаленного доступа по протоколу SSH — KiTTY/PuTTY.
Для передачи файлов между ОС Windows и Linux использовал программу WinSCP.

Ключевые слова, синтаксис и другие основные особенности языка были заимствованы из другого командного интерпретатора sh (сокращение от shell).
Bash — это ещё и мощный язык программирования. Bash — командный интерпретатор (командная оболочка).
Bash — аббревиатура от "Bourne-Again Shell" ("возрождённая" оболочка).

Это сделано с целью большого охвата аудитории. Я занимаюсь сопровождением программных продуктов на базе 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. 00. В этой секции нам нужна строка Vendor=0000 ProdID=0131 Rev=01. Так идентифицирует себя устройство. А конкретно vendor=0000 prodID=0131.

Выполняем команду с повышенными правами: Для корректной работы с ДП необходимо загрузить модуль работы с USB в ядро системы.

sudo modprobe usbserial vendor=0x0000 product=0x0131

usbserial — модуль ядра который обеспечивает эмуляцию COM-порта на USB устройствах. modprobe — программа для добавления модулей в ядро Linux. 0x – означает шестнадцатеричный формат.

Такова важная особенность взаимодействия с устройствами в ОС Linux – работа с устройством как с файлом. Так как у меня подключено одно USB-устройство, то в системе Linux автоматически получает файл ttyUSB0. Файлы устройств хранятся в каталоге /dev.

Для корректной работы с ДП установим скорость передачи данных:

stty -F /dev/ttyUSB0 9600

-F — устройство. stty — команда устанавливает параметры терминального ввода/вывода для устройства. И для данного устройства устанавливается скорость 9600 бод. В нашем случае дисплей покупателя /dev/ttyUSB0.

Теперь можно попробовать вывести приветственное сообщение (пока на английском):

echo "Hello!" > /dev/ttyUSB0

Подробнее о команде ниже. Если всё сделали правильно, то на экране появится наше сообщение.

2 этап. Программирование

И это не очень красиво. На предыдущем этапе мы вывели сообщение на английском языке поверх заставочного сообщения устройства.

Для очистки экрана дисплея выполним команду:

echo -e -n "\x0c\x0b" > /dev/ttyUSB0

Опция -e — включает поддержку вывода escape-последовательностей, -n — указывает, что не надо выводить перевод строки. echo — команда вывода в терминал. Допускается запись -en.

Сочетания символов, состоящих из обратной косой черты \, за которой следует буква или набор цифр, называются escape-последовательностями.

Символ > — управление потоком (перенаправляет вывод). — очистка экрана дисплея и отмена строчного режима, 0b — перемещает курсор в верхнюю крайнюю левую позицию. Если просто выполнить команду echo "Hello!", то в окне терминала появится текст, указанный в кавычках. В данном случае в файл /dev/ttyUSB0 нашего устройства.

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

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

Расширение sh — означает, что это файл-скрипт bash. Где namefile.sh — файл скрипта. u+x — устанавливает право на выполнение файла для текущего пользователя. chmod – программа для изменения прав доступа к файлам и каталогам.

Первый скрипт — основной (dispos.sh). Решение задачи выполним двумя скриптами. Второй — вспомогательный (parse.sh) получает данные погоды, сервисов котировок валют и записывает данные в промежуточные файлы. Он выводит всю необходимую информацию на дисплей покупателя. Промежуточные файлы с данными используются в первом скрипте.

Для того, чтобы скрипты могли выполняться, необходимо выполнить команды:

sudo chmod +x dispos.sh
sudo chmod +x parse.sh

Это "укороченная" версия u+x. Обратите внимание, что используется просто +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()"
} # Функция перевода курсора на следующую строку
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"`

05. После выполнения получаем дату вида: 20. 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)

Параметр -n – выводит текущую выбранную строку. sed — это текстовый редактор, выполняющий операции редактирования над информацией в стандартном потоке ввода или файле. ‘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

Комментарии

Опция -q — выводит минимум информации, -O — сохраняет в указанный файл. Команда wget позволяет скачивать из сети файлы, страницы и т.д.

В строках ниже производится запись в файл:

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}")

Передаётся число 6. Где {1} — параметр.

Обратите внимание на сложную функцию замены подстроки, например:

LINEBTC//[^.^0-9]/

В строке остаются только символ "." и все цифры от 0 до 9.

Послесловие

А некоторые команды, по сравнению с аналогами в 1С, удивляют своей лаконичностью и функциональностью. В языке bash доступны практически все возможности "обычных" языков программирования.

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

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

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

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть