Используем LCD-индикатор в Debian: различия между версиями

Материал из Linux Wiki
Перейти к навигацииПерейти к поиску
м (+lirc)
м
Строка 3: Строка 3:
== Изучение темы ==
== Изучение темы ==


Для начала залез в гугл в поисках информации о малогабаритных символьных LCD-индикаторах с USB-интерфейсом и поддерживающихся в Linux. Сходу нагуглились некие [http://www.mini-box.com/site/index.html picoLCD] и статьи по их настройке. Однако цена в 40-50$ как-то многовато для маленького индикатора. Поиск альтернативных вариантов привел к статье на Википедии про [[ru_wikipedia:HD44780|HD44780]] - фактический стандарт для подобных LCD-контроллеров. Следовательно, оставалось найти способ подключить его к компьютеру.
Для начала залез в гугл в поисках информации о малогабаритных символьных LCD-индикаторах с USB-интерфейсом и поддерживающихся в Linux. Сходу нагуглились некие [http://www.mini-box.com/site/index.html picoLCD] и статьи по их настройке. Однако цена в 40-50$ - как-то многовато для маленького индикатора. Поиск альтернативных вариантов привел к статье на Википедии про [[ru_wikipedia:HD44780|HD44780]] - фактический стандарт для подобных LCD-контроллеров. Следовательно, оставалось найти способ подключить его к компьютеру.


Дальнейший поиск показал, что контроллер прекрасно подключается напрямую на LPT-порт компьютера - а значит, при необходимости с использованием простого переходника USB-LPT за 10$ его можно подключить и к USB.
Дальнейший поиск показал, что контроллер прекрасно подключается напрямую на LPT-порт компьютера - а значит, при необходимости с использованием простого переходника USB-LPT за 10$ его можно подключить и к USB.
Строка 14: Строка 14:


На материнской плате плеера обнаружилась гребенка LPT-порта, поэтому задача упрощалась и покупать USB-LPT-переходник не пришлось. За основу была взята [http://service4u.narod.ru/images/lcd/4x20backlight.gif эта схема], исключая цепи подсветки (диод с резистором, транзистор и подстроечник на 16 контакте индикатора). Разводка индикатора соответствует той, что была показана в [[ru_wikipedia:HD44780|таблице на Википедии]]:
На материнской плате плеера обнаружилась гребенка LPT-порта, поэтому задача упрощалась и покупать USB-LPT-переходник не пришлось. За основу была взята [http://service4u.narod.ru/images/lcd/4x20backlight.gif эта схема], исключая цепи подсветки (диод с резистором, транзистор и подстроечник на 16 контакте индикатора). Разводка индикатора соответствует той, что была показана в [[ru_wikipedia:HD44780|таблице на Википедии]]:
* шина данных LPT-порта цепляется на DB-контакты индикатора
* шина данных LPT-порта цепляется один к одному на DB-контакты индикатора
* 1@LPT -> 6@LCD
* 1@LPT -> 6@LCD
* 14@LPT -> 5@LCD
* 14@LPT -> 5@LCD
Строка 28: Строка 28:
Готовыми конфигами под такой индикатор поделиться никто не захотел, поэтому поначалу пробовал все методом полунаучного тыка.
Готовыми конфигами под такой индикатор поделиться никто не захотел, поэтому поначалу пробовал все методом полунаучного тыка.


Поставил lcd4linux, создал более-менее пристойный конфиг на базе того, что нашел в интернете; попробовал прописывать разные доступные драйверы. Попутно пришлось пробовать заставить работать параллельный порт. С портом помогла [https://www.vmware.com/support/ws5/doc/ws_devices_parallel_configure_linux.html#wp1064410 эта статья]. В двух словах суть: нужно выгрузить модуль '''lp''', а загрузить '''parport_pc''' и '''ppdev'''.
Поставил lcd4linux, создал более-менее пристойный конфиг на базе того, что нашел в интернете; попробовал прописывать разные доступные драйверы. Попутно пытался заставить работать параллельный порт. С портом помогла [https://www.vmware.com/support/ws5/doc/ws_devices_parallel_configure_linux.html#wp1064410 эта статья]. В двух словах суть: нужно выгрузить модуль '''lp''', а загрузить '''parport_pc''' и '''ppdev'''.


Однако индикатор в лучшем случае реагировал закрашиванием произвольных блоков.
Однако индикатор в лучшем случае реагировал закрашиванием произвольных блоков.
Строка 102: Строка 102:


Все бы хорошо, однако при попытке отправить что-то, написанное на русском языке, на индикаторе я получал кракозяблики. Однако индикатор должен поддерживать русский (о чем свидетельствует суффикс '''CT''' в обозначении). При поиске решения были опробованы разные варианты, в первую очередь
Все бы хорошо, однако при попытке отправить что-то, написанное на русском языке, на индикаторе я получал кракозяблики. Однако индикатор должен поддерживать русский (о чем свидетельствует суффикс '''CT''' в обозначении). При поиске решения были опробованы разные варианты, в первую очередь
  prefix iconv('UTF-8', 'KOI8-R', 'текст ')
  expression iconv('UTF-8', 'KOI8-R', 'текст ')
увиденный в [http://blindage.org/?p=2657 этой статье]. Однако это был не KOI8-R, да и другие кодировки не подходили. Зато попался на глаза самописный транслятор, где русские буквы заменялись напрямую на коды. Попробовал отправить один из кодов - получил символ. Не тот, что в том трансляторе, но хоть что-то. Набросал простой однострочник вида
увиденный в [http://blindage.org/?p=2657 этой статье]. Однако это был не KOI8-R, да и другие кодировки не подходили. Зато попался на глаза самописный транслятор, где русские буквы заменялись напрямую на коды. Попробовал отправить один из кодов - получил символ. Не тот, что в том трансляторе, но хоть что-то. Набросал простой скрипт вида


<source lang=bash>
<source lang=bash>

Версия 14:22, 18 февраля 2013

Для одной из своих самодельных конструкций - а именно аудиоплеера на базе Linux - я решил использовать LCD-индикатор для отображения текущей информации. В этой статье я опишу подробности сборки конструкции и настройки софта для работы с индикатором в связке Debian + lcd4linux + lirc + Music On Console Player.

Изучение темы

Для начала залез в гугл в поисках информации о малогабаритных символьных LCD-индикаторах с USB-интерфейсом и поддерживающихся в Linux. Сходу нагуглились некие picoLCD и статьи по их настройке. Однако цена в 40-50$ - как-то многовато для маленького индикатора. Поиск альтернативных вариантов привел к статье на Википедии про HD44780 - фактический стандарт для подобных LCD-контроллеров. Следовательно, оставалось найти способ подключить его к компьютеру.

Дальнейший поиск показал, что контроллер прекрасно подключается напрямую на LPT-порт компьютера - а значит, при необходимости с использованием простого переходника USB-LPT за 10$ его можно подключить и к USB.

Обход магазинов города и рынка радиодеталей принес результат в виде индикатора Winstar WH1602B-NYG-CT, представляющего собой LCD-дисплей без подсветки, состоящий из двух строк по 16 символов и управляющийся аналогом упомянутого контроллера. Обошелся он мне в 7$. Были еще более дорогие варианты с подсветкой, а также пара графических дисплеев. В другом магазине нашел Seiko L2432 за 6$ (24 символа, 2 строки, без подсветки (хотя по даташиту вроде должна быть) и без русского (что я выяснил уже позже)).

Первый индикатор я успешно спалил, перепутав питание (попался на глаза вариант распайки под другой индикатор - читайте примечание на Википедии в статье выше), поэтому был куплен еще один WH1602B-NYG-CT и в дополнение - упомянутый L2432.

Подключение

На материнской плате плеера обнаружилась гребенка LPT-порта, поэтому задача упрощалась и покупать USB-LPT-переходник не пришлось. За основу была взята эта схема, исключая цепи подсветки (диод с резистором, транзистор и подстроечник на 16 контакте индикатора). Разводка индикатора соответствует той, что была показана в таблице на Википедии:

  • шина данных LPT-порта цепляется один к одному на DB-контакты индикатора
  • 1@LPT -> 6@LCD
  • 14@LPT -> 5@LCD
  • 16@LPT -> 4@LCD
  • 1@LCD - общий, 2 - +5В, 3 - регулировка контраста.

Соединять кучу контактов на LPT на общий провод не обязательно. Для запитки можно взять, например, питание с внутреннего USB-порта (1 и 4 контакты, если кто не в курсе :) ).

Чем выше напряжение на входе контрастности - тем ниже контрастность. В финальной конструкции подстроечник был заменен на делитель из двух резисторов на 9,1 К (к +) и 1,5 К (к 0).

Софт

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

Поставил lcd4linux, создал более-менее пристойный конфиг на базе того, что нашел в интернете; попробовал прописывать разные доступные драйверы. Попутно пытался заставить работать параллельный порт. С портом помогла эта статья. В двух словах суть: нужно выгрузить модуль lp, а загрузить parport_pc и ppdev.

Однако индикатор в лучшем случае реагировал закрашиванием произвольных блоков.

Поставил LCDproc. При настройке помогла эта статья. Поначалу индикатор так же само отказывался работать, однако в конфиге /etc/LCDd.conf в разделе hd44780 мне не понравился ConnectionType - дефолтно он был, кажется, 4bit, а линий у меня 8 (с 8bit тоже не заработало). В общем, полез в ман в поисках других вариантов и в итоге перепробовал для hd4470 все, что не относилось к USB и последовательному подключению. И на типе "winamp" индикатор внезапно заработал. Команды из статьи вывели апплеты на индикатор - в общем, все ок.

Мне от индикатора требовалась возможность выводить произвольную строку в произвольный момент времени без переконфигурирования сервисов. В статье предлагалось собрать сторонний модуль ядра lcdmod (который не обновлялся уже много лет), что меня, естественно, не устраивало. Поэтому вернулся к попыткам запуска под lcd4linux, однако теперь уже знал, в какую сторону копать.

Изучение /usr/share/doc/lcd4linux/lcd4linux.conf.sample.gz показало, что там описан аналогичный пример winamp-типа. Правильнее сказать - разводки на LPT-порт, ведь, фактически, вся разница была в указании соответствия сигналов LCD-индикатора сигналам LPT-порта (иначе говоря, если сделать "правильную" разводку, соответствующую дефолтным настройкам lcd4linux - ничего и указывать не придется).

Составил простой конфиг по найденным в интернете примерам - все работает. Описание конфига lcd4linux.conf чем-то напоминает конфиг X-сервера: описываются дисплеи, виджеты, вид экрана и переменные, а потом это все собирается в одну кучу указанием нужного вида для нужного дисплея.

Для отображения произвольной строки было решено использовать fifo-сокеты - со стороны lcd4linux был сделан апплет, читающий информацию с сокета с некоторой периодичностью, а клиент должен был отправлять нужную строку в сокет. Так как строки 2, а нужно было независимо менять их содержимое (да и вывести одной командой информацию сразу на две строки я не знаю как - возможно, надо специальным образом описать виджет), то было сделано 2 виджета, каждый со своим сокетом.

Стоит заметить, что одним из основных режимов вывода на индикатор был вывод информации о проигрываемом треке. Так как индикатор небольшой, то логичным было использовать бегущую строку. lcd4linux имеет встроенные средства для организации бегущей строки (align "M" и указание скорости прокрутки через speed в настройке виджета), но тогда бы не получилось на той же строке выводить какую-то статичную информацию - например, шкалу громкости. В итоге пришлось использовать только статический вывод, а бегущую строку эмулировать в скрипте.

Итоговый конфиг lcd4linux.conf получился такой:


Display HD44780 {
    Driver 'HD44780'
    Model 'generic'
    Port '/dev/parport0'
    Size '16x2'
    Wire {
        RW      'AUTOFD'
        RS      'INIT'
        ENABLE  'STROBE'
    }
}

Widget String1 {
    class      'Text'
    expression exec('[ ! -e /tmp/fifo1 ] && mkfifo -m 666 /tmp/fifo1 || cat /tmp/fifo1')
    width       16
    align      'L'
    update      tick
}

Widget String2 {
    class      'Text'
    expression exec('[ ! -e /tmp/fifo2 ] && mkfifo -m 666 /tmp/fifo2 || cat /tmp/fifo2')
    width      16
    align      'L'
    update      tick
}

Layout Default {
    Row1 {
        Col1 'String1'
    }
    Row2 {
        Col1 'String2'
    }
}

Variables {
   tick 500
}

Display 'HD44780'
Layout  'Default'

Как видите, ничего сложного. Описывается дисплей, где указывается размер дисплея, драйвер и порт, а также та самая разводка проводов. Описываются 2 виджета с проверкой на существование fifo-сокета и создание его при необходимости (да, права 666 не сильно секьюрные, но плеер - однопользовательская система и там никто не будет печатать мне маты на дисплее или делать что-то подобное :) ), описывается размещение виджетов на дисплее, описываются переменные, после чего для заданного дисплея указывается заданное размещение виджетов.

Теперь отправляя на /tmp/fifo1 или /tmp/fifo2 нужную строку, можно увидеть ее на экране.

Поддержка кириллицы

Все бы хорошо, однако при попытке отправить что-то, написанное на русском языке, на индикаторе я получал кракозяблики. Однако индикатор должен поддерживать русский (о чем свидетельствует суффикс CT в обозначении). При поиске решения были опробованы разные варианты, в первую очередь

expression iconv('UTF-8', 'KOI8-R', 'текст ')

увиденный в этой статье. Однако это был не KOI8-R, да и другие кодировки не подходили. Зато попался на глаза самописный транслятор, где русские буквы заменялись напрямую на коды. Попробовал отправить один из кодов - получил символ. Не тот, что в том трансляторе, но хоть что-то. Набросал простой скрипт вида

for i in {0..7}
 do
  for j in {0..7}
   do
    for k in {0..7}
     do
      echo -e "\\0$i$j$k $i$j$k" > /tmp/fifo1
      echo 0$i$j$k
      read var
      echo 0$i$j$k $var >> wh1602
    done
  done
done

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

Непрерывного списка русских букв там не было, были только отличающиеся и начинались они с 240-го кода; остальное надо было преобразовывать в выглядящие так же латинские буквы.

Полученный список был преобразован в читаемый sed'ом вариант, попутно отсеяны совсем уж экзотические символы:

wh1602.sed
s@А@A@g
s@В@B@g
s@С@C@g
s@Е@E@g
s@Н@H@g
s@К@K@g
s@М@M@g
s@О@O@g
s@Р@P@g
s@Т@T@g
s@Х@X@g
s@а@a@g
s@с@c@g
s@е@e@g
s@о@o@g
s@р@p@g
s@х@x@g
s@Б@\\0240@g
s@Г@\\0241@g
s@Ё@\\0242@g
s@Ж@\\0243@g
s@З@\\0244@g
s@И@\\0245@g
s@Й@\\0246@g
s@Л@\\0247@g
s@П@\\0250@g
s@У@\\0251@g
s@Ф@\\0252@g
s@Ч@\\0253@g
s@Ш@\\0254@g
s@Ъ@\\0255@g
s@Ы@\\0256@g
s@Э@\\0257@g
s@Ю@\\0260@g
s@Я@\\0261@g
s@б@\\0262@g
s@в@\\0263@g
s@г@\\0264@g
s@ё@\\0265@g
s@ж@\\0266@g
s@з@\\0267@g
s@и@\\0270@g
s@й@\\0271@g
s@к@\\0272@g
s@л@\\0273@g
s@м@\\0274@g
s@н@\\0275@g
s@п@\\0276@g
s@т@\\0277@g
s@ч@\\0300@g
s@ш@\\0301@g
s@ъ@\\0302@g
s@ы@\\0303@g
s@ь@\\0304@g
s@э@\\0305@g
s@ю@\\0306@g
s@я@\\0307@g
s@«@\\0310@g
s@»@\\0311@g
s@„@\\0312@g
s@”@\\0313@g
s@№@\\0314@g
s@£@\\0317@g
s@×@\\0325@g
s@↑@\\0331@g
s@↓@\\0332@g
s@Д@\\0340@g
s@Ц@\\0341@g
s@Щ@\\0342@g
s@д@\\0343@g
s@ф@\\0344@g
s@ц@\\0345@g
s@щ@\\0346@g
s@é@\\0352@g
s@ç@\\0353@g

Для доработки остается еще замена украинских символов, некоторых юникодных (например, степени) а также диакритики.

Теперь командой вида

echo -e "$(echo "Текст" | sed -f /home/rain/wh1602.sed)" > /tmp/fifo1

можно было печатать и русские слова.

Кстати, упомянутый выше L2432 в знакогенераторе имеет немного больше различных юникодных символов, а вместо русского - китайские иероглифы.

Управляющий скрипт

От индикатора хотелось 3 режима:

  1. Верхняя строка - текущее время трека, потом режим плеера (Play, Pause, Stop), потом пусть будет режим работы карты (например, 24/192). Нижняя строка - бегущая; исполнитель и название трека, взятые из Music On Console. Соответственно, обновление всего этого дела должно быть минимум раз в полсекунды
  2. При регулировке громкости верхняя строка должна заменяться на "Громкость" и уровень громкости в процентах. Нижняя строка - шкала громкости
  3. При перемотке трека сверху отображать текущую позицию, снизу - шкалу позиции на треке
  • Пункты 2 и 3 после отпускания кнопки должны возвращаться к пункту 1 секунд через 5.

Соответственно, был необходим некий сервис - связующее звено между компонентами плеера и lcd4linux. Через некоторое время такой скрипт был написан:


#!/bin/bash

# Установка начальных значений внутренних переменных
clock='0.5' # Скорость работы внутреннего цикла
l='16' # Длина индикатора

a='0' # Инициализация значения переменной бегущей строки
vtime='0' # Инициализация значение таймера показа громкости
rtime='0' # Инициализация значение таймера показа перемотки

showvol='20' # *0.5 sec - время показа экрана регулировки громкости
showtrack='20' # *0.5 sec - время показа экрана перемотки трека

# Файлы-маркеры
volfile='/tmp/volchange' # При наличии файла показывается экран регулировки громкости
rewfile='/tmp/rewind' # При наличии файла показывается экран 
nextfile='/tmp/nextsong' # При наличии файла обновляется информация о треке и цикл бегущей строки начинается заново

# Название микшера
mixer='PCM,0'

# Номер карты
card='1'

# Файл состояния карты в procfs
cardstate='/proc/asound/card1/pcm0p/sub0/hw_params'
#cardstate='/proc/asound/card0/pcm0p/sub0/hw_params'

# Сокет первой строки
str1sock='/tmp/fifo1'
# Сокет второй строки
str2sock='/tmp/fifo2'

# Функция трансляции кириллицы и прочих символов
cyrconv() {
        echo -e "$(cat | sed -f /home/rain/wh1602.sed)"
}

# Ждем, пока не запустится lcd4linux
while [ ! -p "${str1sock}" ] ; do sleep 0.1 ; done

# Пишем приветствие
echo "*  Плеер готов *" | cyrconv > "${str1sock}"
echo "*   к  работе  *" | cyrconv > "${str2sock}"

sleep 5

# Главный цикл
while sleep "${clock}"
        do
# Проверяем значение переменной экрана громкости
                if [ "${vtime}" != '0' ]
                        then
# Получаем текущее значение уровня громкости
                                vol="$(amixer -c "${card}" sget "${mixer}" | awk '/^ +Front/{gsub(/\[|\]|\%/, "", $5) ; print $5 ; exit}')"

# Выводим первую строку
                                echo "Громкость:  ${vol}%" | cyrconv > "${str1sock}"
# Вычисляем и рисуем нужное число блоков во второй строке
                                for i in $(seq 1 $((${vol}*${l}/100)))
                                                do
                                                        echo -n '\0777'
                                                done | cyrconv > "${str2sock}"

                                let vtime-=1

# Проверяем значение переменной перемотки
                elif [ "${rtime}" != '0' ]
                        then
# Получаем значения текущего времени и состояния плеера
                                times="$(mocp -i 2>/dev/null | grep -E 'Time|Sec|State')"

# Рисуем первую строку - текущую позицию и общее время звучания трека
                                mawk '
                                /^CurrentTime:/{
                                        ct=$2
                                }

                                /^TotalTime:/{
                                        tt=$2
                                } END {print ct, "    ", tt}' <<< "${times}" | cyrconv > "${str1sock}"

# Вычисляем и рисуем шкалу прогресса
# Если не проверять состояние плеера и не печатать 0 для seq - можно загнать скрипт в бесконечный цикл с поеданием памяти
                                for i in $(seq 1 $(mawk '
                                        /^CurrentSec:/{
                                                cs=$2
                                        }

                                        /^TotalSec:/{
                                                ts=$2
                                        }

                                        /^State:/{
                                                state=$2
                                        } END {
                                                OFMT="%.0f"
                                                if (state=="STOP") print 0
                                                else print cs/ts*"'$l'"
                                        }' <<< "${times}"))
                                do
                                        echo -n '\0777'
                                done | cyrconv > "${str2sock}"

                                let rtime-=1

# Проверяем, не изменилась ли громкость
                elif [ -e "${volfile}" ]
                        then
                        vtime="${showvol}"
                        rm -f "${volfile}"

# Проверяем, не перематывается ли трек
                elif [ -e "${rewfile}" ]
                        then
                        rtime="${showtrack}"
                        rm -f "${rewfile}"

# Проверяем, нет ли переключения на следующий трек
                elif [ -e "${nextfile}" ]
                        then
# Получаем теги трека
                        tags="                $(mocp -i 2>/dev/null | mawk '/^Title:/{gsub(/^Title: /, ""); gsub(/^([0-9]+)/, "&."); print}')               "
                        a=0
                        rm -f "${nextfile}"

                else
# Иногда из-за переходных процессов в переменной оказывалось непонятно что
# Проверяем наличие полезной информации, если нет - перезапускаем бегущую строку
                        [ "${#tags}" -lt '10' ] && touch "${nextfile}"

# Обрабатываем сразу 2 файла для уменьшения числа операций
# Обрабатываем состояние карты и информацию о состоянии и текущей позиции плеера
                        data="$(mocp -i 2>/dev/null | mawk '
                                /^State:/{
                                        state=$2
                                }

                                /^CurrentTime:/{
                                        time=$2
                                }

                                /^format:/{
                                        gsub(/S|_|LE|BE/, "", $2)
                                        format=$2
                                }

                                /^rate:/{
                                        rate=$2/1000
                                }

                                END {
                                        OFMT="%.0f"
                                        OFS=""

                                        if (state=="PLAY") print time, "  >  ", format, "/", rate
                                        else if (state=="PAUSE") print time, "      ПАУЗА"
                                        else {
                                                print "Плеер остановлен"
                                                print "" > "/tmp/nextsong"
                                                }
                                }' - "${cardstate}")"

# Пришлось сделать так, чтобы индикатор не "мигал" верхней строкой в некоторые моменты времени
                                echo "${data}" | cyrconv > "${str1sock}"

# Бегущая строка
                                [[ "$a" -le "$((${#tags}-$l))" ]] && { echo -e "${tags:$a:$l}" | cyrconv > "${str2sock}" ; let a+=1 ; }  || a='0'

                fi

done

Для создания файлов-маркеров необходимо немного доработать конфиги Music On Console и lircrc (настройка последнего описывалась здесь).

  • В MOCp надо в ~/.moc/config в OnSongChange задать команду touch /tmp/nextsong
  • В lircrc надо в /etc/lirc/lircrc привести соответствующие блоки примерно к такому виду:
begin
prog = irexec
button = KEY_VOLUMEUP
repeat = 1
config = su -l rain -c 'touch /tmp/volchange ; mocp -v +2'
end

begin
prog = irexec                                                                                                                                                     
button = KEY_VOLUMEDOWN                                                                                                                                           
repeat = 1
config = su -l rain -c 'touch /tmp/volchange ; mocp -v -2'
end


begin
prog = irexec
button = KEY_REWIND
repeat = 1
config = su -l rain -c 'touch /tmp/rewind ; mocp --seek -5'
end

begin
prog = irexec
button = KEY_FORWARD
repeat = 1
config = su -l rain -c 'touch /tmp/rewind ; mocp --seek +5'
end

Далее скрипт надо прописать в cron, чтобы он стартовал вместе с системой. У меня это сделано так:

rain@player:~$ crontab -l | grep lcd
@reboot /home/rain/.lcd4linux/lcd.sh

Плюс в домашнем каталоге (или там, где это будет объявлено в функции в начале скрипта) должен находиться скрипт для sed с трансляцией символов.

Ссылки