Arduino + Bluetooth + Androidで遊ぶ

こないだの

Arduinoとブレッドボードを初めて触ってみた - きままな日記帳

で、Arduinoを使ってLEDとキャラクタLCDの制御はうまくいった。

私の目的は、Androidをメインにやっていることもあって「ArduinoとAndroidを組み合わせる」こと。ArduinoとAndroidを連携させるにはどうすればいいか調べてみた。

まず、AndroidデバイスからはWifiかBluetoothくらいしか外部のハードウェアとやりとりする手段が無い。BeagleBoardのようなボードならどうにでもなるだろうけど、市販のAndroidデバイスで考えるとワイヤレスで、となる。

ArduinoでWifi

WiFly Shield - SparkFun Electronics

WiShield 2.0

というものがあるらしいが、国内で扱っているところは無さそう。

もし入手できたらチャレンジしてみてもいいけど…。入手性が難なのでとりあえず今回はパス。

ArduinoでBluetooth

こちらはすでに例をたくさん見かける。

side2 » AndroidとArduinoの接続で使えそうなBluetoothモジュール
このような素晴らしいまとめもあるし。

探してみた結果、SparkfanのBluetoothMateがちょうどマルツパーツ博多店に在庫があったので、早速買ってきた。

値段は約6500円。分かってはいるけど高いよね…。

ArduinoでUSBホスト

ArduinoはUSBホストにもできるらしく、USBホストシールドなるものも売られていて、これとPC用のBluetoothドングル使えば良いのでは?と思って調べてみたものの、ドングルを制御するドライバっぽいものを書かないといけなさそうだし、いきなりチャレンジするにはハードルが高そうだったので断念。

USBホストシールドと、Arduino-BluetoothプロジェクトのGitHubに上がっているソースを使えば動くのかもしれないけど…。どのデバイスでもこれが適用できるのかな。WindowsとかでMicrosoft純正のドライバがインストールされるようなBluetoothデバイスならUBS的に規格が決まってるんだろうか。

ArduinoとBluetoothMateを繫げる

BluetoothMateはシリアル接続だ。

建築発明工作ゼミ2008: Arduino-Processing BlueTooth通信+曲げセンサ

これらのサイトを参考にすると、早い話がVcc/GND/Rx/Txを繋げばOKらしい。BluetoothMateからはCTS/RTSも出ているのだがArduino側に受けるピンが無い。フロー制御が効かないけど今回はクリティカルな用途では無いので目をつぶる。

実質配線は4本なので簡単。…といいつつ、ArduinoとBluetoothMateのRx/Txをクロスして繫げないといけないことにしばらく気づかなかったけど。

2011-01-23 04.26.25

Arduinoに電源を入れると赤ランプが点滅する。この間にPCやAndroidデバイスからペアリングしておく。

通信する

BluetoothMateはRFCOMMの扱いなので、Arduino上ではSerialクラスを使えば送受信できる。

Arduino側から定期的にSerial.write()するようにしておき、PC上でTeraTermを使ってCOMポートを開くときちんとデータの受信ができていた。とりあえず基本的な動作は問題ないようだ。

Android側の処理

Android側では、BluetoothAdapter/BluetoothDeviceクラスを使ってペアリング済みのリモートデバイスを取得し、BluetoothDevice#createRfcommSocketToServiceRecord()でRFCOMMのソケットを作成して接続。あとはStreamを張れば良い。

とりあえず受信だけ行うコードを以下に。テスト用とばかりにonCreate()で全部やってしまっている痛いコード。

package net.swingingblue.android.btarduino;

import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Set;
import java.util.UUID;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.Bundle;
import android.util.Log;

public class SimpleBTTestMain extends Activity {

	private static final String LOG_TAG ="BT_Arduino";
	
	/* SPPで繋げる時のUUIDは決まっている模様 */
	private UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
	
	private BluetoothDevice btDevice;
	private BluetoothSocket btSocket;
	
	private Thread thread;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        // Android端末ローカルのBTアダプタを取得
        BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
        // ペアリング済みのデバイス一覧を取得
        Set<BluetoothDevice> btDeviceSet = btAdapter.getBondedDevices();
        
        Iterator<BluetoothDevice> it = btDeviceSet.iterator();
        
        if(it.hasNext()){
        	// とりあえず最初にマッチしたもの。本来はちゃんとデバイス判定しないとダメ
        	btDevice = it.next();
        	Log.d(LOG_TAG, "btAddr = " + btDevice.getAddress());
        }
    
    	try {
    		// RFCOMM用のSocketを生成
			btSocket = btDevice.createRfcommSocketToServiceRecord(uuid);
			btSocket.connect();
			
			thread = new Thread(new Runnable() {
				@Override
				public void run() {
					try {
						// connectできればInputStream/OutputStreamで通信できる
						InputStream inStream = btSocket.getInputStream();
						
						int retByte = inStream.read();
						if (retByte != -1) {
							Log.d(LOG_TAG, "retData = " + Integer.toString(retByte));
						}
						
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			});
			
			thread.run();
			
		} catch (IOException e) {
			e.printStackTrace();
		}
    }

	@Override
	protected void onPause() {
		super.onPause();
		
		thread.stop();
		try {
			btSocket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

キャラクタLCDと組み合わせる

このままだと固定データのやりとりだけだし、送受信データもAndroid側のLogcatを見るくらいしか手段が無い。

ふと、ちょうど昨日作ったキャラクタLCDに文字を出す回路とスケッチがあるのでそれと組み合わせてみようと思い立つ。キャラクタLCDもBluetoothMateも配線的にはバッティングしないので、そのままArduinoに繋げれば良い。

Arduinoのスケッチは、シリアルで受信したデータをバッファに入れ、1文字ずつLCDに表示させるように変更。

// include the library code:
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7, 8, 9, 10, 11, 12, 13);

int data[128]={};
int incomingByte;
int dataLength = 0;

void setup() {
  pinMode(6, OUTPUT);

  // set up the LCD's number of columns and rows: 
  lcd.begin(16,2);
  lcd.clear();
  
  Serial.begin(115200);
}

void loop() {
  // LCD Back light on
  digitalWrite(6,HIGH);

  // set the LCD cursor to (0,0):
  lcd.setCursor(0, 0);
  
  // see if there's incoming serial data:
  int count = Serial.available();
  if (count > 0) {
    Serial.print(count);
    dataLength = count;
    for (int i=0; i < count; i++) {
      // 一旦バッファに入れ繰り返し表示できるようにする
      incomingByte = Serial.read();
      data[i] = incomingByte;
    }
  }
  
  for (int i=0; i < dataLength; i++) {  
    lcd.write(data[i]);
    delay(300);
  }
  
  delay(300);
  lcd.clear();
  delay(300);
}

Android側は、EditTextとButtonを配置したActivityを作って、入力した英数字をButtonが押されたら送信してやるようにする。

とりあえずここまでは完成。

ここまで調べ物したり配線してコードを書いたりしながらで、約4~5時間くらいだと思う。

これから

ひとまず、Arduino側、Android側とも送受信のやり方が分かったので、あとは

・Arduino側に何を繋げていくか(センサ、アクチュエータ)
・Android側もGUIやデータの見せ方をどうするか、Android自身のセンサーなどと連動させるか

という「何を作るか」という段階に進むためのベースはできたと思う。

ひとつ、上記のArduinoの回路はキャラクタLCDとBluetoothMateの両方を繋いでいるので、電圧か電流が不足気味でキャラクタLCDのバックライトがチラつく。もう少し安定した電流を流してあげないとダメかと…。

参考にしたサイト

すでに参考になるサイトがたくさんあって、助かりました。

Bluetooth Mate Tutorial

建築発明工作ゼミ2008: Arduino-Processing BlueTooth通信+曲げセンサ

Blog: Arduino と Bluetooth

タイトルとURLをコピーしました