2015年9月20日 星期日

透過 Raspberry Pi 讓手機遙控冷氣機



在夏天最不希望遇到的事,就屬於一打開家門,熱烘烘的風就吹到臉上。我一直想要有智慧家電,讓我能再回到家前就打開冷氣機,不過換新的冷氣機實在是太貴了。不如自己動手做,Raspberry Pi 板子還是比新冷氣機便宜。不過現在市面上已經產品可以遙控不同品牌的冷氣機,而且價格在3000 NTD以下,所以要動手做之前還是要考量時間成本。不過老話一句,自己做帶來的成就感是無價的 ??

_______________________________________________________________________________


用Raspberry Pi 去遙控冷氣其實不難,但是如果很多東西都是第一次碰的話,就會花很多時間去評估使用哪種方法。這篇我會列出步驟,還有我曾經遇的問題。這樣想要自己做的人,就可以事先避免浪費時間。

先偷看我做的陽春 iPhone App 介面長怎麼樣。這樣心裡才會有底。


 

很好,只有幾個功能:讀溫濕度、開機、設定溫度。
你一定會想這簡直就是爛爆了,做得這麼殘缺,居然沒有暖氣、除濕這些常見的功能...

不是我不想做,是因為我的Sampo冷氣遙控器是一次送出所有的設定。舉例來說,當我按下溫度下降的按鈕,送出的指令卻是 "冷氣 + 27度 + 風扇上下擺動 + 空氣清淨 "。因為沒有官方文件,我花了一下午還是無法分離各功能的波形。這是我做之前沒有想到的問題,所以最後才做閹割版本。

_______________________________________________________________________________

實作示意圖:

RaspberryPi 版子透過 SSH tunnel的功能連到遠端的AWS server,這個連線會一直存在。透過SSH tunnel,RaspberryPi 就不需要固定IP。當手機要打開冷氣機時,會透過HTTPS連到AWS server,然後server會透過之前的SSH tunnel打開冷氣機。

_______________________________________________________________________________

實作需要的設備:

  • 紅外線模組
    • 可以自己買零件組,但是因為我是第一次使用,為了保險,我從良興買模組
    • 接收模組有LED燈,如果收到訊號就會閃,可以幫助除錯
    • 良興上賣的發射器模組,功率太低,傳送聚集不到兩公尺。後來還是等要自己買零件兜起來(BJT型號為2N3904,價格不到10NTD),可以參考這篇文章 - 自製主動式紅外線遙控發射器
    • 雖然發射模組很爛,但還是建議買一個,可以幫助除錯
    • 接收程式可以下載網路的程式,或是用Linux提供的程式 LIRC。雖然我是使用LIRC,但設定方式很不方便。如果遇到像Sampo這樣的冷氣機,不按照一般LIRC格式,所以會發生錄失敗。這時候就得改用LIRC Raw模式去錄製。
    • 一般設定方式請參考這篇 - Raspberry Pi + LIRC 紅外線遙控實做
  • 固定IP 或是 有網址的伺服器
    • 為了讓手機可以控制冷氣機,需要額外的機器接收手機的指令,接著這台機器再轉送指令到 RaspberryPi 
    • 我是用Amazon AWS 機器。他有免費方案,可以使用
_______________________________________________________________________________


當硬體都設定好了,就差 RaspberryPi 端要打 SSH Tunnel的指令。
先在RaspberryPi 建立 createTunnel.sh,然後替換底下的變數

  1. ADDR_AWS:      server位置
  2. AWS_ID_FILE:  RaspberryPi 連線到AWS server的密碼檔案位置
  3. AWS_UID:          AWS server端的登入ID
  4. PI_UID:               RaspberryPi的登入ID


#!/bin/bash

PORT_PI=19922
PORT_AWS=10022
ADDR_AWS="xxxxxxx.ap-northeast-1.compute.amazonaws.com"
AWS_ID_FILE="/home/pi/bash_shell/EC2Key.pem"
AWS_UID="ec2-user"
PI_UID="pi"

createTunnel(){
        /usr/bin/ssh -f -N -R $PORT_AWS:localhost:22 -L $PORT_PI:$ADDR_AWS:22 $ADDR_AWS -i $AWS_ID_FILE -l $AWS_UID

        if [[ $? -eq 0 ]]; then
                echo Tunnel to AWS created successfully
        else
                echo An error occured creating a tunnel to AWS. Return code: $?
        fi

}

closeTunnel(){
        #get pid of tunnel
        PIDS=`ps aux | grep ssh | grep $PORT_AWS | grep ec2-user | awk {'print $2'}`
        if [[ -n $PIDS ]]; then
                for pid in $PIDS; do
                        #echo $pid
                        sudo kill -9 $pid
                done
        fi
}

/usr/bin/ssh -p $PORT_PI localhost -i $AWS_ID_FILE -l $AWS_UID ssh $PI_UID@localhost -p $PORT_AWS ls > /dev/null

if [[ $? -ne 0 ]]; then
        echo Creating new tunnel connection to AWS
        closeTunnel
        sleep 5
        createTunnel
fi

當檔案建立完後,就可以執行建立連線。不過因為tunnel會因為無線網路訊號不好,或是server過於忙碌的問題,可能每天會斷線一次,所以需要時常檢查連線是否還在。只要RaspberryPi端透過crontab,每分鐘查詢一次就可以達成。

當tunnel斷線時,可能5分鐘之內都還無法再度連線,所以最少有5個連線需要再等待server回覆。一旦server有能力接受tunnel時,RaspberryPi就會有5個連線,這樣會拖垮效能。解決方式就是使用flock指令,保證只會有一個連線需求。建立 createTunnelLock.sh 來將lock加入。這個script需要指定上面所寫的 createTunnel.sh 的位置,把他填入shell_script的變數內就可以了

!/bin/bash
lock_file=/tmp/lock.create_ssh_tunnel
shell_script=/home/pi/bash_shell/createTunnel.sh

echo "befor critical section"

# lock the file handle; this part will block
(
flock -n 200 || exit 1
$shell_script
) 200>$lock_file # open a file handle; this part will always succeed


echo "after critcal section"

一旦建立SSH tunnel後,server就可以對 RaspberryPi下指令,甚至是重新啟動 RaspberryPi,所以控制冷氣也當然能辦到。剩下的就是使用JSON格式的指令,將開關冷氣的命令從手機傳送到AWS server ,讓server解讀完後,透過SSH tunnel去控制,這個步驟是比較直觀的,我就先不提了。至於HTTPS得設定,AWS有提供硬體解SSL的功能,可以參考文件去設定。當這些都設定完後,就是寫 iOS了,因為UI設計實在是看人喜好,我就不提了。簡單的測試可以先用 XCode的playground 上面執行,對於除錯會有很大的幫助。