ロボットカー制御

ここではロボットカーキットの組み立てと、Android端末から走行の制御をしてみたいと思います。

ロボットカー組み立て

ロボットカーの組み立て方法、および配線はこちらをご参照下さい。
https://sites.google.com/a/gclue.jp/roboka-zuo-cheng/home

使用するシールド

今回はFaBoのモーターシールドを使用します。
使用方法はこちらをご参照下さい。

モーターシールド
https://fabo.gitbooks.io/module/content/shield_motor/shield_motor.html

使用するBrick

Bluetooth
https://fabo.gitbooks.io/module/content/brick_serial_bluetooth/brick_serial_bluetooth.html

Arduino

Arduino側では、Android端末から受信したデータにより前進、後退、停止するプログラムを書きます。

#include <SoftwareSerial.h>

#define led1 A0     // A0番ピンにてLEDを制御
#define led2 A1     // A1番ピンにてLEDを制御

#define right_f 4   // 右タイヤのモーター前進用ピン
#define right_pow 3 // 右タイヤのモーター出力設定用ピン
#define right_b 2   // 右タイヤのモーター後退用ピン

#define left_f 5    // 左タイヤのモーター用ピン(前進用)
#define left_pow 6  // 左タイヤのモーター出力設定用ピン
#define left_b 7    // 左タイヤのモーター用ピン(後退用)

SoftwareSerial android(12, 13); // Bluetooth用シリアル通信ピン

char readData = 0;  // androidからのテキスト取得用

void setup() {
  // 右側のタイヤ制御用
  pinMode(right_f, OUTPUT);
  pinMode(right_b, OUTPUT);
  pinMode(right_pow, OUTPUT);

  // 左側のタイヤ制御用
  pinMode(left_f, OUTPUT);
  pinMode(left_b, OUTPUT);
  pinMode(left_pow, OUTPUT);

  // Bluetooth用のシリアルのポートを設定
  android.begin(115200);

  // arduinoのシリアルモニタ用
  Serial.begin(9600);

  Serial.println("start");
}

void loop() {

  if (android.available()) {
    // Androidからデータ受信
    readData = android.read();
    Serial.write(readData);
    Serial.write(10);

    // 受信データによりロボカーの制御を行う
    // 前進
    if (readData == '1') {
      // 右タイヤ前進(H/L)
      digitalWrite(right_f, HIGH);
      digitalWrite(right_b, LOW);
      analogWrite(right_pow, 255); // 0-255 強さ

      // 左タイヤ前進(H/L)
      digitalWrite(left_f, HIGH);
      digitalWrite(left_b, LOW);
      analogWrite(left_pow, 255);  // 0-255 強さ
    }
    // 後退
    else if ( readData == '2') {
      // 右タイヤ後退(L/H)
      digitalWrite(right_f, LOW);
      digitalWrite(right_b, HIGH);
      analogWrite(right_pow, 255); // 0-255 強さ

      // 左タイヤ後退(L/H)
      digitalWrite(left_f, LOW);
      digitalWrite(left_b, HIGH);
      analogWrite(left_pow, 255);  // 0-255 強さ
    }
    // 停止
    else {
      // 右タイヤ停止(L/L)
      digitalWrite(right_f, LOW);
      digitalWrite(right_b, LOW);
      analogWrite(right_pow, 0);   // 0-255 強さ

      // 左タイヤ後退(L/L)
      digitalWrite(left_f, LOW);
      digitalWrite(left_b, LOW);
      analogWrite(left_pow, 0);    // 0-255 強さ
    }
  }
}

Android

Bluetooth認識設定

Bluetoothの認識設定を行います。
[app]>[src]>[main]>[res]内のAndroidManifest.xmlを開き、下記の内容を追加します。

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

AndroidManifest.xml

変更後はこのような状態になります。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="gclue.com.mybluetooth" >

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

画面レイアウト

ロボカー操作用に↑ボタン、Stopボタン、↓ボタンを配置します。

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <Button android:id="@+id/connectButton"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Connect" />

    <TextView
        android:id="@+id/statusValue"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        />

    <TextView
        android:id="@+id/inputValue"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        />

    <Button android:id="@+id/forwardButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="↑" />

    <Button android:id="@+id/stopButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="stop" />

    <Button android:id="@+id/backButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="↓" />

</LinearLayout>

次にメインの処理を作成します。

MainActivity.java

package sample.robo;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
//import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.AppCompatActivity;

import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

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

//public class MainActivity extends ActionBarActivity implements Runnable, View.OnClickListener {
public class MainActivity extends AppCompatActivity implements Runnable, View.OnClickListener {

    /* tag */
    private static final String TAG = "BluetoothSample";

    /* Bluetooth Adapter */
    private BluetoothAdapter mAdapter;

    /* Bluetoothデバイス */
    private BluetoothDevice mDevice;

    /* Bluetooth UUID (固定)*/
    private final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

    /* デバイス名 環境に合ったものに変更*/
    private final String DEVICE_NAME = "RNBT-71EE";

    /* Soket */
    private BluetoothSocket mSocket;

    /* Thread */
    private Thread mThread;

    /* Threadの状態を表す */
    private boolean isRunning;

    /** 接続ボタン. */
    private Button connectButton;

    /** 前進ボタン. */
    private Button forwardButton;

    /** 後退ボタン. */
    private Button backButton;

    /** STOPボタン. */
    private Button stopButton;

    /** ステータス. */
    private TextView mStatusTextView;

    /** Bluetoothから受信した値. */
    private TextView mInputTextView;

    /** Action(ステータス表示). */
    private static final int VIEW_STATUS = 0;

    /** Action(取得文字列). */
    private static final int VIEW_INPUT = 1;

    /** BluetoothのOutputStream. */
    OutputStream mmOutputStream = null;

    /** Connect状態確認用フラグ. */
    private boolean connectFlg = false;

    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        // Layoutにて設定したビューを表示
        setContentView(R.layout.activity_main);

        // TextViewの設定(Layoutにて設定したものを関連付け)
        mInputTextView = (TextView)findViewById(R.id.inputValue);
        mStatusTextView = (TextView)findViewById(R.id.statusValue);

        // Buttonの設定(Layoutにて設定したものを関連付け)
        connectButton = (Button)findViewById(R.id.connectButton);
        forwardButton = (Button)findViewById(R.id.forwardButton);
        backButton  = (Button)findViewById(R.id.backButton);
        stopButton  = (Button)findViewById(R.id.stopButton);

        // ボタンのイベント設定
        connectButton.setOnClickListener(this);
        forwardButton.setOnClickListener(this);
        backButton.setOnClickListener(this);
        stopButton.setOnClickListener(this);

        // Bluetoothのデバイス名を取得
        // デバイス名は、RNBT-XXXXになるため、
        // DVICE_NAMEでデバイス名を定義
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        mStatusTextView.setText("SearchDevice");
        Set< BluetoothDevice > devices = mAdapter.getBondedDevices();
        for ( BluetoothDevice device : devices){

            if(device.getName().equals(DEVICE_NAME)){
                mStatusTextView.setText("find: " + device.getName());
                mDevice = device;
            }
        }
    }

    // 別のアクティビティが起動した場合の処理
    @Override
    protected void onPause(){
        super.onPause();

        isRunning  = false;
        connectFlg = false;

        try{
            mSocket.close();
        }
        catch(Exception e){}
    }

    // スレッド処理(connectボタン押下後に実行)
    @Override
    public void run() {
        InputStream mmInStream = null;

        Message valueMsg = new Message();
        valueMsg.what = VIEW_STATUS;
        valueMsg.obj = "connecting...";
        mHandler.sendMessage(valueMsg);

        try{
            // 取得したデバイス名を使ってBluetoothでSocket接続
            mSocket = mDevice.createRfcommSocketToServiceRecord(MY_UUID);
            mSocket.connect();
            mmInStream = mSocket.getInputStream();
            mmOutputStream = mSocket.getOutputStream();

            // InputStreamのバッファを格納
            byte[] buffer = new byte[1024];

            // 取得したバッファのサイズを格納
            int bytes;
            valueMsg = new Message();
            valueMsg.what = VIEW_STATUS;
            valueMsg.obj = "connected.";
            mHandler.sendMessage(valueMsg);

            connectFlg = true;

            while(isRunning){

                // InputStreamの読み込み
                bytes = mmInStream.read(buffer);
                Log.i(TAG,"bytes="+bytes);
                // String型に変換
                String readMsg = new String(buffer, 0, bytes);

                // null以外なら表示
                if(readMsg.trim() != null && !readMsg.trim().equals("")){
                    Log.i(TAG,"value="+readMsg.trim());

                    valueMsg = new Message();
                    valueMsg.what = VIEW_INPUT;
                    valueMsg.obj = readMsg;
                    mHandler.sendMessage(valueMsg);
                }
            }
        }
        // エラー処理
        catch(Exception e){

            valueMsg = new Message();
            valueMsg.what = VIEW_STATUS;
            valueMsg.obj = "Error1:" + e;
            mHandler.sendMessage(valueMsg);

            try{
                mSocket.close();
            }catch(Exception ee){}
            isRunning  = false;
            connectFlg = false;
        }
    }

    // ボタン押下時の処理
    @Override
    public void onClick(View v) {
        // Connectボタン
        if(v.equals(connectButton)) {
            if(!connectFlg) {

                mStatusTextView.setText("try connect");

                mThread = new Thread(this);
                // Threadを起動し、Bluetooth接続
                isRunning = true;
                mThread.start();
            }
        }
        // ↑ボタン
        else if(v.equals(forwardButton)) {
            if(connectFlg) {
                try {
                    // ↑ボタン押下時、'1'を送信
                    mmOutputStream.write("1".getBytes());
                    // 画面上に'↑:'を表示
                    mStatusTextView.setText("↑:");
                } catch (IOException e) {
                    Message valueMsg = new Message();
                    valueMsg.what = VIEW_STATUS;
                    valueMsg.obj = "Error1:" + e;
                    mHandler.sendMessage(valueMsg);
                }
            }
            // Connectボタンにより接続されていない場合
            else {
                mStatusTextView.setText("Please push the connect button");
            }
        }
        // ↓ボタン
        else if(v.equals(backButton)) {
            if(connectFlg) {
                try {
                    // ↓ボタン押下時、'2'を送信
                    mmOutputStream.write("2".getBytes());
                    // 画面上に'↓:'を表示
                    mStatusTextView.setText("↓:");
                } catch (IOException e) {
                    Message valueMsg = new Message();
                    valueMsg.what = VIEW_STATUS;
                    valueMsg.obj = "Error2:" + e;
                    mHandler.sendMessage(valueMsg);
                }
            }
            // Connectボタンにより接続されていない場合
            else {
                mStatusTextView.setText("Please push the connect button");
            }
        }
        // stopボタン
        else if(v.equals(stopButton)) {
            if(connectFlg) {
                try {
                    // stopボタン押下時、'0'を送信
                    mmOutputStream.write("0".getBytes());
                    // 画面上に'STOP:'を表示
                    mStatusTextView.setText("STOP:");
                } catch (IOException e) {
                    Message valueMsg = new Message();
                    valueMsg.what = VIEW_STATUS;
                    valueMsg.obj = "Error5:" + e;
                    mHandler.sendMessage(valueMsg);
                }
            }
            // Connectボタンにより接続されていない場合
            else {
                mStatusTextView.setText("Please push the connect button");
            }
        }
    }

    /**
     * 描画処理はHandlerでおこなう
     */
    Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            int action = msg.what;
            String msgStr = (String)msg.obj;
            if(action == VIEW_INPUT){
                mInputTextView.setText(msgStr);
            }
            else if(action == VIEW_STATUS){
                mStatusTextView.setText(msgStr);
            }
        }
    };
}

これでプログラムは完了です。

まず、ロボットカーに単三電池4本(モーター用)、9V電池(Arduino電源用)を接続します。

AndroidとBluetooth接続し、画面のボタンからロボットカーを操作してみてください。

ここまで出来ましたら以下の内容を試してみましょう。

1.右旋回、左旋回機能を追加
2.速度変更
3.センサーを接続し、取得した値をAndroid端末に送信
4.センサーの値により動作変更(暗くなったら遅くなるなど)