いつもの日記 » この記事

作ればわかる! Androidプログラミング 第4版 SDK5/6 Android Studio対応 10の実践サンプルで学ぶAndroidアプリ開発入門 (Smart Mobile Developer)

 

作ればわかる! Androidプログラミング 第4版 SDK5/6 Android Studio対応 10の実践サンプルで学ぶAndroidアプリ開発入門 (Smart Mobile Developer)
金宏 和實 (著)

 

お盆の最終日までになんとかこの本のコードの打ち込みと動作の確認が終わりました。

 

以前に第三版を読んだ感想を書きましたが、改めて第四版を購読しました。初版が2016年5月発行なので、2年ぐらい経過していますね。Androidの開発環境はコロコロ変わるのでどの程度そのまま適用できるか心配でしたが、現時点(2018.08.16)の時点で多少変更を伴いましたが、10章の「ジョギングの友」まで本に書いてあるとおりにプロジェクトを作って打ち込んでいったかんじでは動きました(尚、サイトからダウンロードできるサンプルコードは基本そのまま動きました)。また、11章はトレースロボを持ってないので確認できませんでした。
もっとも自分のAndroidStudioのバージョンが2.3.3なので、最新の環境ではどうなるのか分かりませんが…。

 

ちょっと困った点

 

・ウィザードを使ってレイアウトを起こすと、新しいタイプのレイアウトで雛形を作るので本のサンプルコードと食い違います(コピペして合わせました)。

 

・5章ではビルドする前に、onAttach();を削除しないと予期せぬエラーになって停止しました。

 

・10章は本の指示に従ってプロジェクトを作ってコードを書いたのですが、コンパイルに失敗したので、本のサンプルコードに合わせてグレードルの記述を変える必要がありました。
★本のコード(グレードル)
compile ‘com.google.android.gms:play-services:8.4.0′
★ウィザードに従って作った雛形のもの(このままだと範囲が狭くてlocationなど他に使っているものがコンパイルできなくてエラーがでる)
compile ‘com.google.android.gms:play-services-maps:11.0.4′
★動くように本のように範囲を広く指定しなおした
compile ‘com.google.android.gms:play-services:11.0.4′

 

コードを入力中にAndroidStudioが出し続けていたエラーの原因がよく分からずもやもやしてましたが、こうゆうこともあるのですね。うーん難しい。

UnityとAndroidを連携させBluetoothでArudinoと通信してみた(5)

やっとArduinoパートまで進めました。

 

ここは、参考サイトには無い項目なので自力で作ってみました。(´・∀・` )

 

スクリプトを書き出してみました。こんな風になります。

 

// スマホとブルートゥース(シリアル)通信で、やり取りするプログラム
// スマホのUnityアプリに配置してあるスライダーからのRGB値を受信してRGB_REDの色を変更する


// Bluetooth通信ライブラリ
#include <SoftwareSerial.h>
SoftwareSerial BT(10, 11);  // RX:10,TX:11 それぞれブレッドボードのBT機器の逆にRXはTXへ、TXはRXへ

// char配列->int変換用
#include <stdlib.h>


// バッファ
char buffer[4]={0};  // 初期化 4byte(3+\n)

// RGB値
int R = 0, G = 0, B = 0;

void setup() {
  
  // [1] アルディーノ側初期設定
  Serial.begin(9600);

  // [2] HC-06 側初期設定
  BT.begin(9600);

  Serial.println("setup passed");

  
}

void loop() {

  int byteRead = 0;
  int rgb_value;


  // Bluetooth 受信処理
  if (BT.available()) {

       byteRead = BT.available(); // バッファに溜まっている文字数を取得

       /*
       Serial.println("Reading To Buffer...");
       Serial.println(byteRead);
       Serial.println();
       */
       delay(100);  // 上のシリアルモニターへの処理(負荷)がなくなると、RGB_LEDがうまく点灯しなくなるので、代わりに遅延をかけている。
       
      for(int i=0; i<3; i++){
        buffer[i]= (BT.read());
        buffer[i+1] = '\0';
      }
      //Serial.println(buffer);
  }

  rgb_value = atoi(buffer);   // int型に変換

  if(0<=rgb_value && rgb_value<256){
    // R値
    analogWrite(5,rgb_value);
  }else if(256<=rgb_value && rgb_value<512){
    // G値
    analogWrite(6,rgb_value - 256); // ゲタは外しておく
  }else if(512<=rgb_value && rgb_value<768){
    // B値
    analogWrite(7,rgb_value - 512); // ゲタは外しておく
  }else{
    // error
    //Serial.println("無効値を受信");
  }

  
}

 

Arduinoのリファレンスにあるソフトウェアシリアルのread()関数の戻り値は、char型で受けているので、このスクリプトも文字の終端1byteを加えた4byteのバッファを用意して受け取っています(終端文字を挿入しては上書きするといったおかしなこともしてますが)。バッファに値が入ったら、もう一度int型に変換し直します。RGB値にゲタを履かせてずらして飛ばせば戻すのも切り分けも平易に出来るのでこう言う風にしました。

 

これで準備が整ったので、後は、Arduinoにこのスクリプトを流し込んで、スマホに書き込んだアプリを起動すれば、すでにペアリング済みのBluetooth機器を選択するダイアログが出てくるので、HC-06(実験ではHC-06を使用)を選択すれば、コネクションを確立します。コネクションを確立後、Unityのロゴが出てスライダー画面が出てくるので、スライドするとArduino側に接続されたRGB_LEDが指定のRGB値で点灯するようになります。

 

余談
Arduinoの電源にモバイルバッテリーが使えたらいいなぁとかねてから思ってて、ダイソーで買った300円バッテリーをつなげてみたらやっぱり使えませんでした。5Vの出力あるのになんでかな~とかしばらく考えてましたが、よくよく考えたらArduinoの入力電源はUNOもNANOも7V~12Vだということをすっかり失念してました。そりゃムリだよね。ところで、このダイソーの300円バッテリーせめて他の用途で使えないかなぁと思って、色々調べてみたら(参考サイト http://d.hatena.ne.jp/wakwak_koba/20170603)のような記事をみつけました。なるほど、以前ラジオの電源にしようと使ったらリセットがかかって何度も再起動した原因はこれか~と、納得しました。本当にこのモバイルバッテリーは、充電以外なんの用途にも使えないんですねぇ。残念。

UnityとAndroidを連携させBluetoothでArudinoと通信してみた(4)

前回は、Unityからエクスポートした内容をAndroidStudioに取り込めるようにしたところまで進みました。今回の備忘録は、Android側のスクリプト部分についてになります。

 

引き続き参考サイト(http://tokyo.supersoftware.co.jp/code/4807)の手順したがって、Arduinoに出力できるように改造していきたいと思います。

 

といっても、今回はほとんど手をいれるところがないのですぐ終わっちゃうかも。

 

AndroidStudioで適当な名前でプロジェクトを作って、参考サイトのコードをコピペしてみると、Unityで書き出したものは前回の移植の結果取り込めており、エラーは出なくなりました。

 

ですが~、コピペした段階でやたら警告が出てきます。

 

private ProgressDialog waitDialog; //非推奨のコード。

 

このサンプルプログラムで使われている上のようなダイアログ系の命令がことごとく非推奨になってしまっているので、このままでは使えません。そのままビルドするとエラーになります。ではどうしろというか調べたら、DialogFragmentを使えとかでてきますが、そんな使ったこと無いもので人様の書いたコードをいじくりましたらますます動かなくなってしまうおそれがあるので絶対イヤ!゚(゚´Д`゚)゚ こうゆうところでコケると、結構萎えます。

 

どこを参考にしたか忘れてしまいましたが、AndroidStudioの[ファイル]-[プロジェクト構造]の[Module]-[app]から、右列の[Complile SDK version]をAPI 25まで引き下げればそのままこのスクリプトは使えますので、設定を変えてしまいます。DialogFragmentは、本筋でないので今回は追いません。

 

なんとかエラーも消えましたので、参考サイトの2つのコードについて進めていきます。

 

ほとんどそのまんまで使います。変更点は、

BluetoothTestActivity.javaについては、

onClickButtonの中身が変更になります。引数としてUnityから受け取ったint型の値を元コードでは、switch文で場合分けして、新たなstring型のメッセージを飛ばしていますが、

String msg;
msg = String.format("%03d",position);
Log.d("メッセージの内容2",msg);
bluetoothTask.doSend(msg);

 

といった感じに、int型のRGB値を整形してからメッセージとして飛ばしています。

 

(整形前 例)
1
55
512

 

という形で入ってくるint型のRGB値のデータを、

 

(整形後 例)
001
055
512

 

といった感じの3桁の文字列に変形して送信用の関数に飛ばします。こうしておかないと、後でArduinoでデータを受け取った時に困ったことになります。

 

参考サイトのコードでは、ボタンをクリックした時にボタンアクションに対応する1個のメッセージが飛ぶ仕組みになってましたが、スライダーの場合はスライド中の値をメッセージとして次々飛ばしてきますので、少し細工をしてからArduinoに渡すようにしないと後で悶絶することになります。

 

という訳で、BluetoothTask.javaの方も一行だけ手を加えておきます。

 

    /*
     * サーバにメッセージの送信を行う非同期タスク。
     */
    private class SendTask extends AsyncTask<String, Void, Object>{
        @Override
        protected Object doInBackground(String... params){
            try{
                //btOut.write(params[0].getBytes());
                btOut.write(params[0].getBytes(),0,3);      // 第二、オフセット。第三、長さ (参考)https://docs.oracle.com/javase/jp/8/docs/api/java/io/OutputStream.html
                btOut.flush();      // 強制的に書き出し
                return "";
            }catch (Throwable t){
                doClose();
                return t;
            }
        }

 

元コードでは、引数として渡されたString型データの長さ分全部送信しちゃいなさいと書かれてますが(参考 https://msdn.microsoft.com/ja-jp/library/aa904361(v=vs.71).aspx)、ここを書き換えて、念のため3byte単位で抽出して書き出しなさいという風にしてしまいます。

 

この部分、一見無くてもいいように見えるんですが(引数はonClickButton関数で3桁に整形済みなので)、
この非同期のスレッド(doInBackground)からArduinoにメッセージが飛ぶ様子をArduinoのシリアルモニタで観察してみると、何も細工をしないで飛ばすと、1byte受信したり、3byteだったり、9byteとかまちまちな長さで飛んでいってるのが観察できます。

 

確信は出来ないですが、ここで先頭からのオフセット3byteのみ飛ばすという指定をすることで、例えば、

 

溜まっていたメッセージが
001
だけなら、そのまま3byte送信。

 

溜まっていたメッセージが
001055なら、
055は捨てて、001のみ発信という形になるのを期待して書いています。

 

メッセージの後ろから3byteを抽出して送信というのがもっともいいんでしょうが、多少のズレがあってもいいやと思ったのでこのままにしてあります。後ろから3byte指定にしたい場合は、lenを取得して-3の位置をオフセットの開始位置にして発信すればできそうな気がします。

 

次で、やっとArduinoにはいります。

UnityとAndroidを連携させBluetoothでArudinoと通信してみた(3)

前回は、Unityでユーザーインターフェースを作ったところまででしたので、今回はその続きの内容からになります。参考サイト(http://tokyo.supersoftware.co.jp/code/4807)様の内容でいうと、Android用プロジェクトとしてエクスポートするというところからになります。

 

キャプチャーが貼ってあってそのとおりにしたかったんですが、Unity5以降の画面では、微妙に表示内容が違っていたので、以下のキャプチャーの様にして書き出しました。

 

kakidashi

 

古い形式のADT(Legacy)で書き出したら、うまく取り込めました。ちなみにGradleで書き出すと、後述する別の参考サイトの取り込み方で指定されているフォルダ構成にならなかったのであきらめました。尚、Gradleについては、この辺の記事を読めばいいのかな(もっとも自分はまだ読んでないですが [参考]http://gihyo.jp/dev/serial/01/android_studio/0006)。

 

参考サイトでは、この後サラッとエクスポートしたプロジェクトをAndroidのプロジェクトにインポートしますと書かれていますが、やり方がよく分からなかったので、ここは随分試行錯誤することになりました。Androidに「インポート」って項目があるのでやってみたけど、うまくいかなかったり…。

 

またまた色々調べて、以下のサイトのやり方に従ってやってみたら、取り込むことが出来ました。

 

(参考サイト) http://meleap124.blogspot.jp/2015/04/android-studiounity.html

 

こちらのサイトの指示どおりに従って、手作業で移植すれば動きますね。4のマージは、自分の場合は、Unituロゴアイコンのデータをコピーする程度でした。特に上書きは無かったです。5のマージは、ちょっと注意がいるので、最初の参考サイト(http://tokyo.supersoftware.co.jp/code/4807)の下の方に記載されている、AndroidManifest.xmlの内容に沿ったものになっているか確認しながら移植することになると思います。

 

私の場合は、以下の様にAndroidStudioが自動で作成したマニュフェストに追加記載しました。また、Unityのユーザーインターフェースは、横向きレイアウトで作成したので、landscape指定を追加でしておきました。

 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="jp.co.hoge.unity.bluetooth.test">

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

    <application
        android:allowBackup="true"
        android:icon="@drawable/app_icon"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/UnityThemeSelector"
	android:isGame="true">
        <activity android:name=".BluetoothTestActivity" android:screenOrientation="landscape">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
        android:name="jp.co.hoge.unity.bluetooth.test.UnityPlayerActivity"
        android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale"
        android:label="@string/app_name"
        android:launchMode="singleTask"
        android:screenOrientation="landscape">
        <!--<intent-filter>-->
        <!--<action android:name="android.intent.action.MAIN" />-->
 
        <!--<category android:name="android.intent.category.LAUNCHER" />-->
        <!--<category android:name="android.intent.category.LEANBACK_LAUNCHER" />-->
        <!--</intent-filter>-->
        <meta-data
            android:name="unityplayer.UnityActivity"
            android:value="true" />
        </activity>
    </application>
  <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="26" />
  <uses-feature android:glEsVersion="0x00020000" />
  <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
  <uses-feature android:name="android.hardware.touchscreen.multitouch" android:required="false" />
  <uses-feature android:name="android.hardware.touchscreen.multitouch.distinct" android:required="false" />
</manifest>

 

つづく。

UnityとAndroidを連携させBluetoothでArudinoと通信してみた(2)

前回からの続きになります。

 

最初に参考サイト(http://tokyo.supersoftware.co.jp/code/4807)と同じく、Unity側のユーザーインターフェースを作り変えます。元プログラムではONGUI()で、ボタンをスクリプトから作成しているわけですが、このイベントが発生する部分をスライダーに置き換えてしまいます。

 

GUIのパーツをペタペタ張り込んで作りました。キャプチャーはこんな感じ。

 

unity_01

 

スクリプトは以下の様に改編しました。(※書き出す時のパッケージ名ですが、ここではちゃんとhogeと打ち替えてますが(自分が作ったものは引用元サイトの名前のまま)、ここを自分みたいにいい加減に設定しちゃうと、後々困ったことになるので注意したいところ)

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Runtime.InteropServices;
using System;

public class slide : MonoBehaviour {

	private Text rText;	// 格納用(タグを介してスライダーの右のテキストボックスに数値表示する為に用意した)
	private Text gText;	// 格納用
	private Text bText;	// 格納用

	private float r;	// RGB値の数値を持つ
	private float g;
	private float b;

	private Image panel;	// バネルを格納する

	// ★Bluetooth通信関連
	private IntPtr BluetoothTestActivityClass;
	private IntPtr ptr_onClickButton;


	void Start () {

		// バネルを取得しておく。
		panel = GameObject.FindWithTag("panel").GetComponent<Image>();


		Slider slider_r = GameObject.FindWithTag("slider_r").GetComponent<Slider> ();
		/*
		 * GetComponentで指定するのは、クラスの名称ではなく、型。(✕)Slider_R (○)Slider
		*/
		Slider slider_g = GameObject.FindWithTag("slider_g").GetComponent<Slider> ();
		Slider slider_b = GameObject.FindWithTag("slider_b").GetComponent<Slider> ();


		rText = GameObject.FindWithTag("text_r_value").GetComponent<Text> ();
		gText = GameObject.FindWithTag("text_g_value").GetComponent<Text> ();
		bText = GameObject.FindWithTag("text_b_value").GetComponent<Text> ();


		// ★Bluetooth通信を行うための追加設定
		AndroidJNI.AttachCurrentThread();
		IntPtr cls_Activity = AndroidJNI.FindClass("jp/co/hoge/unity/bluetooth/test/BluetoothTestActivity");
		Debug.Log("cls_Activity = " + cls_Activity);
		IntPtr fid_Activity = AndroidJNI.GetStaticFieldID(cls_Activity, "mBluetoothTestActivity", "Ljp/co/hoge/unity/bluetooth/test/BluetoothTestActivity;");
		IntPtr obj_Activity = AndroidJNI.GetStaticObjectField(cls_Activity, fid_Activity);
		BluetoothTestActivityClass = AndroidJNI.NewGlobalRef(obj_Activity);

		// ★java側で実行される関数onClickButtonに渡す引数の型のシグネチャをint型に。
		ptr_onClickButton = AndroidJNI.GetMethodID(cls_Activity, "onClickButton", "(I)V");		// 引数はI(int)で、戻り値の型は、V(void)

		jvalue[] jargs = new jvalue[1];		// 要素1の共用体を作成する。


		/* 赤(0~255) */
		slider_r.onValueChanged.AddListener((value)=>{
			// ラムダ式で直接表記

			jargs [0].i = (int)value;	// 単純にキャスト;
			AndroidJNI.CallVoidMethod(BluetoothTestActivityClass, ptr_onClickButton, jargs);	// 登録したjava(AndroidStudio)側の関数を実行する。


			//Debug.Log(value);	// この時のvalueは自動的にスライダからもらえる
			rText.text = value.ToString();	// float->string

			r = value / 256;
			// unityのcolor型が取る値の範囲は、0から1までの間なので、256段階に分割しなおさないと
			// http://shiro-izu.hateblo.jp/entry/2016/01/22/163858

			panel.color = new Color (r,g,b);	// 変化がある都度更新

			Debug.Log(panel.color);
		
		});

		/* 緑(256~511) */
		slider_g.onValueChanged.AddListener((value)=>{
			// ラムダ式で直接表記

			jargs [0].i = (int)value + 256;	// 値を256分ずらす。Arduinoで除去
			AndroidJNI.CallVoidMethod(BluetoothTestActivityClass, ptr_onClickButton, jargs);	// 登録したjava(AndroidStudio)側の関数を実行する。

			//Debug.Log(value);	// この時のvalueは自動的にスライダからもらえる
			gText.text = value.ToString();	// float->string

			g = value / (int)value;	// 単純にキャスト256;	// unityのcolor型が取る値の範囲は、0から1までの間なので、256段階に分割しなおさないと

			panel.color = new Color (r,g,b);	// 変化がある都度更新

			Debug.Log(panel.color);

		});

		/* 青(512~768) */
		slider_b.onValueChanged.AddListener((value)=>{
			// ラムダ式で直接表記

			jargs [0].i = (int)value + 512;	// 値を512分ずらす。Arduinoで除去
			AndroidJNI.CallVoidMethod(BluetoothTestActivityClass, ptr_onClickButton, jargs);	// 登録したjava(AndroidStudio)側の関数を実行する。

			//Debug.Log(value);	// この時のvalueは自動的にスライダからもらえる
			bText.text = value.ToString();	// float->string

			b = value /256;	// unityのcolor型が取る値の範囲は、0から1までの間なので、256段階に分割しなおさないと

			panel.color = new Color (r,g,b);	// 変化がある都度更新

			Debug.Log(panel.color);

		});

	}

	// 毎ターン
	void Update(){
	}


	// 外部から取得できるように
	public Color getRGBval(){

		Color rgbColor = new Color (r,g,b);

		return rgbColor;

	}

}

 

ONGUI()で処理していた内容が、一部不要になったり、他の場所に移動したりしています。スライダーが、発するイベントはonValueChangedだけだそうなので、これを検知したら、java側のptr_onClickButtonという名前の関数に、jargsという名前のint型の値が入っている共用体を渡すということを書きました。なお紐づけされる関数名ですが、ボタンクリックではなく、スライダーのタップなので本当は関数名も別の名前にするべきでしたが、参考サイトと比較しにくくなっちゃうので、関数名はこのまま同じでここでは使っています。(かっこ悪いけどまぁ、自分が把握しやすければいいやと)

 

また、JNI(Java Native Interface)については、(´・∀・`)ヘーこんな風にして値を渡すんだーと関心しているぐらいの初心者なので、無理にいじくりまわさないで、int型を素直に渡してます。これが配列を渡したり出来たらまた色々なことが出来そうな気がするんですが、実力が伴わないので諦めました。(・3・)ムリ

 

つつ゛く

UnityとAndroidを連携させBluetoothでArudinoと通信してみた(1)

「スマホ上のUnityアプリからArduinoに対して命令を送ってみた」といったものです。ユーザーインターフェース部分をUnityで作ったので、その分ややこしくなりました。忘れっぽいのでいつものように備忘録として何をどうやって作ったのかを記録してゆきます。

 

といっても、何から何までどうやってやればいいのかさっぱり解らない状態からのスタートでしたので、先達で参考になりそうなサイト探しからいつものようにしまして、こちらのサイトさんの例をベースにして作ってみました。

 

(参考)Unity で Bluetooth 通信
http://tokyo.supersoftware.co.jp/code/4807

 

上記サイトを読んで、実際作ってもらえば分かることなのですが、Unityでユーザー操作用の画面を作って、ABC…、HIJ…、OPQ…、VWX…というテキストが表示されている4つのボタンを配置し、それらをクリックすると、そのイベントに紐付けられているAndroid側のJavaからイベントに対応するメッセージを、Bluetooth通信で受信用のPCのUnityアプリに飛ばし、Unityの画面上に結果を表示するというプログラムが紹介されています。

 

ちなみに上のプログラムをトレースして組んだだけで、わたしなどは結構お腹いっぱいでした(もうヘトヘト)が、もうちょっと頑張ってこのプログラムをベースにして、受信部分をPCではなくArduinoに変更してなんかやらせてみようと粘ってみることにしました。

 

で、何を作ったのかというのが下の画像になります。上のUnityで作ったRGB値を設定するスライダーから飛ばしたRGB値を、下のArduinoで受信して、RGB_LEDをスマホの色指定と同期させるシンプルな回路を作ってみました。

 

slider_sc
(Unityで作成)

 

20180106a
(HC-06で受信した値をRGB_REDへ)

 

長くなりそうなので何回かに分割して書きます。今回はここまで。