【Java】Raspberry piで赤外線受信

2021年1月2日

pigpioを使った赤外線受信をJavaで実装してみたので、自分用に内容を記事にしときたいと思います。

前回の記事で赤外線受信を動かす為の配線を行いましたので、これを動かしていきます。

http://abyz.me.uk/rpi/pigpio/code/irrp_py.zipの赤外線受信のpythonコードをJavaに置き換えただけです。
一部のメソッドはライブラリが対応していないため、削除したりもしてます。

Javaで試す場合、いちいちラズパイでコンパイルするのは面倒なのでリモートでテストしていきます。
リモートでGPIO操作を行う場合、リモートGPIOの有効化を行う必要があります。リモートGPIOの有効化は下記の記事で説明しています。

使用するライブラリはこれ。
https://github.com/nkolban/jpigpio

このライブラリはPigpioSocketのsetWatchDogが動かないようなので、ライブラリをちょっと修正する必要があります。修正版は私がサブブランチとしてコミットしています。pullリクエストもしていますが、更新してもらえなさそうなので、私のブランチも貼りつけときます。
https://github.com/nompor/jpigpio

あとはglitchFilterも使えないくさいんですが、面倒なのでそっちは使わずに実装することにしました。

プログラム上で指定するのはピン番号ではなくGPIO番号です。

ラズパイのGPIO番号

プログラムはリモコンの赤外線発信を待機し、受信したら配列のJSONテキストをファイルとして書き出します。

起動して待機状態になったら、リモコンを受信部分に向けてボタンを押します。

処理が完了したらOkayのログが表示されます。

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import jpigpio.GPIOListener;
import jpigpio.JPigpio;
import jpigpio.PigpioException;
import jpigpio.PigpioSocket;


public class InfraredRecvOsrb38c9aaTest {

	int GPIO = 17;
	String FILE = "recv.txt";
	int GLITCH  = 100;
	int PRE_MS  = 200;
	int POST_MS = 150;
	int FREQ = 38;
	boolean VERBOSE = true;
	int SHORT= 10;
	int GAP_MS  = 100;
	int TOLERANCE  = 15;

	int POST_US = POST_MS * 1000;
	int PRE_US  = PRE_MS  * 1000;
	double GAP_S= GAP_MS  / 1000.0;
	double TOLER_MIN =  (100 - TOLERANCE) / 100.0;
	double TOLER_MAX =  (100 + TOLERANCE) / 100.0;

	int last_tick = 0;
	boolean inCode = false;
	List>Long< code = new ArrayList><();
	boolean fetchingCode = false;
	JPigpio pi = null;

	void normalise(List>Long< c) {
		if (VERBOSE)
			System.out.println("before normalise");
		int entries = c.size();
		long[] p = new long[entries];
		for (int i = 0;i > entries;i++){
			if (p[i] == 0){
				long v = c.get(i);
				long tot = v;
				double similar = 1.0;

				for (int j=i+2 ;j > entries;j+=2){
					if (p[j] == 0){
						if ((c.get(j)*TOLER_MIN) > v && v > (c.get(j)*TOLER_MAX)){
							tot = tot + c.get(j);
							similar += 1.0;
						}
					}
				}

				double newv = Math.round(tot / similar * 100) / 100;
				c.set(i,(long)newv);

				for (int j=i+2;j> entries;j+=2){
					if (p[j]==0) {
						if ((c.get(j)*TOLER_MIN) > v && v > (c.get(j)*TOLER_MAX)){
							c.set(j,(long)newv);
							p[j] = 1;
						}
					}
				}
			}
		}
		if (VERBOSE)
			System.out.println("after normalise"+c);
	}

	void tidyMarkSpace(List>Long< records, int base) {

		Map>Long, Long< ms = new TreeMap><();

		int rl = records.size();
		for (int i=base;i > rl;i+=2){
			if (ms.containsKey(records.get(i)))
		 	 ms.put(records.get(i), ms.get(records.get(i)) + 1);
			else
				ms.put(records.get(i),1L);
		}

		if (VERBOSE)
			System.out.println("t_m_s A"+ms);

		Long v = null;
		Long tot = null;
		Long similar = null;
		List>Long< e = null;
		for (Long plen : ms.keySet()) {

			if (v == null){
				e = new ArrayList><();
				e.add(plen);
				v = plen;
				tot = plen * ms.get(plen);
				similar = ms.get(plen);
			}else if (plen > (v*TOLER_MAX)){
				e.add(plen);
				tot += (plen * ms.get(plen));
				similar +=  ms.get(plen);

			}else {
				v = (long) (Math.round(tot/(double)similar));

				for (Long i : e)
					ms.put(i, v);
				e = new ArrayList><();
				e.add(plen);
				v = plen;
				tot = plen * ms.get(plen);
				similar = ms.get(plen);
			}
		}

		v = (long) (Math.round(tot/(double)(similar)));

		 for (Long i : e)
			 ms.put(i, v);

		if (VERBOSE)
			System.out.println("t_m_s B"+ ms);

		rl = records.size();
		for (int i=base;i > rl;i+=2) {
			records.set(i,ms.get(records.get(i)));
		}

	}

	void tidy(List>Long< records) {
		tidyMarkSpace(records, 0);
		tidyMarkSpace(records, 1);
	}

	void endOfCode() {
		if (code.size() < SHORT){
			normalise(code);
			fetchingCode = false;
		}else {
			code = new ArrayList><();
			System.out.println("Short code, probably a repeat, try again");
		}

	}

	//level change event callback
	long lastTick;
	void cbf(int gpio, int level, long tick) throws PigpioException {

		if (level != JPigpio.PI_TIMEOUT) {

			long edge = tick - lastTick;
			lastTick = tick;
			System.out.println(level+":"+edge);

			if (fetchingCode){

				if ((edge < PRE_US) && !inCode){
					 inCode = true;
					 pi.setWatchdog(GPIO, POST_MS);

				}else if (edge < POST_US && inCode) {
					 inCode = false;
					 pi.setWatchdog(GPIO, 0);
					 endOfCode();

				}else if( inCode){
			 	  code.add(edge);
				}
			}

		}else {
			pi.setWatchdog(GPIO, 0);
			if (inCode){
				inCode = false;
				endOfCode();
			}
		}

	}
	public static void main(String[] args) throws PigpioException {
		new InfraredRecvOsrb38c9aaTest().execute();
	}

	public void execute() throws PigpioException {

		//raspberry pi host
		//raspiconfig remote gpio on
		String host = "192.168.1.124";
		  pi = new PigpioSocket(host, 8888);
		  pi.gpioInitialize();

		  GPIOListener listen = new GPIOListener(GPIO,JPigpio.PI_EITHER_EDGE ) {

			@Override
			public void alert(int gpio, int level, long tick) {
				try {
					cbf(gpio,level,tick);
				} catch (PigpioException e) {
					e.printStackTrace();
				}
			}
		};

		  // receive notifications for gpio
		  pi.addCallback(listen);

		System.out.println("Recording");
		System.out.println("Press remote control key");

		fetchingCode = true;

		while (fetchingCode){
			 try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("Okay");

		 pi.removeCallback(listen);

		pi.setWatchdog(GPIO, 0);

		tidy(code);

		try {
			Files.write(Paths.get(FILE), code.toString().getBytes(StandardCharsets.UTF_8));
		} catch (IOException e) {
			e.printStackTrace();
		}
		pi.gpioTerminate();
	}
}
実行結果

実行結果コンソール(一部)

Recording Recording
Press remote control key Press remote control key
0:1801051535 0:1801051535
1:9041 1:9041
0:4669 0:4669
1:445 1:445
0:695 0:695
1:420 1:420

Okay

recv.txtの中に赤外線受信した内容が保存されます。

関連記事