Пульт ДУ (Haskell + Arduino)

Проблема: я хочу переключать музыку, которую играет mplayer лежа на диване. 
Имеется: компьютер под NixOS, Arduino, макетка, три провода, ИК диод TSOP312. 
Дисклеймер: делать я это буду максимально тупым способом из возможных.


Настройка системы

Устанавливаем необходимые пакеты
nix-env -iA nixos.arduino
nix-env -iA nixos.xdotool
nix-env -iA nixos.mplayer

Для нормальной работы с серийным портом нам необходимо добавить себя в группу dialout. На нормальных дистрибутивах это делается утилитой usermod, здесь же мы подкоректируем наш /etc/nixos/configuration.nix
users.extraUsers.nik.extraGroups = [“dialout”]
После этого перегружаем систему 
nixos-rebuild switch
reboot

Я использую оконный менеджер xmonad, а Arduino IDE написана на джава, у которой странные родственные связи между окнами. Так что для нормальной работы отображения необходимо подправить xmonad.hs:

import XMonad.Util.EZConfig
import XMonad.Hooks.EwmhDesktops
import XMonad.Hooks.SetWMName

main = xmonad $ def {

, startupHook = setWMName “LG3D”

И перезагрузить xmonad, то есть нажатьMOD+q, Где MOD это ваша клавиша-модификатор (в моем случае это Win).


Arduino

Соберем нашу схему:

Теперь запускаем arduino и заходим в Sketch/Include Library/Manage Libraries… , находим IRremote и обновляем его до последней версии. Подключаем ардуино и загружаем в него следующий код:

#include <IRremote.h>
int RECV_PIN = 11;
IRrecv irrecv(RECV_PIN);
decode_results res;
void setup()
{
Serial.begin(9600);
irrecv.enableIRIn();
Serial.println("START");
}
void loop() {
if (irrecv.decode(&res)) {
Serial.println(res.value, HEX);
irrecv.resume();
}
}

Теперь из вывода Serial Monitor’а выясним коды нужных нам клавиш и подправим наш код соответсвенно:

#include <IRremote.h>
int RECV_PIN = 11;
IRrecv irrecv(RECV_PIN);
decode_results res;
void setup()
{
Serial.begin(9600);
irrecv.enableIRIn();
Serial.println("START");
}
void loop() {
if (irrecv.decode(&res)) {
if(res.value == 0x808E817){
Serial.write("UV");
}
else if(res.value == 0x8086897){
Serial.write("LV");
}
else if(res.value == 0x808F807){
Serial.write("MV");
}
else if(res.value == 0x80820DF){
Serial.write("PP");
}
else if(res.value == 0x808E01F){
Serial.write("LF");
}
else if(res.value == 0x808609F){
Serial.write("LB");
}
else if(res.value == 0x8088877){
Serial.write("BF");
}
else if(res.value == 0x80808F7){
Serial.write("BB");
}
irrecv.resume();
}
}

Haskell

Перейдем к клиенту. 
mkdir pult
touch Main.hs

Редактируем Main.hs

{-# LANGUAGE OverloadedStrings #-}

import qualified Data.ByteString.Char8 as B
import System.Hardware.Serialport
import Control.Monad
import System.Process


main :: IO ()
main = do
let port = "/dev/ttyUSB0"
s <- openSerial port defaultSerialSettings { commSpeed = CS9600 }
mainLoop s

mainLoop s = do
r <- recv s 2
callCommand $ case r of
"LV" -> "amixer set Master 2-"
"UV" -> "amixer set Master 2+"
"MV" -> "amixer set Master toggle"
"LF" -> "xdotool key Right"
"LB" -> "xdotool key Left"
"BF" -> "xdotool key Up"
"BB" -> "xdotool key Down"
"PP" -> "xdotool key space"
_ -> "true"
mainLoop s

Теперь настроим наш проект под cabal. 
cabal init
Выбираем вариант 2) Executable когда спросят
Обновляем build-dependsв файле pult.cabal:

  build-depends: base >=4.9 && <4.10
, serialport
, bytestring
, process

Собираем проект и запускаем программу
cabal sandbox init
cabal install -j
./.cabal-sandbox/bin/pult2

Теперь можно открыть в соседнем окне mplayer, и эмулировать нажатия кнопок при помощи пульта лежа на диване.

Like what you read? Give Nick Yurchenko a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.