ПРЕДУПРЕЖДЕНИЕ: Весь код, встреченный внизу является говнокодом чуть более, чем полностью, я такое в восьмом классе писал в Borland C++ Builder. Надо будет шесть раз с нуля переписать.
Сижу я дома, никого не трогаю, передо мной лежит свеженькая arduino. Что делать? Надо к ней что-то присобачить!
Немного поразмыслив, поняв, что у меня нет ИК-фотодиода под рукой, вспомнил, что у меня есть лишние геймпады от денди.
Хорошо. Посматриваем мануальчики к ардуино, и, параллельно ищем спеки к геймпадам. Быстро узнаём, что это дело питается от +5В, и уровни у него - 0В и +5В. Это очень хорошо, ибо у ардуинки - так же, и не надо ничего ставить дополнительно.
Довольно быстро удаётся найти следующий ман:http://bit16.ru/index.php?gl=jpad&act=1, откуда мы берём распиновочку.
Распиновочка есть, а вот спеки найти было сложнее. Но, через полчаса где-то , находим следующий ман: http://www.parallax.com/Portals/0/Downloads/docs/prod/acc/32368-NesGamepadCtrlrAdapter-v1.0.pdf
Этот ман описывает контроллер для подключения двух геймпадов NES. Нам это, конечно, нафиг не надо, но там есть этот прекрасный параграф:
Возможно, это будет читать кто-то, кто не знает инглиш, так что расскажу, как это работает. Заодно, добавлю больше букв в бложек. Когда геймпад принимает импульс на LATCH, он "запоминает" комбинацию нажатых клавиш, и шлёт нам бит, показывающий, нажата ли кнопка "A", или нет. Затем, мы шлём семь импульсов на CLK и, соответсвенно получаем ещё семь бит, для семи оставшихся кнопок. В общем-то, всё крайне понятно при первом взгляде на график.
Всё подцепил. Написал простейшую программку. Не работает. Полчаса мучений. Потом понимаю, что подрубил нерабочий геймпад. Подрубаю нужный - всё ок!
Моя программка для arduino
А теперь начинается самое ужасное! Один день я истратил на гугление, как же получить данные из последовательного устройства. На следующий день, решил спросить на.. лоре. И правильно сделал! Мне подсказали реально рабочий способ. Выглядит он вот так вот:
Но... не рабоатет. Тот же няшный чувак с лора, предположил, что мой говнодевайс шлёт данные не в формате вроде 128\n, а в формате 128\r\n. Смотрим в hexdump - он прав!
Далее идут попытки заткнуть весь поток в dos2unix, в hd| xxd -r |dos2unix, у кого-то работает, у меня - нет. Пробовал я ненужные символы порезать сдвигами, но у нас ведь bash! Тут всё, не как у людей. Сдвиги тут работают только для чисел. Пробовал убрать последние символы стандартными средствами работы со строками - но и они нихрена не работают.
В конечном итоге решил попробовать отдельно каждую строку совать в dos2unix. И это - заработало. Ещё попробовал отдельно каждую строку совать в xxd -p - получаем hex вместе с символами перевода. Засунуть поток в xxd -p не вышло, ибо после второй строки подряд у меня получается ошибка xxd: cannot seek backwarkds. Так и не понял, к чему это. Если у вас - заработает, вы - молодец.
Вариант с xxd -p и последующим сравнением полученных кодов с кодами, которые мы запомнили работал субъективно быстрее (да, моё говно на bash жрёт ~2% моего ебаного проца), так что я оставил его.
Получается что-то вроде
Заводим переменную, отслеживающую, сколько опросов подряд кнопка остаётся нажатой.
Смотрим коды, сравниваем, выполняем нужные команды.
Ну, все пользователи exaile давно осилили qdbus, у mplayer'а - сложнее, но гуглится на раз (http://ubuntuforums.org/showthread.php?t=1629000).
Сижу я дома, никого не трогаю, передо мной лежит свеженькая arduino. Что делать? Надо к ней что-то присобачить!
Немного поразмыслив, поняв, что у меня нет ИК-фотодиода под рукой, вспомнил, что у меня есть лишние геймпады от денди.
Хорошо. Посматриваем мануальчики к ардуино, и, параллельно ищем спеки к геймпадам. Быстро узнаём, что это дело питается от +5В, и уровни у него - 0В и +5В. Это очень хорошо, ибо у ардуинки - так же, и не надо ничего ставить дополнительно.
Довольно быстро удаётся найти следующий ман:http://bit16.ru/index.php?gl=jpad&act=1, откуда мы берём распиновочку.
Распиновочка есть, а вот спеки найти было сложнее. Но, через полчаса где-то , находим следующий ман: http://www.parallax.com/Portals/0/Downloads/docs/prod/acc/32368-NesGamepadCtrlrAdapter-v1.0.pdf
Этот ман описывает контроллер для подключения двух геймпадов NES. Нам это, конечно, нафиг не надо, но там есть этот прекрасный параграф:
Возможно, это будет читать кто-то, кто не знает инглиш, так что расскажу, как это работает. Заодно, добавлю больше букв в бложек. Когда геймпад принимает импульс на LATCH, он "запоминает" комбинацию нажатых клавиш, и шлёт нам бит, показывающий, нажата ли кнопка "A", или нет. Затем, мы шлём семь импульсов на CLK и, соответсвенно получаем ещё семь бит, для семи оставшихся кнопок. В общем-то, всё крайне понятно при первом взгляде на график.
Всё подцепил. Написал простейшую программку. Не работает. Полчаса мучений. Потом понимаю, что подрубил нерабочий геймпад. Подрубаю нужный - всё ок!
Моя программка для arduino
#define CLKDELAY 1 #define FREQ 30 int data = 13; int clock = 12; int latch = 11; //выбираем пины, куда втыкать наши проводки int code; int result; boolean a; boolean b; boolean ta; boolean tb; boolean select; boolean start; boolean up; boolean down; boolean left; boolean right; void setup() { //выполняется один раз. pinMode(data, INPUT);//включаем пин данных на вход pinMode(clock, OUTPUT);//на эти пины мы шлём импульсы, pinMode(latch, OUTPUT);//так что это - выходы digitalWrite(latch, LOW);//для начала на них digitalWrite(clock, LOW);//пустим низкий уровень Serial.begin(9600);//частота, на которой работает USB. Нам пока хватит. } void loop() { //шлём импульс на LATCH, снимаем A digitalWrite(latch, HIGH); delay(CLKDELAY); a = digitalRead(data); digitalWrite(latch, LOW); //Дальше шлём импульсы на CLK и снимаем остальные данные //1 delay(CLKDELAY); digitalWrite(clock, HIGH); delay(CLKDELAY); b = digitalRead(data); digitalWrite(clock, LOW); delay(CLKDELAY); //2 digitalWrite(clock, HIGH); delay(CLKDELAY); select = digitalRead(data); digitalWrite(clock, LOW); //3 delay(CLKDELAY); digitalWrite(clock, HIGH); start = digitalRead(data); delay(CLKDELAY); digitalWrite(clock, LOW); //4 delay(CLKDELAY); digitalWrite(clock, HIGH); delay(CLKDELAY); up = digitalRead(data); digitalWrite(clock, LOW); //5 delay(CLKDELAY); digitalWrite(clock, HIGH); delay(CLKDELAY); down = digitalRead(data); digitalWrite(clock, LOW); //6 delay(CLKDELAY); digitalWrite(clock, HIGH); delay(CLKDELAY); left = digitalRead(data); digitalWrite(clock, LOW); //7 delay(CLKDELAY); digitalWrite(clock, HIGH); delay(CLKDELAY); right = digitalRead(data); digitalWrite(clock, LOW); delay(FREQ); //задержка между опросами result = 0; result << 7; //на будущее if (!a) result++; result=result<<1; if (!b) result++; result=result<<1; if(!select) result++; result=result<<1; if(!start) result++; result=result<<1; if(!up) result++; result=result<<1; if(!down) result++; result=result<<1; if(!left) result++; result=result<<1; if(!right) result++; Serial.println(result, DEC);//ебашим результат в универсальный последовательный автобус //В десятичной системе - потому, что нам - похуй. Потом, когда посмотрете код на bash, поймёте. }
Пара слов о программке. Она выводит 16битное число, где первые 7 бит - нули (потом собираюсь к ардуинке ещё много чего присобачить, так что с запасом). Затем идут последовательно биты для каждого пина. Так как выход с геймпада - инверсный, добавляем инверсию в if. Ну а так, всё понятно любому школьнику.
Итак, на тот момент я мог сделать cat /dev/ttyACM0 и увидеть кодыА теперь начинается самое ужасное! Один день я истратил на гугление, как же получить данные из последовательного устройства. На следующий день, решил спросить на.. лоре. И правильно сделал! Мне подсказали реально рабочий способ. Выглядит он вот так вот:
while read value do #Чозахочешь done < deviceНу либо пайпом.
Но... не рабоатет. Тот же няшный чувак с лора, предположил, что мой говнодевайс шлёт данные не в формате вроде 128\n, а в формате 128\r\n. Смотрим в hexdump - он прав!
Далее идут попытки заткнуть весь поток в dos2unix, в hd| xxd -r |dos2unix, у кого-то работает, у меня - нет. Пробовал я ненужные символы порезать сдвигами, но у нас ведь bash! Тут всё, не как у людей. Сдвиги тут работают только для чисел. Пробовал убрать последние символы стандартными средствами работы со строками - но и они нихрена не работают.
В конечном итоге решил попробовать отдельно каждую строку совать в dos2unix. И это - заработало. Ещё попробовал отдельно каждую строку совать в xxd -p - получаем hex вместе с символами перевода. Засунуть поток в xxd -p не вышло, ибо после второй строки подряд у меня получается ошибка xxd: cannot seek backwarkds. Так и не понял, к чему это. Если у вас - заработает, вы - молодец.
Вариант с xxd -p и последующим сравнением полученных кодов с кодами, которые мы запомнили работал субъективно быстрее (да, моё говно на bash жрёт ~2% моего ебаного проца), так что я оставил его.
Получается что-то вроде
while read number do hex=`echo $number | xxd -p` case $hex in 320d0a) #влево action;; 310d0a) #вправо action;; 33320d0a) #вправо+select action;; 33340d0a) #влево+select action;; esac done < /dev/ttyACM0Затем понимаем, что часть команд должна повторяться при длительном нажатии на кнопку, например, перемотка, или задержка субтитров, а часть - нет, например play/pause.
Заводим переменную, отслеживающую, сколько опросов подряд кнопка остаётся нажатой.
Смотрим коды, сравниваем, выполняем нужные команды.
Ну, все пользователи exaile давно осилили qdbus, у mplayer'а - сложнее, но гуглится на раз (http://ubuntuforums.org/showthread.php?t=1629000).
mkfifo /tmp/mplayer-control mplayer -slave -input file=/tmp/mplayer-control /path/to/some/file/to/play
Затем в эту очередь кидаем команды, например,
echo "pause" > /tmp/mplayer-control echo "quit" > /tmp/mplayer-control
Список команд есть здесь: http://www.mplayerhq.hu/DOCS/tech/slave.txt
Также, неплохо бы управлять громкостью. Тут нам поможет вот этот ОТЛИЧНЫЙ СКРИПТ: http://pastebin.com/F1tM6R2J.
Небольшое лирическое отступление. Думаю, всем понятно, что если ничего не нажато, то делать всякие там сравнения нафиг не надо. Поэтому в начало цикла чтения надо добавить вот такое:
Ещё, у меня есть функция, которая смотрит, запущен ли mplayer, чтобы понять, управлять им, или exaile. Приводить отдельно не буду, потом увидите.
Если ардуинка пропала - ждём секунду, и снова проверяем, есть ли она. Ну, а так, вроде всё.Также, неплохо бы управлять громкостью. Тут нам поможет вот этот ОТЛИЧНЫЙ СКРИПТ: http://pastebin.com/F1tM6R2J.
Небольшое лирическое отступление. Думаю, всем понятно, что если ничего не нажато, то делать всякие там сравнения нафиг не надо. Поэтому в начало цикла чтения надо добавить вот такое:
if [ $hex = 300d0a ] then prevkey=300d0a continue fiТакже я отметил, что при многократном включении/выключении arduino, /dev/ttyACM0 может остаться, а ардуинка получит себе /dev/ttyACM1. Так что я решил подстраховаться и добваить следующее:
b=0 for ((a=5; a >=0 ; a--)) do if [ -e /dev/ttyACM$a ] then b=$a break fi doneИ, соответственно, везде обращаемся к девайсу, как /dev/ttyACM$b
Ещё, у меня есть функция, которая смотрит, запущен ли mplayer, чтобы понять, управлять им, или exaile. Приводить отдельно не буду, потом увидите.
Конечный скрипт - держите.
#!/bin/bash mkfifo /tmp/mplayer-control whatruns() #Производит опрос, какие из нужных нам программ, запущены. #Делаю это, ибо хочу управлять разными программами, с помощью одинаковых комбинаций клавиш #Пока тут только mplayer - exaile у меня запущен всегда. { if ps ax |grep mplayer |grep -q slave ; then mplayer=1 else mplayer=0 fi } while true do b=0 for ((a=5; a >=0 ; a--)) do if [ -e /dev/ttyACM$a ] then b=$a break fi done while [ -e /dev/ttyACM$b ] #работаем, пока есть геймпад do whatruns #перед работой проверили, что запущено readcount=0 #Отсчитывает количество опросов геймпада readspressed=0 #Отсчитывает, сколько опросв подряд комбинация остаётся нажатой prevkey=0 #Код предыдущей комбинации while read number do let "readcount+=1" if [ $readcount -ge 50 ] #Каждые 20 опросов then whatruns #проверяем, что запущено readcount=0 fi hex=`echo $number | xxd -p` if [ $hex = 300d0a ] then prevkey=300d0a continue fi if [[ $hex = $prevkey ]] then let "readspressed+=1" else readspressed=0 fi prevkey=$hex #Для тех команд, которые работают один раз при длительном нажатии if [ "$readspressed" -eq 0 ] then case $hex in 31360d0a) #start if [ $mplayer -eq 1 ] then echo "pause" > /tmp/mplayer-control else qdbus org.exaile.Exaile /org/exaile/Exaile org.exaile.Exaile.PlayPause fi ;; 320d0a) #влево if [ $mplayer -eq 0 ] then qdbus org.exaile.Exaile /org/exaile/Exaile org.exaile.Exaile.Prev fi ;; 310d0a) #вправо if [ $mplayer -eq 0 ] then qdbus org.exaile.Exaile /org/exaile/Exaile org.exaile.Exaile.Next fi ;; esac fi #для команд, которые будт срабатывать каждые десять опросов if (( $readspressed != 0 || `expr $readspressed % 10` == 9 )) then case $hex in 320d0a) #влево if [ $mplayer -eq 1 ] then echo "seek -5" > /tmp/mplayer-control fi ;; 310d0a) #вправо if [ $mplayer -eq 1 ] then echo "seek +5" > /tmp/mplayer-control fi ;; 33320d0a) #вправо+select if [ $mplayer -eq 1] then echo "sub_delay +0.1" > /tmp/mplayer-control else r=`qdbus org.exaile.Exaile /Player org.freedesktop.MediaPlayer.PositionGet` let "r+=5000" qdbus org.exaile.Exaile /Player org.freedesktop.MediaPlayer.PositionSet $r fi ;; 33340d0a) #влево+select if [ $mplayer -eq 1 ] then echo "sub_delay -0.1" > /tmp/mplayer-control else r=`qdbus org.exaile.Exaile /Player org.freedesktop.MediaPlayer.PositionGet` let "r-=5000" qdbus org.exaile.Exaile /Player org.freedesktop.MediaPlayer.PositionSet $r fi ;; 380d0a) #вверх volume up;; 340d0a) #вниз volume down ;; esac fi done < /dev/ttyACM$b done sleep 1 doneP.S. Прошу прощения за такую ебаную тему подсветки кода - включил дефолт на скорую руку. Как-нибудь исправлю.