JavaでArduinoを動かしてみた！

### はじめに
[ラクス Advent Calendar 2019](https://qiita.com/advent-calendar/2019/rakus)の10日目です。
昨日の記事は@choreiさんの[Vuetify公式ドキュメントの読み方](https://qiita.com/chorei/items/d2390b58a342f0c314eb)でした。

今回の記事は、JavaのプログラムでArduinoを動かしてみたお話です。
[Oracle Code One 2019 報告会](https://jjug.doorkeeper.jp/events/98354)で聴講したセッションに触発されて、Java + Arduinoで色々試してみました。

### Arduinoとは
本題に行く前に、Arduinoについて軽く説明します。
Arduinoは、AVRというマイクロコンピュータと入出力ポートを備えた基板で、制御にArduino言語というCに似た言語を利用します。
※基板と言語を合わせてArduinoと呼ぶこともあるようです

制御するためのプログラムを**スケッチ**と呼び、Arduino IDEを使ってPC上で作成することができます。
作成したスケッチをArduino IDEでコンパイルし、PCから基盤へ転送するのがArduinoで電子工作する基本的な流れです。

### JavaでArduinoを動かす準備
**Firmata 4j**というライブラリを利用すると、JavaのプログラムからArduinoを簡単に動かすことができます。
Firmata 4jは、**Firmata**というプロトコルを使って、JavaのプログラムとArduinoとの通信を行うためのライブラリです。

ということで、JavaでArduinoを動かす準備をしましょう。
今回使った開発環境は、以下の通りです。

- Arduino
  - 基盤  
    Arduino Uno
  - IDE  
    Arduino IDE
- Java
  - JDK  
    1.8.0_131
  - IDE  
    Eclipse Version Neon.3 Release (4.6.3)

まずは、ArduinoでFirmataが使えるようにします。
Arduino IDEを起動し、`スケッチ例` > `Firmata` > `StandardFirmata` を選択すると、Firmataによる通信を行う為のプログラムが表示されます。
あとは、表示されたプログラムをコンパイルして基盤に転送すれば、Arduino側の準備は完了です。
~~正直、おまじない程度に理解してます~~

次に、Javaのプロジェクトを作成します。
Firmata 4jは`pom.xml`で導入できるようになっているので、今回はmavenでプロジェクトを作成しました。
プロジェクトを作成したら、Firmata 4jのリポジトリ > Installationにあるとおり、pom.xmlに依存関係を追加します。

これで、JavaのプログラムからArduinoを動かす準備はできました。
ただ、今回はスケッチを作成するように実装したかったので、専用のクラスを自作してみました。

```AbstractSketch.java
package test1;

import java.util.ArrayList;
import java.util.List;

import org.firmata4j.IODevice;
import org.firmata4j.Pin;
import org.firmata4j.firmata.FirmataDevice;

/**
 * スケッチの抽象クラス. <br>
 * Arduino言語と同様、setUp, loopメソッドを実装する.<br>
 * また、スケッチの継続条件をisContinuableメソッドで実装する.
 * 
 * @author FlatMountain
 *
 */
public abstract class AbstractSketch {
	private IODevice device;
	protected List<Pin> pins;

	/**
	 * スケッチの実行フレーム(ms).
	 */
	protected int frame;

	/**
	 * ArduinoをFirmata4jで操作できるようにする.
	 */
	private void initializeSketch() {
		device = new FirmataDevice("xxx"); // ← Arduino IDEに表示されたポート名

		try {
			device.start();
			device.ensureInitializationIsDone();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}

		pins = new ArrayList<>();
		for (int i = 0; i < device.getPinsCount(); i++) {
			pins.add(device.getPin(i));
		}

		frame = 0;
	}

	/**
	 * Arduino言語のsetUpと同じように実装する.
	 */
	abstract protected void setUp();

	/**
	 * Arduino言語のloopと同じように実装する.
	 */
	abstract protected void loop();

	/**
	 * スケッチが継続できるか判定する.
	 * 
	 * @return 継続できればtrue, できなければfalse.
	 */
	abstract protected boolean isContinuable();

	/**
	 * Firmata4jの終了処理.
	 */
	private void finalizeSketch() {
		try {
			if (device.isReady()) {
				device.stop();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * スケッチの実行処理. <br>
	 * loopメソッドの実行後、1msスリープする.
	 */
	public void execute() {
		try {
			this.initializeSketch();
			this.setUp();

			while (this.isContinuable()) {
				this.loop();
				Thread.sleep(1);
				frame++;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			this.finalizeSketch();
		}
	}
}
```
mainメソッドでは、抽象クラスの型で宣言した変数に実装クラスのインスタンスを代入して、`execute`メソッドを実行するようにします。

```Main.java
package test1;

public class Main {
	public static void main(String[] args) {
		AbstractSketch sketch = new xxxxxx(); // ← ここで実装クラスのインスタンスを代入する
		sketch.execute();
	}
}
```
### Arduino + JavaでLチカしてみた！
電子工作におけるHello WorldはLチカ[^1]らしいので、初めにJavaでLチカをやってみます。

まずは、ArduinoにLEDランプを繋ぎます。
以下のように、抵抗入りLEDをArduinoへ繋ぎました。
※諸々の事情で回路図は貼れませんでした...

|電子部品|Arduino|
|---|---|
|LEDランプのアノード|13ピン|
|LEDランプのカソード|GND|

次にJava側の実装を行います。
スケッチの抽象クラスを継承して、Lチカを行うクラスを実装します。

Arduino Unoの13ピンはデジタル出力に対応しているので、`org.firmata4j.Pin`クラスの`setValue`メソッドに、LEDランプがOnのとき1を、Offのとき0を渡してやると、Lチカが実現できます。

```LEDFlash.java
package test1;

import org.firmata4j.Pin;

/**
 * Lチカのスケッチ.
 * 
 * @author FlatMountain
 * 
 */
public class LEDFlash extends AbstractSketch {
	private Pin pin;

	/**
	 * 13ピンを出力モードに設定する.
	 */
	@Override
	public void setUp() {
		pin = pins.get(13);

		try {
			pin.setMode(Pin.Mode.OUTPUT);
			pin.setValue(0);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 600msごとに13ピンのオン、オフを切り替える.
	 */
	@Override
	public void loop() {
		try {
			if (frame % 600 == 0) {
				long value = pin.getValue() == 1 ? 0 : 1;
				pin.setValue(value);
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 5秒間スケッチを実行する.
	 */
	@Override
	public boolean isContinuable() {
		return frame <= 600 * 5;
	}
}
```
mainメソッドでLチカのスケッチを実行するよう実装すれば、Java側の実装は終わりです。

```Main.java
package test1;

public class Main {
	public static void main(String[] args) {
		AbstractSketch sketch = new LEDFlash();
		sketch.execute();
	}
}
```
あとは、PCとArduinoを接続して作成したMainクラスを実行すれば、ArduinoがLチカしてくれるはずです。
では、試しにEclipse上で実行してみましょう。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Arduino + JavaでLチカしてみた！ <a href="https://t.co/fVI5zzDmKi">pic.twitter.com/fVI5zzDmKi</a></p>&mdash; FM_Kz (@FMKz8) <a href="https://twitter.com/FMKz8/status/1204067812479660033?ref_src=twsrc%5Etfw">December 9, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

期待通りLチカすれば成功です。

### Arduino + Javaでスイッチ入力してみた！
Firmata4jを使えば、Arduinoからの入力もJavaで受け取ることができます。
ということで、Arduinoに繋いだスイッチを押したとき、コンソールにメッセージを表示してみます。

まずは、Arduinoにタクトスイッチと330Ωの抵抗を繋ぎます。
今回は以下のように繋ぎました。
※分かりづらくてすみません

|電子部品|Arduino|
|---|---|
|タクトスイッチ（抵抗とつながっていない方の足）|5V|
|抵抗、タクトスイッチ|13ピン|
|抵抗（スイッチとつながっていない方の足）|GND|

次にJava側の実装を行います。
Lチカと同じように、スイッチ入力でコンソールへのメッセージ表示を行うクラスを実装します。

`org.firmata4j.Pin`クラスの`getValue`メソッドは、スイッチが押されていれば1を、押されていなければ0を返します。
そこで、一つ前のループと今のループのgetValueの結果を判定すれば、スイッチが押されたときにメッセージを表示することができます。

```Switch.java
package test1;

import org.firmata4j.Pin;

/**
 * スイッチ入力のスケッチ.
 * 
 * @author FlatMountain
 *
 */
public class Switch extends AbstractSketch {
	private Pin pin;
	private boolean isPrevFrameSwitchOff;

	/**
	 * 13ピンを入力モードに設定して、スイッチが押されていないことにする.
	 */
	@Override
	public void setUp() {
		pin = pins.get(13);
		isPrevFrameSwitchOff = false;

		try {
			pin.setMode(Pin.Mode.INPUT);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * スイッチが押された瞬間、コンソールにメッセージを表示する.
	 */
	@Override
	public void loop() {
		try {
			if (isPrevFrameSwitchOff && pin.getValue() == 1) {
				System.out.println("Hello Aruino!");
			}
			isPrevFrameSwitchOff = pin.getValue() == 0;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 5秒間スケッチを実行する.
	 */
	@Override
	public boolean isContinuable() {
		return frame <= 600 * 5;
	}

}
```
mainメソッドでスイッチ入力のスケッチを実行するよう実装すれば、Java側の実装は終わりです。

```Main.java
package test1;

public class Main {
	public static void main(String[] args) {
		AbstractSketch sketch = new Switch();
		sketch.execute();
	}
}
```
あとは、PCとArduinoを接続して作成したMainクラスを実行すれば、Arduinoがスイッチ入力待ちになるはずです。
入力待ち中にスイッチを押すと、コンソールにメッセージが表示されるはずです。

では、試しにEclipse上で実行してみましょう。
![Switch.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216807/77c76423-744d-5c31-7e03-01c57da7a679.gif)

期待通りコンソールにメッセージが表示されれば成功です。
~~メッセージが「Hello **Aruino**！」になっているのはナイショ~~

### おわりに
Firmata 4jを使ってみた感想ですが、自身が慣れ親しんだ言語、開発環境でArduinoを動かすプログラムを作成できたのがとても楽でした。
また、Javaでシリアル通信を実装してArduinoを動かす方法もありますが、自身の知見が足りないこともあり、Firmata 4jを使う方が断然楽に感じました。
Oracle Code One 2019 報告会で聴講したセッションだと、JUnitとArduinoを連動させるような実装もあったので、今度はそれに挑戦してみようと思います。

以上、「JavaでArduinoを動かしてみた！」でした。
明日も[ラクス Advent Calendar 2019](https://qiita.com/advent-calendar/2019/rakus)をお楽しみに！

### 参考資料

- [ゼロからよくわかる! Arduinoで電子工作入門ガイド - Amazon](https://www.amazon.co.jp/dp/4297102374)
- [firmataプロトコル覚え書き - GitHub](https://gist.github.com/hiroeorz/7868628)
- [firmata4j - GitHub](https://github.com/kurbatov/firmata4j)

[^1]: **L**EDランプを点滅（**チカ**チカ）させること