# 樹莓派與紅外線遙控器

## 一、利用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()
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://s761111.gitbook.io/raspi-sensor/shu-mei-pai-yu-gong-wai-xian.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
