NXT主機上有藍芽BlueTooth因此能傳送與接收其他裝置的藍芽資料,本篇要實做用一般常見的Android手機與Lego NXT主機透過藍芽做資料交換。
準備的物件
1.Android手機一隻新舊款沒關係但要有藍芽功能。
2.Lego Mindstorms NXT 或EV3,馬達3顆接 A、B、C Port
3.先將手機與NXT 的藍芽都打開,讓手機尋找NXT主機內定密碼為1234,連結成功後手機上就有這台NXT的資訊與名字作為手機App要連結的目標。
由於Android手機有很多Sensor如重力感應器、方向感應器、光線感應器、GPS等如能加以應用等於幫NXT省下不少外購Sensor費用。
如果大家有淘汰的Android手機可以試試看本篇的應用。
第 一部份開發 Android App程式:
我在App主畫面上放了 9個 Button以方便未來擴充應用
執行 App 程式的主畫面
顯示手機上 Sensor 資訊
原始碼如下:開發環境 Eclipse 與 Android、Lejos套件
package com.example.lego_android;
import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.LinearLayout;
import android.graphics.Color;
import android.view.Window;
import android.widget.*;
import android.graphics.drawable.Drawable;
import android.view.Gravity;
import android.text.TextPaint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import java.util.*;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import android.content.*;
public class MainActivity extends Activity {
private int MP=LinearLayout.LayoutParams.MATCH_PARENT;
private int WC=LinearLayout.LayoutParams.WRAP_CONTENT;
private TableRow.LayoutParams view_layout2;
private TextView txtID;
private TextView txtSync;
private TextView txtTitle01;
private TextView txtTrending;
private TextView txtTitle02;
private String strTimeCnt="";
private TextView txtVer;
private boolean chkvername=false;
private String strVer="";
private int ifisze=18;
private int iChnl=0;
private String strTitleH="LegoRun 資 訊 管 理";
private BluetoothAdapter adapter;
private BluetoothSocket nxtSocket;
public DataInputStream nxtDataIn;
public DataOutputStream nxtDataOut;
public final int MODE_CONTROL = 1;
private int mode;
private boolean bConnect_NXT=false;
private String NXTUUID = "00001101-0000-1000-8000-00805F9B34FB";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout layout=new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setLayoutParams(new LinearLayout.LayoutParams(MP,MP));
layout.setBackgroundColor(Color.rgb(19,92,160));
//標題隱藏
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(layout);
TableLayout tl= new TableLayout(this);
tl.setLayoutParams(new LinearLayout.LayoutParams(MP,MP));
tl.setBackgroundColor(Color.parseColor("#333333"));
tl.setStretchAllColumns(true);
layout.addView(tl);
TableLayout.LayoutParams row_layout_mid = new TableLayout.LayoutParams();
row_layout_mid.weight=1f;
row_layout_mid.setMargins(0,1,0,1); //setMargins(left, top, right, bottom);
TableLayout.LayoutParams row_layout_max = new TableLayout.LayoutParams();
row_layout_max.weight=8f;
row_layout_max.setMargins(0,1,0,1); //setMargins(left, top, right, bottom);
TableLayout.LayoutParams row_layout_min = new TableLayout.LayoutParams();
row_layout_min.weight=0.2f;
row_layout_min.setMargins(0,1,0,1); //setMargins(left, top, right, bottom);
TableRow.LayoutParams view_layout1 = new TableRow.LayoutParams(MP,MP);
view_layout1.span=2;
view_layout2 = new TableRow.LayoutParams(getScrWH(MainActivity.this)[0]/2,MP);
view_layout2.setMargins(1,0,0,0);
TableRow tr1 = new TableRow(this);
tr1.setLayoutParams(row_layout_mid);
tr1.setGravity(Gravity.CENTER_HORIZONTAL);
txtTitle01 = new TextView(this);
txtTitle01.setLayoutParams(view_layout1);
txtTitle01.setText(strTitleH);
txtTitle01.setBackgroundColor(Color.parseColor("#00C9C9")); //#46C7C7
txtTitle01.setGravity(Gravity.CENTER);
TextPaint tp = txtTitle01.getPaint();
tp.setFakeBoldText(true);
txtTitle01.setTextColor(Color.parseColor("#EEEEEE"));
txtTitle01.setOnClickListener(new ButtonClickListener());
txtTitle01.setTextSize(ifisze);
txtTitle01.setTag("txtTitle01");
tr1.addView(txtTitle01);
tl.addView(tr1);
TableRow tr2 = new TableRow(this);
tr2.setLayoutParams(row_layout_mid);
tr2.setGravity(Gravity.CENTER_HORIZONTAL);
txtTitle02 = new TextView(this);
txtTitle02.setText("尚未連結Lego NXT...");
txtTitle02.setTextSize(18);
txtTitle02.setLayoutParams(view_layout1);
txtTitle02.setGravity(Gravity.CENTER);
txtTitle02.setBackgroundColor(Color.WHITE);
tr2.addView(txtTitle02);
tl.addView(tr2);
TableRow tr3 = new TableRow(this);
tr3.setLayoutParams(row_layout_max);
tr3.setGravity(Gravity.CENTER_HORIZONTAL);
tl.addView(tr3);
LinearLayout layoutBrick=new LinearLayout(this);
layoutBrick.setOrientation(LinearLayout.VERTICAL);
layoutBrick.setLayoutParams(view_layout1);
layoutBrick.setBackgroundColor(Color.parseColor("#95B9C7"));
tr3.addView(layoutBrick);
TableLayout tl_Brick= new TableLayout(this);
tl_Brick.setLayoutParams(new LinearLayout.LayoutParams(MP,MP));
tl_Brick.setBackgroundColor(Color.parseColor("#FFFFFF"));
tl_Brick.setStretchAllColumns(true);
layoutBrick.addView(tl_Brick);
TableLayout.LayoutParams row_layout_Brick = new TableLayout.LayoutParams();
row_layout_Brick.weight=1f;
TableRow.LayoutParams view_layout_Brick = new TableRow.LayoutParams(MP,MP);
view_layout_Brick.weight=0.7f;
int ibtn=0;
for(int i=0;i<=2;i++){
TableRow tr_Brick = new TableRow(this);
tr_Brick.setLayoutParams(row_layout_Brick);
tr_Brick.setGravity(Gravity.CENTER_HORIZONTAL);
tl_Brick.addView(tr_Brick);
for(int j=0;j<=2;j++){
Button btn_Brick = new Button(this);
btn_Brick.setLayoutParams(view_layout_Brick);
btn_setup(btn_Brick,ibtn); //設定按鈕外觀圖形
btn_Brick.setOnClickListener(new ButtonClickListener());
tr_Brick.addView(btn_Brick);
ibtn++;
}
}
TableRow tr4 = new TableRow(this);
tr4.setLayoutParams(row_layout_min);
tr4.setGravity(Gravity.CENTER_HORIZONTAL);
txtTrending = new TextView(this);
txtTrending.setText("資料設定");
txtTrending.setTextSize(18);
txtTrending.setLayoutParams(view_layout1);
txtTrending.setGravity(Gravity.CENTER);
txtTrending.setTag("txtTrending");
txtTrending.setOnClickListener(new ButtonClickListener());
txtTrending.setBackgroundColor(Color.parseColor("#E5E4E2"));
tr4.addView(txtTrending);
tl.addView(tr4);
//-------------------------------------------
TableRow tr5 = new TableRow(this);
tr5.setLayoutParams(row_layout_mid);
tr5.setGravity(Gravity.CENTER_HORIZONTAL);
txtID = new TextView(this);
setTxtView(txtID,"帳 號 設 定","txtID");
tr5.addView(txtID);
txtSync = new TextView(this);
setTxtView(txtSync,"資 料 更 新","txtSync");
tr5.addView(txtSync);
tl.addView(tr5);
//----------------------------------------------
TableRow tr6 = new TableRow(this);
tr6.setLayoutParams(row_layout_mid);
tr6.setGravity(Gravity.CENTER_HORIZONTAL);
TextView txtHelp = new TextView(this);
setTxtView(txtHelp,"使 用 說 明","txtHelp");
tr6.addView(txtHelp);
txtVer = new TextView(this);
setTxtView(txtVer,"版 本 資 訊","txtVer");
tr6.addView(txtVer);
tl.addView(tr6);
//藍芽啟用
adapter = BluetoothAdapter.getDefaultAdapter();
if(adapter==null)
{
Toast.makeText(getApplication(), "No Bluetooth adapter found", Toast.LENGTH_SHORT).show();
this.finish();
}
if(!adapter.isEnabled())
startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE), 1);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public int[] getScrWH(Activity activity){
int scrWH[]={0,0};
DisplayMetrics dm = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
scrWH[0] = dm.widthPixels;
scrWH[1] = dm.heightPixels;
return scrWH;
}
//按鈕事件
class ButtonClickListener implements OnClickListener {
// onClick 方法(觸碰按鈕時的事件處理器)
public void onClick(View v){
//取得 tag
String tag = (String)v.getTag();
if (tag.equals("btn0")){
if(((Button)v).getText().equals("藍芽連結")){
txtTitle02.setText("尋找藍芽中...");
//開啟藍芽
if(connectNxt())
((Button)v).setText("關閉藍芽");
}else{
try {
txtTitle02.setText("傳送功能 = 關閉藍芽連結");
nxtDataOut.writeInt(999);
nxtDataOut.flush();
nxtSocket.close();
bConnect_NXT=false;
} catch (IOException e) {
e.printStackTrace();
}
((Button)v).setText("藍芽連結");
}
}
//傳送資料
if (tag.equals("btn1")){
try {
if(((Button)v).getText().equals("啟動馬達A")){
txtTitle02.setText("傳送功能 = 啟動馬達A");
nxtDataOut.writeInt(0);
nxtDataOut.flush();
((Button)v).setText("關閉馬達A");
}else{
txtTitle02.setText("傳送功能 = 關閉馬達A");
nxtDataOut.writeInt(1);
nxtDataOut.flush();
((Button)v).setText("啟動馬達A");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (tag.equals("btn2")){
try {
if(((Button)v).getText().equals("啟動馬達B")){
txtTitle02.setText("傳送功能 = 啟動馬達B");
nxtDataOut.writeInt(2);
nxtDataOut.flush();
((Button)v).setText("關閉馬達B");
}else{
txtTitle02.setText("傳送功能 = 關閉馬達B");
nxtDataOut.writeInt(3);
nxtDataOut.flush();
((Button)v).setText("啟動馬達B");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (tag.equals("btn3")){
try {
if(((Button)v).getText().equals("啟動馬達C")){
txtTitle02.setText("傳送功能 = 啟動馬達C");
nxtDataOut.writeInt(4);
nxtDataOut.flush();
((Button)v).setText("關閉馬達C");
}else{
txtTitle02.setText("傳送功能 = 關閉馬達C");
nxtDataOut.writeInt(5);
nxtDataOut.flush();
((Button)v).setText("啟動馬達C");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
private boolean connectNxt()
{
if(bConnect_NXT){
txtTitle02.setText("手機已經連結成功不須再次連結");
return false;
}
String name="NXT8547";
BluetoothDevice nxt = null;
Set
devicesSet = adapter.getBondedDevices();
if(devicesSet.size()==0)
{
txtTitle02.setText("手機上無任何藍芽連結裝置");
return false;
}
for (BluetoothDevice device : devicesSet)
{
if (device.getName().equals(name))
{
nxt = device;
break;
}
}
if(nxt==null)
{
txtTitle02.setText("找不到Lego NXT name:"+name);
return false;
}
try
{
nxtSocket = nxt.createRfcommSocketToServiceRecord(UUID.fromString(NXTUUID));
nxtSocket.connect();
nxtDataOut = new DataOutputStream(nxtSocket.getOutputStream());
nxtDataIn = new DataInputStream(nxtSocket.getInputStream());
}
catch(IOException e)
{
txtTitle02.setText("連結此裝置:"+name+" 失敗!!");
return false;
}
txtTitle02.setText("連結此裝置:"+name+" 成功!!");
bConnect_NXT=true;
return true;
}
private void setTxtView(TextView v,String strText,String strTag){
v.setText(strText);
v.setTextSize(ifisze);
v.setLayoutParams(view_layout2);
v.setGravity(Gravity.CENTER);
v.setBackgroundColor(Color.parseColor("#F3F3F3"));
v.setTag(strTag);
v.setOnClickListener(new ButtonClickListener());
}
private void btn_setup(Button btn_Brick,int ibtn){
Context context=getApplicationContext();
Drawable[][] image=new Drawable[9][2];
image[0][0]=context.getResources().getDrawable(R.drawable.net_42_128);
image[0][1]=context.getResources().getDrawable(R.drawable.net_42_72);
image[1][0]=context.getResources().getDrawable(R.drawable.net_24_128);
image[1][1]=context.getResources().getDrawable(R.drawable.net_24_72);
image[2][0]=context.getResources().getDrawable(R.drawable.net_18_128);
image[2][1]=context.getResources().getDrawable(R.drawable.net_18_72);
image[3][0]=context.getResources().getDrawable(R.drawable.net_08_128);
image[3][1]=context.getResources().getDrawable(R.drawable.net_08_72);
image[4][0]=context.getResources().getDrawable(R.drawable.net_21_128);
image[4][1]=context.getResources().getDrawable(R.drawable.net_21_72);
image[5][0]=context.getResources().getDrawable(R.drawable.net_27_128);
image[5][1]=context.getResources().getDrawable(R.drawable.net_27_72);
image[6][0]=context.getResources().getDrawable(R.drawable.net_22_128);
image[6][1]=context.getResources().getDrawable(R.drawable.net_22_72);
image[7][0]=context.getResources().getDrawable(R.drawable.net_01);
image[7][1]=context.getResources().getDrawable(R.drawable.net_23_72);
image[8][0]=context.getResources().getDrawable(R.drawable.net_26_128);
image[8][1]=context.getResources().getDrawable(R.drawable.net_26_72);
String[][] strBtn=new String[9][2];
strBtn[0][0]="藍芽連結";
strBtn[0][1]="btn0";
strBtn[1][0]="啟動馬達A";
strBtn[1][1]="btn1";
strBtn[2][0]="啟動馬達B";
strBtn[2][1]="btn2";
strBtn[3][0]="啟動馬達C";
strBtn[3][1]="btn3";
strBtn[4][0]="啟動光線";
strBtn[4][1]="btn6";
strBtn[5][0]="Fun05";
strBtn[5][1]="btn7";
strBtn[6][0]="Fun06";
strBtn[6][1]="btn4";
strBtn[7][0]="Fun07";
strBtn[7][1]="btn5";
strBtn[8][0]="Fun08";
strBtn[8][1]="btn8";
btn_Brick.setText(strBtn[ibtn][0]);
btn_Brick.setTextSize(ifisze);
btn_Brick.setTag(strBtn[ibtn][1]);
//-------------
int spacing=0;
int width=btn_Brick.getMeasuredWidth();
int height=btn_Brick.getMeasuredHeight();
int txt_width=(int)(btn_Brick.getTextSize()*btn_Brick.getText().length());
int txt_height=(int)(btn_Brick.getLineCount()*btn_Brick.getLineHeight());
int img_width=0;
int img_height=0;
int content_height=0;
int content_width=0;
int padding_w=0;
int padding_h=0;
int isel=0;
if(getScrWH(this)[0]>=720){
img_width=image[ibtn][0].getIntrinsicWidth();
img_height=image[ibtn][0].getIntrinsicHeight();
isel=0;
}else{
img_width=image[ibtn][1].getIntrinsicWidth();
img_height=image[ibtn][1].getIntrinsicHeight();
isel=1;
}
content_height=txt_height+img_height+spacing;
content_width=txt_width+img_width+spacing;
padding_w=width/2-content_width/2;
padding_h=height/2-content_height/2;
btn_Brick.setCompoundDrawablesWithIntrinsicBounds( null,image[ibtn][isel], null, null);
////setMargins(left, top, right, bottom);
//btn_Brick.setPadding(0,padding_h,0,0);
btn_Brick.setCompoundDrawablePadding(padding_h/2);
//----------------
}
}
第二部份開發Lejos NXT 程式
import lejos.nxt.Button;
import lejos.nxt.ButtonListener;
import lejos.nxt.LCD;
import lejos.nxt.Motor;
import lejos.nxt.comm.BTConnection;
import lejos.nxt.comm.Bluetooth;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import lejos.nxt.comm.*;
import lejos.util.Delay;
public class BT_Frm01 {
public static void main(String[] args)throws Exception {
// TODO Auto-generated method stub
Button.ESCAPE.addButtonListener(new ButtonListener(){
public void buttonPressed(Button b){System.exit(1);}
public void buttonReleased(Button b){}
});
LCD.drawString("Waiting",0,0);
BTConnection btc = Bluetooth.waitForConnection(0, NXTConnection.RAW);
DataInputStream din = btc.openDataInputStream();
DataOutputStream dout = btc.openDataOutputStream();
LCD.drawString("Connected",0,0);
Motor.A.setSpeed(450);
Motor.B.setSpeed(450);
Motor.C.setSpeed(450);
while(true){
int iData=din.readInt();
LCD.clear();
LCD.drawString("value="+String.valueOf(iData),0,2);
if(iData==999){
btc.close();
System.exit(1);
break;
}
switch(iData){
case 0:
Motor.A.forward();
break;
case 1:
Motor.A.stop();
break;
case 2:
Motor.B.forward();
break;
case 3:
Motor.B.stop();
break;
case 4:
Motor.C.forward();
break;
case 5:
Motor.C.stop();
break;
}
Delay.msDelay(1000);
}
}
}
除了手機可跟NTX互動溝通外不同的NXT主機也可以互動,這是我用第一台NXT主機當控制器透過BT遙控第二台NXT車輛的影片,第一隻馬達控制前輪方向動作、第二隻馬達控制後輪前進後退動作
VIDEO