想了想,还是把这个项目记录一下,刚接触Android不久,这个算是第二个Android项目吧!不废话了,还是开始叙述一下项目吧!整个项目涉及Android的Wifi、Bluetooth通信,这里只介绍蓝牙部分:
一边是Android手机,另一端是串口蓝牙模块,具体是在淘宝买的(参考网址:http://item.taobao.com/item.htm?id=12792736263),串口蓝牙与AVR(ATMEGA 1280)相连。Android手机通过蓝牙向ATMEGA 1280发送指令信息来控制机器人做出相应动作。
主界面截图如下:两个EditText输入两个马达的速度,后面两个按钮设置马达转动的方向,点击发送指令开始发送,同时在下面的TextView中显示所发送的指令。
界面布局比较简单,就不再贴代码了。解释一下指令格式:前四位为固定值“CMD:”;接下来四位为ACK发送成功一次,ACK累加1;接下来四位设置第一个马达,第一位设置方向,后三位为速度;最后四位就是第二个马达的设置。
Android要使用蓝牙,首先要在AndroidManifest.xml文件中进行如下配置:
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />
在AndroidManifest.xml里面注册Service
<service android:name=".BluetoothServe.BluetoothService" android:enabled="true"></service>
1、首先是主Activity,主界面如上图,点击菜单,可以扫描远端蓝牙设备,可以设置本机蓝牙可见性,代码如下:
package com.example.android.BluetoothChat; import java.io.IOException; import com.example.android.BluetoothServe.BluetoothService; import com.example.android.BluetoothServe.BluetoothData; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothSocket; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; public class BluetoothChat extends Activity { /** Called when the activity is first created. */ private static final String TAG = "BlutoothTest"; private static final boolean D = true ; private Button btF1,btF2,btW1,btW2,sendCMD; private TextView mTextView; private TextView commandview,ackview; private EditText edit1,edit2; private BluetoothAdapter myBluetoothAdapter = null; private BluetoothSocket btSocket = null; private static final int REQUEST_CONNECT_DEVICE = 1; private String BackACK = "ACK:"; private String speed1,speed2; private int left = 1,right=1; private static int count = 1; private BluetoothData bluetoothData; //此处用于启动蓝牙服务程序 private ServiceConnection connection=new ServiceConnection(){ @Override public void onServiceConnected(ComponentName arg0, IBinder service) { bluetoothData = (BluetoothData)service; } @Override public void onServiceDisconnected(ComponentName arg0) { bluetoothData = null; } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //本地蓝牙启动 myBluetoothAdapter =BluetoothAdapter.getDefaultAdapter(); OpenBluetooth(myBluetoothAdapter); try { bindService(new Intent(BluetoothChat.this,BluetoothService.class), connection, Context.BIND_AUTO_CREATE); } catch (Exception e) { // TODO: handle exception } btF1 = (Button)findViewById(R.id.bt4); btF2 = (Button)findViewById(R.id.bt5); btW1 = (Button)findViewById(R.id.bt1); btW2 = (Button)findViewById(R.id.bt2); edit1 = (EditText) findViewById(R.id.edit1); edit2 = (EditText) findViewById(R.id.edit2); mTextView =(TextView) findViewById(R.id.mytext); commandview = (TextView) findViewById(R.id.commandview); sendCMD = (Button)findViewById(R.id.bt3); ackview = (TextView) findViewById(R.id.ackview); btF1.setOnClickListener(new myButtonClickListener()); btF2.setOnClickListener(new myButtonClickListener()); btW1.setOnClickListener(new myButtonClickListener()); btW2.setOnClickListener(new myButtonClickListener()); sendCMD.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub String command = "CMD:"; try { count = bluetoothData.getACK(); } catch (Exception e) { // TODO: handle exception } speed1 = edit1.getText().toString(); speed2 = edit2.getText().toString(); command += bluetoothData.CreateCMD(speed1, speed2, String.valueOf(count), left, right); try { bluetoothData.SendMessage(command); // bluetoothData.getACK(); } catch (Exception e) { // TODO: handle exception } commandview.setText(command); //count++; } }); } private void OpenBluetooth(BluetoothAdapter myBluetoothAdapter) { //本地蓝牙启动 if(myBluetoothAdapter == null) { Toast.makeText(this,"蓝牙不可用", Toast.LENGTH_LONG).show(); //finish(); return; } if(!myBluetoothAdapter.isEnabled()) { Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivity(enableIntent); } } class myButtonClickListener implements OnClickListener{ @Override public void onClick(View v) { // TODO Auto-generated method stub switch (v.getId()){ case R.id.bt1: left=0; mTextView.setText("马达一向后"); break; case R.id.bt2: right=0; mTextView.setText("马达二向后"); break; case R.id.bt4: left=1; mTextView.setText("马达一向前"); break; case R.id.bt5: right=1; mTextView.setText("马达二向前"); break; default: break; } } } @Override protected void onStart() { // TODO Auto-generated method stub super.onStart(); if(D) { Log.e(TAG, "++ ON START ++"); Log.e(TAG, "ABOUT TO ATTEMPT CLIENT CONNECT"); } } @Override protected void onResume(){ super.onResume(); new Thread() { public void run() { try { if(bluetoothData.getMessage() != null) BackACK += bluetoothData.getMessage(); ackview.setText(BackACK); } catch (Exception e) { // TODO: handle exception } try { sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }.start(); if(D){ Log.e(TAG, "++ ON RESUME ++"); } } public void DisplayMessage(String str) { Toast.makeText(this, str, Toast.LENGTH_LONG).show(); } //创建对话框的方法 @Override protected Dialog onCreateDialog(int id) { // TODO Auto-generated method stub AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage(BackACK) .setCancelable(false) .setPositiveButton("YES", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // TODO Auto-generated method stub //MyActivity.this.finish(); dialog.dismiss(); } }); AlertDialog alert = builder.create(); return alert; //return super.onCreateDialog(id); } @Override protected void onPause() { // TODO Auto-generated method stub super.onPause(); if(D){ Log.e(TAG, "++ ON PAUSE ++"); } } @Override protected void onStop() { // TODO Auto-generated method stub super.onStop(); // count = 1; if(D) { Log.e(TAG, "++ ON STOP ++"); } } @Override protected void onDestroy() { // TODO Auto-generated method stub unbindService(connection); super.onDestroy(); //count = 1; if (D) { Log.e(TAG, "++ ON DESTROY ++"); } try { if(btSocket != null) btSocket.close(); } catch (IOException e) { Log.e(TAG, "Close failed"); } } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.option_menu, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.scan: // Launch the DeviceListActivity to see devices and do scan Intent serverIntent = new Intent(this, DeviceListActivity.class); startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE); return true; case R.id.discoverable: // Ensure this device is discoverable by others ensureDiscoverable(); return true; } return false; } //设置本机蓝牙可见性 private void ensureDiscoverable() { if(D) Log.d(TAG, "ensure discoverable"); if (myBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); startActivity(discoverableIntent); } } }
2、下面是蓝牙服务程序
package com.example.bote.BluetoothServe; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Method; import java.util.UUID; import android.app.Service; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothSocket; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.util.Log; import android.widget.Toast; public class BluetoothService extends Service { private static final UUID myUUID = UUID .fromString("00001101-0000-1000-8000-00805F9B34FB"); private static String address = "00:12:08:21:06:01";//远端蓝牙地址 private BluetoothAdapter myBluetoothAdapter = BluetoothAdapter .getDefaultAdapter(); //获取本地蓝牙信息 private BluetoothDevice mDevice = myBluetoothAdapter .getRemoteDevice(address); //获取远端蓝牙信息 private BluetoothSocket btSocket = null; private InputStream inputStream = null; private OutputStream outputStream = null; private int temp = 0; private String CMD = null; private String BackACK = null; private static final String TAG = "BlutoothService"; private int ACK=0; /*Binder类实现BluetoothData接口*/ public class myIBinder extends Binder implements BluetoothData { BluetoothService getService() { return BluetoothService.this; } /*发送指令函数,参数为指令信息,发送成功返回true,否则返回false*/ public boolean SendMessage(String str) { // TODO Auto-generated method stub byte[] msgBuffer; msgBuffer = str.getBytes(); try { if (btSocket != null) { outputStream = btSocket.getOutputStream();//打开输出流 } } catch (IOException e) { Log.e(TAG, "ON Resume:output stream creation failed"); } try { outputStream.write(msgBuffer); ACK++; //发送成功后,ACK自动加一 if(ACK>9999){ ACK=0; } return true; } catch (IOException e) { // TODO: handle exception Log.e(TAG, "On Resume:Exception during write"); DisplayMessage("发送失败!"); } return false; } /*获取ACK信息并返回*/ public String getMessage() { // TODO Auto-generated method stub if (BackACK != null) { return BackACK; } return null; } /*初始速度设置函数,向船发送指令,设置两个马达的初始速度,发送成功返回true,否则返回false*/ public boolean Sendmessagelr(int left, int right) { // TODO Auto-generated method stub String string = null; string=CreateCMD(String.valueOf(left),String.valueOf(right), String.valueOf(ACK),1,1); SendMessage(string); return false; } /*生成指令函数,通过传递速度、ACK以及方向信息来生成一个指令 * 指令的前4位表示ACK,不足四位的前面补零 * 接下来4位为第一个马达信息,第一位表示方向,为0表示反转,为1表示正转,后三位为速度大小,不足3位的前面补零 * 最后4位为第二个马达信息,第一位表示方向,为0表示反转,为1表示正转,后三位为速度大小,不足3位的前面补零*/ public String CreateCMD(String speed1, String speed2, String ACK, int left, int right) { // TODO Auto-generated method stub temp = ACK.length(); while (temp < 4){ ACK='0'+ACK; temp++; } CMD = ACK; temp = speed1.length(); while (temp < 3){ speed1='0'+speed1; temp++; } CMD += (left+speed1); temp = speed2.length(); while (temp < 3){ speed2='0'+speed2; temp++; } CMD += (right+speed2); return CMD+'!'; } } private IBinder mBinder = new myIBinder(); @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub System.out.println("onBind Service"); return mBinder; } @Override public void onCreate() { // TODO Auto-generated method stub System.out.println("onCreate Service"); DisplayMessage("Bluetooth Service Start"); DisplayMessage("正在尝试连接蓝牙设备" + mDevice + "请稍后···"); /*将蓝牙连接建立过程放到新线程中*/ new Thread() { public void run() { //如果本地蓝牙尚未开启,无法建立连接,必须等待蓝牙开启 if (!myBluetoothAdapter.isEnabled()) { try { Log.e(TAG, "线程休眠"); sleep(8000); //线程睡眠,等待蓝牙开启 } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } Log.e(TAG, "线程开始"); try { btSocket = mDevice .createRfcommSocketToServiceRecord(myUUID); //创建socket通信 } catch (IOException e) { // TODO Auto-generated catch block DisplayMessage("创建套接字失败!"); e.printStackTrace(); } try { connectDevice(); //调用蓝牙连接函数来建立连接 } catch (Exception e) { // TODO: handle exception } } }.start(); super.onCreate(); } @Override public void onDestroy() { // TODO Auto-generated method stub System.out.println("onDestroy Service"); super.onDestroy(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { // TODO Auto-generated method stub Log.i("LocalService", "Received Start ID" + startId + ":" + intent); System.out.println("onStartCommand Service"); return START_STICKY; } @Override public void onStart(Intent intent, int startId) { // TODO Auto-generated method stub System.out.println("onStart Service"); getMessageThread getMessage = new getMessageThread(); getMessage.start(); super.onStart(intent, startId); } @Override public boolean onUnbind(Intent intent) { // TODO Auto-generated method stub System.out.println("onUnbind Service"); return super.onUnbind(intent); } public void DisplayMessage(String str) { Toast.makeText(this, str, Toast.LENGTH_LONG).show(); //显示Toast信息 } protected void connectDevice() { try { // 连接建立之前的先配对 if (mDevice.getBondState() == BluetoothDevice.BOND_NONE) { Method creMethod = BluetoothDevice.class .getMethod("createBond"); Log.e("TAG", "开始配对"); creMethod.invoke(mDevice); } else { } } catch (Exception e) { // TODO: handle exception DisplayMessage("无法配对!"); e.printStackTrace(); } myBluetoothAdapter.cancelDiscovery(); try { btSocket.connect(); DisplayMessage("连接成功!"); } catch (IOException e) { // TODO: handle exception DisplayMessage("连接失败!"); try { btSocket.close(); } catch (IOException e2) { // TODO: handle exception Log.e(TAG, "Cannot close connection when connection failed"); } } } //从蓝牙端获取信息类,这个方法不一定对或者并没有完善 public class getMessageThread extends Thread { public void run() { if (btSocket != null) { try { inputStream = btSocket.getInputStream(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } try { BackACK = Integer.toString(inputStream.read()); } catch (Exception e) { // TODO: handle exception } } } }
/* * 蓝牙服务接口 * */ package com.example.bote.BluetoothServe; public interface BluetoothData { /*发送指令函数,参数为指令信息,发送成功返回true,否则返回false*/ public boolean SendMessage(String str); /*从蓝牙端获取信息函数,返回的信息为获取到的字符窜*/ public String getMessage(); /*初始速度设置函数,向船发送指令,设置两个马达的初始速度,发送成功返回true,否则返回false*/ public boolean Sendmessagelr(int left, int right); /*生成指令函数,通过传递速度、ACK以及方向信息来生成一个指令*/ public String CreateCMD(String speed1, String speed2, String ACK,int left, int right); }
// The on-click listener for all devices in the ListViews private OnItemClickListener mDeviceClickListener = new OnItemClickListener() { public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) { // Cancel discovery because it's costly and we're about to connect mBtAdapter.cancelDiscovery(); // Get the device MAC address, which is the last 17 chars in the View String info = ((TextView) v).getText().toString(); String address = info.substring(info.length() - 17); BluetoothService nService = new BluetoothService(); // Create the result Intent and include the MAC address Intent intent = new Intent(); intent.putExtra(EXTRA_DEVICE_ADDRESS, address); nService.onStart(intent,1); //启动蓝牙服务 setResult(Activity.RESULT_OK, intent); finish(); } };