Androidアプリの作成


Androidアプリの作成

ArduinoとDemokitが連携できたところで、今度はオリジナルのAndroidアプリの作成をおこないます。

新規プロジェクトの作成 

Project名を入力します。"ArduinoSample"と入力します。

AndroidのBuild Targetは、Android 2.3.3の下のGoogle APIsを選択します。Tabletの場合は、3.1の下のGoogle APIsを選択します。

パッケージ名を入力します。"com.gclue.ArduinoSample"と入力します。



ソースコートの編集

main.xmlの編集

Arduinoから送付されてくる値を表示するためのTextViewを追加します。

res/layout/main.xml
1:  <?xml version="1.0" encoding="utf-8"?>
2:  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3:      android:layout_width="fill_parent"
4:      android:layout_height="fill_parent"
5:      android:orientation="vertical" >
6: 
7:      <TextView
8:          android:id="@+id/valueText"
9:          android:layout_width="fill_parent"
10:        android:layout_height="wrap_content"
11: />
12:     
13: </LinearLayout>

ArduinoSampleActiviy.javaの作成

まず、一番最初にArduinoSampleActivity.javaを作成します。Androidアプリを作成するには、extends ActivityでActivityを継承します。Activityを継承したAndroidアプリでは、onCreate()が一番最初に呼ばれる仕様になっています。

src/com.gclue.ArduinoSample/ArduinoSampleActivity.java

AppnameActivity.java

ガジェットの仕様の URL が見つかりませんでした

[ソースコード解説] アプリの起動

AndroidのActivityのルールで、120行目〜139行目のonCreate(), 155行目〜178行目のonResume()の順に呼び出されます。onResume()の中で、USB機器が接続された際の処理である205行目〜219行目のopenAccessory()が呼び出され、さらにそこからThreadを起動し、243行目〜263行目のrun()が呼び出されます。run()の中で、arduinoとのデータの送受信をおこなっています。

[ソースコード解説] アプリの終了

AndroidのActivityのルールで、184行目〜188行目のonPause(), 194行目〜198行目のonDestroy()の順に呼び出されます。onPause()の中で、226行目〜238行目のcloseAccessory()をさらに呼び出し、USB機器が切断された際の処理をおこなっています。

[ソースコード解説] データの送受信

データの送受信は、Arduino側でloop()が周期的に呼び出され、Android側もrun()が周期的に呼び出され、双方がループの状態を作る事でおこなっています。通常のシステムでは、いずれかがループになりデータを呼び出すようなモデルになっていますが、ADKでは、ArduinoとAndroidいずれもループ状態を作り出し、その中でデータの送受信をおこなっているのが特徴になります。




243    public void run() {
244        Log.i(TAG, "run");
245        int ret = 0;
246        byte[] buffer = new byte[16384];
247        int i;
248
249        while (ret >= 0) {
250            try {
251                ret = mInputStream.read(buffer);
252            } catch (IOException e) {
253                break;
254            }
……
277        }
278   }

[ソースコード解説]  USB接続までの手続き


まず、最初にonCreate()内でUsbManagerのインスタンスを取得します。

125        mUsbManager = UsbManager.getInstance(this);

すでに接続済みのUSB Accessoryがある場合は、それを用いてopenAccessory()を呼び出します。
接続されているUSB Accessoryがない場合は、onResume()で接続処理をおこないます。

131        if (getLastNonConfigurationInstance() != null) {
132            mAccessory = (UsbAccessory) getLastNonConfigurationInstance();
133            openAccessory(mAccessory);
134        }

次に、onResume()内では、onResume()内で取得したmUsbManagerを用いて、USB Accessoryのリストを取得します。

164        UsbAccessory[] accessories = mUsbManager.getAccessoryList();

取得したリストの0倍目がnullの場合には、168行目でopenAccessory()を呼び出してUSB Accessoryへの接続処理をおこないます。

165        UsbAccessory accessory = (accessories == null ? null : accessories[0]);
166        if (accessory != null) {
167            if (mUsbManager.hasPermission(accessory)) {
168                openAccessory(accessory);
169            } else {
170                synchronized (mUsbReceiver) {
171                    if (!mPermissionRequestPending) {
172                        mUsbManager.requestPermission(accessory, mPermissionIntent);
173                        mPermissionRequestPending = true;
174                    }
175                }
176            }
177        } else {
178            Log.d(TAG, "mAccessory is null");
179        }

USBとのデータの送受信をおこなうための処理は、openAccessory()でおこないます。211行目でFileDescriptorを取得し、212行目でArduinoからの入力値を取得するためのInputStream、213行目でArduinoへのデータ出力用のOutputStream()を定義しています。

206    private void openAccessory(UsbAccessory accessory) {
207        Log.i(TAG, "openAccessory");
208        mFileDescriptor = mUsbManager.openAccessory(accessory);
209        if (mFileDescriptor != null) {
210            mAccessory = accessory;
211            FileDescriptor fd = mFileDescriptor.getFileDescriptor();
212            mInputStream = new FileInputStream(fd);
213            mOutputStream = new FileOutputStream(fd);
214            Thread thread = new Thread(null, this, "DemoKit");
215            thread.start();
216            Log.d(TAG, "accessory opened");
217        } else {
218            Log.d(TAG, "accessory open fail");
219        }
220    }

[ソースコード解説]  USBの切断処理

Androidアプリが終了する際には、onPause()、onDestroy()の順に呼び出されます。onPause()ではcloseAccessory()を呼び出し、231行目でFileDescriptorをcloseしています。


226  private void closeAccessory() {
227 
228     Log.i(TAG, "closeAccessory");
229       try {
230            if (mFileDescriptor != null) {
231                mFileDescriptor.close();
232            }
233       } catch (IOException e) {
234       } finally {
235           mFileDescriptor = null;
236          mAccessory = null;
237       }
238 }

[ソースコード解説] 起動後のUSB初期化処理

94行目〜114行目のBroadcastReceiver mUsbReceiver = new BroadcastReceiver()では、アプリが起動中にUSB接続や切断が行われた際の処理を記載しています。AndroidのBroadcastReceiverという仕組みを使う事で、USBが接続された際に、Androidシステムから通知を受け取る事が可能になります。

[ソースコード解説] 描画処理


Androidでは、メインのスレッドのみ描画処理が可能になっています。本サンプルでは、メインスレッドから214行目、215行目で別のThreadを起動しています。別のスレッド内のループ処理が243行目〜278行目で定義されているrun()になっており、この中で描画処理をおこなう事ができません。そこで、283行目〜294行目のHandlerという仕組みを使って、描画処理をおこなえるようにしています。

今回の描画処理は、139行目で、res/layout/main.xmlからTextViewウィジェットを取得し

139        valueText = (TextView)findViewById(R.id.valueText);

Handlerの中で、typeが0ならTextViewウィジェットの背景色を黒に、typeが1ならTextViewウィジェットの背景色を白にしています。

287                if(type == 0){
288                    valueText.setBackgroundColor(Color.BLACK);
289                }
290                else if(type == 1){
291                    valueText.setBackgroundColor(Color.WHITE);
292                }


accessory_filterの作成

ArduinoSampleプロジェクトのresフォルダ以下に、xmlフォルダを作成し、accessory_filter.xmlというファイルを作成します。


accessory_filter.xml
1: <?xml version="1.0" encoding="utf-8"?>
2: <resources>
3:     <usb-accessory manufacturer="GClue, Inc." model="ArduinoSample" version="1.0" />
4: </resources>

今回のサンプルでは、manufacture, model, versionがマッチした場合にアプリ起動のダイアログボックスが表示されます。

 項目    名前
 manufacture GClue, Inc.
 model ArduinoSample
 version 1.0

AndroidManifest.xmlの編集

AndroidManifest.xml

1:  <?xml version="1.0" encoding="utf-8"?>
2:  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3:      package="com.gclue.ArduinoSample"
4:      android:versionCode="1"
5:      android:versionName="1.0" >
6:      
7:      <uses-sdk android:minSdkVersion="10" />
8:
9:      <application
10:       android:icon="@drawable/ic_launcher"
11:       android:label="@string/app_name" >
12:         <uses-library android:name="com.android.future.usb.accessory" />
13:         <activity
14:            android:label="@string/app_name"
15:            android:name=".ArduinoSampleActivity" 
16:            android:launchMode="singleInstance"
17:          >
18:             <intent-filter>
19:       <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
20:            </intent-filter>
21: 
22:            <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
23:           android:resource="@xml/accessory_filter" />
24:        </activity>
25:     </application>
26:     
27: </manifest>

<uses-library>タグで、com.android.future.usb.accessoryを追加します。
12:         <uses-library android:name="com.android.future.usb.accessory" />

また、16行目で<activity>タグのlaunchModeをsingleInstanceにします。
16:             android:launchMode="singleInstance"

さらに、18行目〜20行目の<intent-filter>タグ、Androidシステムから発行されるアクション名: android.hardware.usb.action.USB_ACCESSORY_ATTACHEDのIntentに反応するようにします。
18:             <intent-filter>
19:       <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
20:            </intent-filter>

また、先ほど作成した、22行目〜23行目の<meta-data>タグでaccessory_filterをアクション名: android.hardware.usb.action.USB_ACCESSORY_ATTACHEDと指定します。
22:            <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
23:           android:resource="@xml/accessory_filter" />

Arduinoスケッチの作成

Arduino側のスケッチも作成します。Androidに送付するバイト配列に0x1,0x2,0x3を代入します。

1:  #include <Max3421e.h>
2:  #include <Usb.h>
3:  #include <AndroidAccessory.h>
4:  
5:  AndroidAccessory acc("GClue, Inc.", // 組織名
6:                       "ArduinoSample", // アプリ名
7:                       "Sample  for ADK", // アプリ説明
8:                       "1.0", // バージョン
9:                       "http:///www.gclue.com", // URL
10:                     "0000000012345678"); // シリアル
11: void setup();
12: void loop();
13: 
14: void setup()
15: {
16:   acc.powerOn();
17:   Serial.begin(115200);
18: }
19: 
20: void loop()
21: {
22:   byte msg[3];
23:  
24:   if(acc.isConnected()){
25:     
26:     msg[0] = 0x1;
27:     msg[1] = 0x2;
28:     msg[2] = 0x3;
29:     acc.write(msg, 3);
30:   }
31:  
32:   delay(10);
33: }

起動確認

ArduinoSampleをbuildし実機転送します。AndroidとArduinoを接続し、下記画面が表示されれば成功です。

    


解説
USBが接続された場合と、切断された場合は、下図の様に遷移します。


ハードウェアのボタンを押すと画面が光るサンプルを作成する

ArduinoSampleActivityを改良して、ボタンを押すとAndroidアプリの画面が白くなるサンプルを作成してみます。

main.xmlの修正

Arduinoでmsg[0]が0x1の場合はAndroidの画面を黒い画面に、msg[1]が0x2の場合はAndroidの画面を白い画面を表示するようにします。画面の色を白/黒に変化させるために、TextViewウィジェットを画面最大に広げます。

1: <?xml version="1.0" encoding="utf-8"?>
2: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3:    android:layout_width="fill_parent"
4:    android:layout_height="fill_parent"
5:    android:orientation="vertical" >
6: 
7:    <TextView
8:          android:id="@+id/valueText"
9:          android:layout_width="fill_parent"
10:        android:layout_height="fill_parent"
11: />
12:     
13: </LinearLayout>

TextViewウィジェットを画面いっぱいに広げるために、android:layout_heightをfill_parentに変更します。

10:        android:layout_height="fill_parent"

Android側の修正

msgを受信した後の条件を変更します。また、handlerの中で、TextViewウィジェットの色の変更をおこないます。

AppnameActivity.java

ガジェットの仕様の URL が見つかりませんでした



Arduino側の修正 

1:  #include <Max3421e.h>
2:  #include <Usb.h>
3:  #include <AndroidAccessory.h>
4:  
5:  AndroidAccessory acc("GClue, Inc.", // 組織名
6:                       "ArduinoSample", // アプリ名
7:                       "Sample  for ADK", // アプリ説明
8:                       "1.0", // バージョン
9:                       "http:///www.gclue.com", // URL
10:                     "0000000012345678"); // シリアル
11: 
12: void setup();
13: void loop();
14: 
15: void setup()
16: {
17:   acc.powerOn();
18: }
19: 
20: void loop()
21: {
22:   byte msg[3];
23: 
24:   if(acc.isConnected()){
25:       msg[0] = 0x2;
26:       msg[1] = 0x2;
27:       msg[2] = 0x3;
28:       acc.write(msg, 3);
29:   }
30:  
31:   delay(100);
32: }

画面を点滅させる


1:  #include <Max3421e.h>
2:  #include <Usb.h>
3:  #include <AndroidAccessory.h>
4:  
5:  AndroidAccessory acc("GClue, Inc.", // 組織名
6:                       "ArduinoSample", // アプリ名
7:                       "Sample  for ADK", // アプリ説明
8:                       "1.0", // バージョン
9:                       "http:///www.gclue.com", // URL
10:                     "0000000012345678"); // シリアル
11: int count = 0;
12: 
13: void setup();
14: void loop();
15: 
16: void setup()
17: {
18:   acc.powerOn();
19: }
20: 
21: void loop()
22: {
23:   byte msg[3];
24:   count++;
25:   if(acc.isConnected()){
26:       if(count%2 == 0){
27:         msg[0] = 0x1;
28         msg[1] = 0x2;
29         msg[2] = 0x3;
30:         acc.write(msg, 3);
31:       }
32:       else{
33:         msg[0] = 0x2;
34:         msg[1] = 0x2;
35:         msg[2] = 0x3;
36:         acc.write(msg, 3);
37:       }
38:   }
39:  
40:   delay(100);
41: }

Arduino側にボタンを拡張する 

1:  #include <Max3421e.h>
2:  #include <Usb.h>
3:  #include <AndroidAccessory.h>
4:  
5:  #define  BUTTON1 A0 
6:  
5:  AndroidAccessory acc("GClue, Inc.", // 組織名
6:                       "ArduinoSample", // アプリ名
7:                       "Sample  for ADK", // アプリ説明
8:                       "1.0", // バージョン
9:                       "http:///www.gclue.com", // URL
10:                     "0000000012345678"); // シリアル
13: int count = 0;
14: 
15: void setup();
16: void loop();
17: 
18: void setup()
19: {
20:   acc.powerOn();
21:   pinMode(BUTTON1, INPUT);
22:   digitalWrite(BUTTON1, HIGH);  
23: }
24: 
25: void loop()
26: {
27:   byte msg[3];
28:   byte button;
29:   
30:   count++;
31:   if(acc.isConnected()){
32:       button = digitalRead(BUTTON1);
33:       
34:       if(button == HIGH){
35:         msg[0] = 0x1;
36:         msg[1] = 0x2;
37:         msg[2] = 0x3;
38:         acc.write(msg, 3);
39:       }
40:       else if(button == LOW){
41:         msg[0] = 0x2;
42:        msg[1] = 0x2;
43:         msg[2] = 0x3;
44:         acc.write(msg, 3);
45:       }
46:  }
47:  
48:   delay(100);
49: }

回路の作成

ボタンの同じ側面の一方にA0を、もう一方にGNDを接続します。
これで、ボタンを押すと画面が点灯するサンプルの完成です。


ソフトウエアのボタンを押すとLEDが光るサンプルを作成する

次は、Android側のボタンを押すと、Arduino側のLEGが光るサンプルを作成してみます。

main.xmlの修正

Buttonウィジェットを追加します。

1:  <?xml version="1.0" encoding="utf-8"?>
2:  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3:      android:layout_width="fill_parent"
4:      android:layout_height="fill_parent"
5:      android:orientation="vertical" >
6:  
7:      <Button
8:          android:id="@+id/button1"
9:          android:layout_width="fill_parent"
10:        android:layout_height="wrap_content"
11:        android:text="Light" 
12:   />
13:    
14:   <TextView
15:        android:id="@+id/valueText"
16:        android:layout_width="fill_parent"
17:        android:layout_height="fill_parent"
18: />
19:    
20: </LinearLayout>

Buttonウィジェット
7:      <Button
8:          android:id="@+id/button1"
9:          android:layout_width="fill_parent"
10:        android:layout_height="wrap_content"
11:        android:text="Light" 
12:   />

Android側の修正

Buttonウィジェットをプログラムに取り込み、Arduinoにコマンドを送信します。

ArduinoSampleActivity.java


[ソースコード解説] データ送信の処理の追加

Android側からArduino側へのデータ送信は、mOutputStream.write()でおこないます。今回のサンプルでは、ボタンを毎に、LEDが点灯/消灯する処理を追加しています。

res/layout/main.xmlで追加されたButtonを取り込みます。

96          private Button button;

154        button = (Button)findViewById(R.id.button1);

ボタンがクリックされた際の処理は157行目〜180行目でおこなっています。101行目で定義したisLightの状態によって、mOutputStream.write()で送付する値をかえています。

101    private boolean isLight = false;

155         
156        // ボタンのイベント
157        button.setOnClickListener(new OnClickListener(){
158            // ボタンがクリックされた場合
159            @Override
160            public void onClick(View v) {
161                byte[] buffer = new byte[3];
162                buffer[0] = 0;
163                buffer[1] = 0;
164                if(!isLight){
165                    buffer[2] = 0x1;
166                    isLight = true;
167                }
168                else{
169                    buffer[2] = 0x2;
170                    isLight = false;
171                }
172                if (mOutputStream != null && buffer[1] != -1) {
173                    try {
174                        mOutputStream.write(buffer);
175                    } catch (IOException e) {
176                        Log.e(TAG, "write failed", e);
177                    }
178                }
179            }
180        });

Arduino側の修正

Arduinoでは、出力の2番ピンをつかってLEDに電流を流します。

1:   #include <Max3421e.h>
2:   #include <Usb.h>
3:   #include <AndroidAccessory.h>
4: 
5:   #define  BUTTON1 A0 
6:   #define  LED1     2
7:   
8:  AndroidAccessory acc("GClue, Inc.", // 組織名
9:                         "ArduinoSample", // アプリ名
10:                       "Sample  for ADK", // アプリ説明
11:                       "1.0", // バージョン
12:                       "http:///www.gclue.com", // URL
13:                       "0000000012345678"); // シリアル
14: int count = 0;
15:  
16:  void setup();
17:  void loop();
18:  
19:  void setup()
20:   {
21:     acc.powerOn();
22:     pinMode(BUTTON1, INPUT);
23:     digitalWrite(BUTTON1, HIGH); 
24:     digitalWrite(LED1, HIGH); 
25:   }
26:  
27:  void loop()
28:  {
29:    byte msg[3];
30:    byte button;
31    
32:    count++;
33:    if(acc.isConnected()){
34:        int len = acc.read(msg, sizeof(msg), 1);
35:   if (len > 0) {
36:     if (msg[0] == 0x0) {
37:       if (msg[1] == 0x0){
38:              if (msg[2] == 0x1){
39:                 digitalWrite(LED1, HIGH);
40:              }
41:              else if (msg[2] == 0x2){
42:                 digitalWrite(LED1, LOW);
43:              }
44:            }
45:          }
46:        }
47:  
48:        button = digitalRead(BUTTON1);
49:        
50:        if(button == HIGH){
51:          msg[0] = 0x1;
52:          msg[1] = 0x2;
53:          msg[2] = 0x3;
54:          acc.write(msg, 3);
55:       }
56:        else if(button == LOW){
57:          msg[0] = 0x2;
58:          msg[1] = 0x2;
59:          msg[2] = 0x3;
60:          acc.write(msg, 3);
61:        }
62:    }
63:   
64:    delay(100);
65:  }

回路の作成



ċ
wakasagi.m4v
(603k)
Akira Sasaki,
2012/01/26 22:21
Comments