android手机通过串口蓝牙透传模块与AVR单片机通信实例。。。蓝牙服务程序案例

想了想,还是把这个项目记录一下,刚接触Android不久,这个算是第二个Android项目吧!不废话了,还是开始叙述一下项目吧!整个项目涉及Android的Wifi、Bluetooth通信,这里只介绍蓝牙部分:

一边是Android手机,另一端是串口蓝牙模块,具体是在淘宝买的(参考网址:http://item.taobao.com/item.htm?id=12792736263),串口蓝牙与AVR(ATMEGA 1280)相连。Android手机通过蓝牙向ATMEGA 1280发送指令信息来控制机器人做出相应动作。

主界面截图如下:两个EditText输入两个马达的速度,后面两个按钮设置马达转动的方向,点击发送指令开始发送,同时在下面的TextView中显示所发送的指令。

android手机通过串口蓝牙透传模块与AVR单片机通信实例。。。蓝牙服务程序案例_第1张图片

界面布局比较简单,就不再贴代码了。解释一下指令格式:前四位为固定值“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);
        }
    }
}

开始是将蓝牙服务程序作为主界面的一个进程,结果运行速度比较慢,由于程序一启动就进行蓝牙连接,所以主进程被阻塞,导致程序启动后过好一会才有反应,而且手机一旦锁屏后,蓝牙连接被断开,解锁后又重新进行 蓝牙连接,觉得很不方便,效率不高。所以把整个蓝牙的连接建立、蓝牙数据传送以及其他处理放到service里面,程序启动后,启动蓝牙服务程序一直处于后台运行,当要传递数据时直接调用service里面的服务即可。

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
			}
		}
	}
}


3、那Activity和Service如何传递数据呢?从service程序总可以看出,多了一个myIBinder类继承自Binder,并且实现接口BluethData,对了,就是通过这个接口来实现Activity与Service传递数据,接口定义如下:

/*
 * 蓝牙服务接口  
 * */

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);
}


4、如果这样直接制定远端蓝牙的地址建立连接,那么当要改变地址时,得手动修改源程序的蓝牙地址,这样显得不人性化,所以添加一个扫描蓝牙地址的功能。扫描程序没必要自己写,下面是Android自带的例子BluetoothChat里面的DeviceListActivity.java的内容,直接用就行,不过,有个地方需要修改一下,就是当点击扫描到的蓝牙地址时,需要重新启动蓝牙服务程序,直接调用service里面的OnStart()方法就行。修改如下:
 
// 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();
        }
    };

这是几个月前做的项目,现在来梳理,可能有些地方有问题。还有此处的重点是向单片机发送信息,并没有处理从单片机回送的信息,因此简化了蓝牙接收信息的过程,当然处理起来也不难,网上有很多教程。请多多指教~





   

你可能感兴趣的:(android,android,android)