# 樹莓派與紅外線遙控器

## 一、利用remote-ssh extension打造遠端開發環境

先前有介紹使用VS Code遠端開發Django，當時使用的方法有點土法練鋼，現在微軟提供非常簡易的remote-ssh擴充套件，可以讓VS Code很方便的與遠端樹莓派整合在一起，利用前端電腦的強大處理能力去協同開發後端樹莓派的Python應用。

在介紹紅外線的接收與發射前，讓我們先來打造一個Python遠端開發的樹莓派環境。

{% embed url="<https://youtu.be/-2-_8vnFxXc>" %}

## 二、打開支援核心

現今的Linux核心已可直接支援紅外線遙控器的收發，只要將它打開即可，非常方便使用。

{% embed url="<https://www.youtube.com/watch?v=2kdwYIp4DR8&t=21s>" %}

## 三、萬碼奔騰，自製編碼對應表

紅外線使用的編碼表按鍵名稱列表：<https://peppe8o.com/download/txt/ir-keytable%20available%20keycodes.txt>

由於遙控器的廠牌太多種類，甚至是同廠牌也有不同型號的遙控器，其編碼也不一定會完全一致，因此，學習自製編碼對應表，是常遇到的課題。

{% embed url="<https://youtu.be/49hVGNcNaXo>" %}

## 四、控制VLC播放音樂及LED閃爍

有了前面的基礎後，就可以利用python-evdev模組，來讀取遙控器資料並判斷是哪個按鈕被按下，進行相對應的程式處理。同時，由於VLC於背景播放，可使用psutil模組，來處理結束播放事宜。這二個模組功能是相當強大的，值得好好研究與學習。官網說明文件如下：

{% embed url="<https://python-evdev.readthedocs.io/en/latest/tutorial.html>" %}

{% embed url="<https://psutil.readthedocs.io/en/latest/#processes>" %}

{% embed url="<https://youtu.be/m7LwtYC4MHY>" %}

```python
# ir_read_nomessage.py
from evdev import InputDevice, list_devices, categorize, ecodes

def get_ir_device():
    devices = [InputDevice(path) for path in list_devices()]
    for device in devices:
        print(device.path, device.name, device.phys)
        if 'ir_recv' in device.name:
            return device
        else:
            device.close()

dev = get_ir_device()
try:
    for event in dev.read_loop():
        if event.type == ecodes.EV_KEY:
            if event.value == 1:
                if ecodes.KEY[event.code] == 'KEY_1':
                    print('press 1')
                elif ecodes.KEY[event.code] == 'KEY_2':
                    print('press 2')
                elif ecodes.KEY[event.code] == 'KEY_3':
                    print('press 3')
                    dev.close()
                    quit()
except KeyboardInterrupt:
    print('press ctrl-c')
    dev.close()
    quit()
```

```python
# vlc_id.py
import psutil
# 方法一：先取得所有的process id (PID)，再找出vlc的PID，找到就kill離開
# process_ids = psutil.pids()
# for pid in process_ids:
#     p = psutil.Process(pid)
#     if 'vlc' in p.name():
#         print(p.name())
#         print(p.pid)
#         p.kill()
#         break

# 方法二：透過iter的方式，一個個找，找到就kill離開
for proc in psutil.process_iter(['pid', 'name']):
    if 'vlc' in proc.info['name']:
        print(proc.info['pid'])
        p = psutil.Process(proc.info['pid'])
        p.kill()
        break
```

```python
from evdev import InputDevice, list_devices, categorize, ecodes
from gpiozero import LED
import subprocess
import psutil
led = LED(27)
led_on = False
vlc_on = False

def get_ir_device():
    devices = [InputDevice(path) for path in list_devices()]
    for device in devices:
        print(device.path, device.name, device.phys)
        if 'ir_recv' in device.name:
            return device
        else:
            device.close()

def stop_vlc():
    for proc in psutil.process_iter(['pid', 'name']):
        if 'vlc' in proc.info['name']:
            print(proc.info['pid'])
            p = psutil.Process(proc.info['pid'])
            p.kill()
            return

dev = get_ir_device()
try:
    for event in dev.read_loop():
        if event.type == ecodes.EV_KEY and event.value == 1:
            if ecodes.KEY[event.code] == 'KEY_1':
                # 控制VLC播放音樂
                if vlc_on:
                    # 利用PID停止播放
                    print('停止播放音樂')
                    stop_vlc()
                else:
                    print('開始播放音樂')
                    subprocess.run(["cvlc", "-LZ", "/home/pi/Music"])
                vlc_on = not vlc_on
            elif ecodes.KEY[event.code] == 'KEY_2':
                # 控制LED亮與暗
                if led_on:
                    led.off()
                else:
                    led.blink()
                led_on = not led_on
            elif ecodes.KEY[event.code] == 'KEY_3':
                print('離開程式')
                dev.close()
                quit()
except KeyboardInterrupt:
    print('按鍵Ctrl+c中斷程式')
    dev.close()
    quit()
```

## 五、發射紅外線訊號

先利用evtest來檢視鍵盤訊號值，再利用ir\_ctl測試，依據scancode發射紅外線訊號給接收器，最後撰寫程式來完成工作。

{% embed url="<https://youtu.be/12JclhUL0iE>" %}

```python
from evdev import InputDevice, list_devices, categorize, ecodes
from configparser import ConfigParser
from collections import defaultdict
from time import sleep
import os

config = ConfigParser()

config.read(os.path.expanduser('~/lien.toml'))
# 列出所有的設定section
# print(keymap.sections())
scancodes = config['protocols.scancodes']
keymaps = defaultdict()
# 原先格式是
# [protocols.scancodes]
#　0x41ba09 = "KEY_1"
# 轉成 "KEY_1" = 0x41ba09
# 並且將 " 字符除去（利用replace），如此才可以使用keymaps['KEY_1']
# 如果不去除，就必須要用keymaps['"KEY_1"']，此用法容易混淆
for s in scancodes:
    keymaps[scancodes[s].replace('"','')] = s

dev = InputDevice('/dev/input/event0')
try:
    for event in dev.read_loop():
        if event.type == ecodes.EV_KEY and event.value == 1:
            if event.code == ecodes.KEY_A:
                print('按鍵 A')
                command = f"ir-ctl -S necx:{keymaps['KEY_1']} -d /dev/lirc0"
                os.system(command)
            elif event.code == ecodes.KEY_B:
                print('按鍵　B')
                command = f"ir-ctl -S necx:{keymaps['KEY_2']} -d /dev/lirc0"
                os.system(command)
            elif event.code == ecodes.KEY_C:
                print('結束程式')
                dev.close()
                break
except KeyboardInterrupt:
    dev.close()
    quit()
```
