前回こんな記事を書きましたが、USBのシリアル通信でなくSoftwareSerialを使いました。
SoftwareSerialを使うことでUSBをPC側に繋いだまま開発ができます。またシリアル線ができたので他のマイコンなどにも使えて便利そうです。またVCCがあるのでPCとのUSBを外してもAndroid側から電力を供給できます。
用意するもの
- Android
- Arduino
- ジャンプワイヤ
- FTDI USBシリアル変換アダプター
Arduino側
SoftwareSerialはシリアル通信をソフト側でやろうというものです。USBケーブル内でやってる配線を簡単にArduino側の配線に繋げるイメージでしょうか。FTDIの変換アダプターはTX・RX・VCC・GNDをArduino側とつなげます。TXとRXは送信と受信ですが、Arduino側のRX(10ピン)はアダプタ側のTX、TX(11ピン)はアダプタ側のRXと逆になるので注意してください。送る側に付けるのは受ける側、受ける側に繋がっているのは送る側ですからね。
Serial受信時はわかりやすいように13ピンのLEDを点灯させるようにしました。またSoftwareSerialのボーレートはこれより低いとメッセージの取りこぼしがありました。
#include "SoftwareSerial.h" #define StringMaxLength 32 int ledNum = 13; SoftwareSerial mySerial(10, 11); // RX, TX char data[StringMaxLength]; int counter = 0; void setup() { Serial.begin(14400); pinMode(ledNum, OUTPUT); // set the data rate for the SoftwareSerial port mySerial.begin(9600); mySerial.println("Hello from Arduino SoftwareSerial!"); } void loop() { if(mySerial.available()) { Serial.println("available"); digitalWrite(ledNum, HIGH); data[counter] = mySerial.read(); counter++; if(data[counter] == '\0' || counter == StringMaxLength) { data[counter] == '\0'; Serial.println(data); mySerial.print("Hi from Arduino "); mySerial.println(data); counter = 0; digitalWrite(ledNum, LOW); } } }
Android側
ベースはFTDriverのサンプルとほぼ同じです。レイアウト変えたのと、TextViewをクリアするボタンをつけた感じです。Android側から固定のStringを送っていますが、テキストエリアから自在にメッセージを送るように作っても良いと思います。
アクティビティ
package net.jonki.serialapplication; import jp.ksksue.driver.serial.FTDriver; import android.app.Activity; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.hardware.usb.UsbManager; import android.os.Bundle; import android.view.Menu; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends Activity { FTDriver mSerial; // [FTDriver] Permission String private static final String ACTION_USB_PERMISSION = "jp.ksksue.tutorial.USB_PERMISSION"; Button btnBegin,btnRead,btnWrite,btnEnd; TextView tvMonitor; StringBuilder mText = new StringBuilder();; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnBegin = (Button) findViewById(R.id.btnBegin); btnRead = (Button) findViewById(R.id.btnRead); btnWrite = (Button) findViewById(R.id.btnWrite); btnEnd = (Button) findViewById(R.id.btnEnd); btnRead.setEnabled(false); btnWrite.setEnabled(false); btnEnd.setEnabled(false); tvMonitor = (TextView) findViewById(R.id.tvMonitor); // [FTDriver] Create Instance mSerial = new FTDriver((UsbManager)getSystemService(Context.USB_SERVICE)); // [FTDriver] setPermissionIntent() before begin() PendingIntent permissionIntent = PendingIntent.getBroadcast(this, 0, new Intent( ACTION_USB_PERMISSION), 0); mSerial.setPermissionIntent(permissionIntent); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public void onDestroy() { super.onDestroy(); // [FTDriver] Close USB Serial mSerial.end(); } public void onBeginClick(View view) { // [FTDriver] Open USB Serial if(mSerial.begin(FTDriver.BAUD9600)) { btnBegin.setEnabled(false); btnRead.setEnabled(true); btnWrite.setEnabled(true); btnEnd.setEnabled(true); Toast.makeText(this, "connected", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "cannot connect", Toast.LENGTH_SHORT).show(); } } public void onReadClick(View view) { int i,len; // [FTDriver] Create Read Buffer byte[] rbuf = new byte[4096]; // 1byte <--slow-- [Transfer Speed] --fast--> 4096 byte // [FTDriver] Read from USB Serial len = mSerial.read(rbuf); mText.append("\n-------\n"); for(i=0; i<len; i++) { mText.append((char) rbuf[i]); } tvMonitor.setText(mText); } public void onWriteClick(View view) { String wbuf = "Hello! I'm jonki."; mSerial.write(wbuf.getBytes()); } public void onEndClick(View view) { // [FTDriver] Close USB Serial mSerial.end(); btnBegin.setEnabled(true); btnRead.setEnabled(false); btnWrite.setEnabled(false); btnEnd.setEnabled(false); Toast.makeText(this, "disconnect", Toast.LENGTH_SHORT).show(); } public void onClearClick(View view) { mText.setLength(0); tvMonitor.setText(""); } }
レイアウトファイル
<?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" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <Button android:id="@+id/btnBegin" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="12sp" android:onClick="onBeginClick" android:text="Begin"/> <Button android:id="@+id/btnRead" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="12sp" android:onClick="onReadClick" android:text="Read"/> <Button android:id="@+id/btnWrite" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="12sp" android:onClick="onWriteClick" android:text="Write"/> <Button android:id="@+id/btnEnd" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="12sp" android:onClick="onEndClick" android:text="End"/> <Button android:id="@+id/btnClear" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="12sp" android:onClick="onClearClick" android:text="Clear"/> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/tvMonitor" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="" /> </LinearLayout> </LinearLayout>
まとめ
次は小さいATTinyでこれをやろうと思います。