広告

iPhone でエアコンを遠隔操作する

!!! 半年以上も前に書かれた古い情報です !!!
!!! 半年以上も前に書かれた古い情報です !!!
!!! 半年以上も前に書かれた古い情報です !!!
!!! 半年以上も前に書かれた古い情報です !!!
!!! 半年以上も前に書かれた古い情報です !!!

環境

Ubuntu 12.04.1 (64bit)
Oracle JDK 7

前準備

Oracle JDK 7 の導入
RXTX の導入
PC-OP-RS1 の導入
RXTX で PC-OP-RS1 を操作する

内容

iPhone でエアコンを遠隔操作するための,エアコン操作サーバを作成してみます.
信号の流れは下記の通りです.

[iPhone] — [ルータ] — [エアコン操作サーバ] — [PC-OP-RS1] — [エアコン]

まず,RXTX で PC-OP-RS1 を操作する における学習プログラムを使用し,エアコンの信号を保存しておきます.

例えば,

エアコンを運転モード冷房 18 度,風量最大で ON する場合の信号 cool_18_max_on.txt
エアコンを運転モード暖房 30 度,風量最大で ON する場合の信号 heat_30_max_on.txt
エアコンを OFF する場合の信号 off.txt

など.

そして,RXTX で PC-OP-RS1 を操作する における送信プログラムを少し編集してサーブレットにします.
サーブレットをエアコン操作サーバ上で動かします.
サーブレットが GET リクエストを送信し,GET パラメータの値に応じたエアコン信号が OP-PC-RS1 を通してエアコンに送信されます.

例えば,/RunPcOpRs1Sender?signal=cool_18_max_on.txt というリクエストを受信すると,/var/remocon/cool_18_max_on.txt の内容を PC-OP-RS1 (/dev/ttyUSB0) へと送信します.
結果,エアコンの電源が ON します.

今回,Java のサーブレットコンテナとしては,Jetty を使用しています.

リスナ

package com.example.remocon;

import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import gnu.io.UnsupportedCommOperationException;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.TooManyListenersException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PcOpRs1Sender implements SerialPortEventListener{
	private static Logger log = LoggerFactory.getLogger(PcOpRs1Sender.class);
	static final String SEPARATOR = System.getProperty("file.separator");

	private String deviceName;
	private SerialPort port;

	private InputStream is;
	private OutputStream os;

	@SuppressWarnings("unused")
	private boolean flag0x74;
	private boolean flag0x31;

	private String signalPath;

	PcOpRs1Sender(String _deviceName, String _signalPath){
		deviceName = _deviceName;
		signalPath = _signalPath;
		flag0x74 = false;
		flag0x31 = false;
	}

	boolean openSerialPort(){
		try {
			CommPortIdentifier portID = CommPortIdentifier.getPortIdentifier(deviceName);
			try {
				port = (SerialPort)portID.open("PC-OP-RS1 Test Application", 5000);
				log.info("You can use " + deviceName + " now!");
			} catch (PortInUseException e) {
				log.info("[openSerialPort()] " + deviceName + " is used by other application now.");
				e.printStackTrace();
				return false;
			}
		} catch (NoSuchPortException e) {
			log.info("[openSerialPort()] " + deviceName + " does not exist.");
			e.printStackTrace();
			return false;
		}
		return true;
	} // openSerialPort()

	boolean setSerialPort(){
		try {
			port.setSerialPortParams(
					115200,                 // [bps]
					SerialPort.DATABITS_8,
					SerialPort.STOPBITS_1,
					SerialPort.PARITY_NONE
			);
			port.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
			try {
				port.addEventListener(this);
			} catch (TooManyListenersException e) {
				log.info(deviceName + ":setSerialPort():TooManyListenersException");
				e.printStackTrace();
				return false;
			}
			port.notifyOnDataAvailable(true);
			try {
				is = port.getInputStream();
				os = port.getOutputStream();
			} catch (IOException e) {
				log.info(deviceName + ":setSerialPort():IOException");
				e.printStackTrace();
				return false;
			}
			log.info(deviceName + " : setSerialPort() is OK!");
		} catch (UnsupportedCommOperationException e) {
			log.info(deviceName + ":UnsupportedCommOperationException");
			e.printStackTrace();
			return false;
		}
		return true;
	} // setSerialPort()

	private void closePort(){
		port.close();
		log.info(deviceName + " is closed");
	}

	public void send0x74(){
		log.info(deviceName + " : PC-OP-RS1 sent 0x74!");
		byte b = 0x74;
		try {
			os.write(b);
			os.flush();
			os.close();
			flag0x74 = true;
		} catch (IOException e) {
			log.info(deviceName + ": send0x74:IOException");
			e.printStackTrace();
			System.exit(-1);
		}
	} // send0x74(){

	public void send0x31(){
		log.info(deviceName + " : PC-OP-RS1 sent 0x31!");
		byte b = 0x31;
		try {
			os.write(b);
			os.flush();
			os.close();
			flag0x31 = true;
		} catch (IOException e) {
			log.info("[Error] " + deviceName + ": send0x31:IOException");
			e.printStackTrace();
			System.exit(-1);
		}
	} // send0x31(){

	public void sendSignal(){
		log.info(deviceName + " : PC-OP-RS1 sends signal");
		byte[] out = new byte[240];
		byte b;
		try {
			FileReader fr = new FileReader(signalPath);
			BufferedReader br = new BufferedReader(fr);
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			String str;
			while((str = br.readLine()) != null){
				//System.out.println(str);
				int i = 0;
				while(i < str.length()){
					String tmpStr = str.substring(i, i+2);
					int tmpInt = Integer.parseInt(tmpStr, 16);
					b = (byte)tmpInt;
					baos.write(b);
					i = i + 2;
				}
			}
			br.close();
			fr.close();
			out = baos.toByteArray();
			baos.close();
			for(int i = 0; i < out.length; i++){
				if((i!=0) && (i!=1) && (i!=(out.length-1))){
					os.write(out[i]);
					System.out.print(String.format("%02x", out[i]));
				}
			}
			os.flush();
			os.close();
		} catch(FileNotFoundException e){
			log.info(deviceName + ": sendSignal:FileNotFoundException");
			e.printStackTrace();
			System.exit(-1);
		}catch(IOException e){
			log.info(deviceName + ": sendSignal:IOException");
			e.printStackTrace();
			System.exit(-1);
		}
	} // sendSignal()

	public void serialEvent(SerialPortEvent event) {
		if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
			read();
		}
	}

	private void read(){
		byte[] in = new byte[1024];
		int len;
		try {
			while((len = is.read(in)) != -1){
				if(String.format("%02x", in[0]).equals("59") && (len == 1)){
					log.info(deviceName + " : PC-OP-RS1 receive 0x59!");
						if(!flag0x31){
							send0x31();
						}else{
							sendSignal();
						}
				}
				if(String.format("%02x", in[0]).equals("45") && (len == 1)){
					System.out.println();
					log.info(deviceName + " : PC-OP-RS1 receive 0x45!");
					break;
				}
			}
			is.close();
		} catch (IOException e) {
			log.info(deviceName + ": read():IOException");
			e.printStackTrace();
		} finally {
			closePort();
		}
	} // read()

} // class

サーブレット

package com.example.remocon;

import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RunPcOpRs1Sender extends HttpServlet{

	/**
	 *
	 */
	private static final long serialVersionUID = 1L;
	private static Logger log = LoggerFactory.getLogger(RunPcOpRs1Sender.class);

	public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException{

		String signal = req.getParameter("signal");

		String deviceName = "/dev/ttyUSB0";
		String signalPath = "/var/remocon/" + signal;

		CommPortIdentifier portID;
		@SuppressWarnings("unused")
		SerialPort port;

		@SuppressWarnings("unchecked")
		Enumeration portList = CommPortIdentifier.getPortIdentifiers();
		while(portList.hasMoreElements()){
			portID = (CommPortIdentifier)portList.nextElement();
			log.info("[portID.getName()] " + portID.getName());

			if(portID.getPortType() == CommPortIdentifier.PORT_SERIAL){
				log.info("[portID.getPortType()] " + deviceName + " is Serial device.");

				if(portID.getName().equals(deviceName)){

					if(portID.isCurrentlyOwned()){
						log.info("[portID.isCurrentlyOwned()] " + deviceName + " is used by other aplication now.");
						System.exit(-1);
					}else{
						log.info("[portID.isCurrentlyOwned()] You can use " + deviceName);

						PcOpRs1Sender rxtx = new PcOpRs1Sender(deviceName, signalPath);

						if(rxtx.openSerialPort()){
							if(rxtx.setSerialPort()){
								rxtx.send0x74();
								res.sendRedirect("index.jsp");
							}else{
								log.info(deviceName + ": rxtx.setSerialPort()");
								break;
							}
						}else{
							log.info(deviceName + ": rxtx.openSerialPort()");
							break;
						}
					}

				}else{
					log.info(portID.getName() + " is NOT match " + deviceName);
				}

			}else if(portID.getPortType() == CommPortIdentifier.PORT_PARALLEL){
				log.info("[portID.getPortType()] " + deviceName + " is Parallel device.");
				break;
			}else{
				log.info("[portID.getPortType()] " + deviceName + " is Unknown device.");
				break;
			}

		} // while()

	} // doGet()

} // class

クライアントについて

HTTP の GET リクエストでエアコンを操作しているので,iPhone 以外でも,iPad, Android, Mac, Windows などからも操作可能です.
(WEB ブラウザを使用するなり,SDK のネットワーク系の API を使用するなどして)

サーバについて

今回のエアコン操作サーバは,Oracle JDK と RXTX と OP-PC-RS1 が動作すればよいので,BeagleBoard-xM, PandaBoard, Raspberry Pi 上に構築することも可能です.
この場合,5V の AC アダプタでの駆動なので,非常に低消費電力となります.

注意点としては,ARM 向けの Oracle JDK が現在 Soft-float なので,OS もそれに合わせる必要があります.
BeagleBoard-xM, PandaBoard では Soft-float の Ubuntu, Raspberry Pi では Soft-float の Debian で動作確認しました.
ARM への Oracle JDK のインストール方法はこちら.

また,ARMv7 でも ARMv6 でも RXTX のソースからのビルドができることも確認しています.
ARM での RXTX の導入方法はこちら.

自宅外 (外出先) からの操作

自宅がグローバル固定 IP アドレスを持っていたり,DDNS を使用している場合は,外出先からでも自宅のエアコンを操作することもできます.
我が家では YAMAHA の RTX810 を導入し,L2TP/IPsec を使用し,iPhone からでも Android からでも外出先からよりセキュアに自宅のエアコンを操作しています.

WEB ブラウザ上で操作するための GUI を JSP 等で作成するとより便利になります.

温度や湿度もモニタリングする

Arduino と連携させると,外出先から自宅の気温や湿度を確認することができます.
温度・湿度センサを接続した Arduino と連携させる方法はこちら.

広告

PC-OP-RS1

Posted by admin