你的位置:首页 > 操作系统

[操作系统]Android蓝牙实例(和单片机蓝牙模块通信)


  最近做毕设,需要写一个简单的蓝牙APP进行交互,在网上也找了很多资料,终于给搞定了,这里分享一下^_^。

1、Android蓝牙编程

  蓝牙3.0及以下版本编程需要使用UUID,UUID是通用唯一识别码(Universally Unique Identifier),这是一个软件构建的标准,也是被开源基金会组织应用在分布式计算环境领域的一部分。在蓝牙3.0及下一版本中,UUID被用于唯一标识一个服务,比如文件传输服务,串口服务、打印机服务等,如下:

#蓝牙串口服务 
SerialPortServiceClass_UUID = '{00001101-0000-1000-8000-00805F9B34FB}' 
LANAccessUsingPPPServiceClass_UUID = '{00001102-0000-1000-8000-00805F9B34FB}'

#拨号网络服务 
DialupNetworkingServiceClass_UUID = '{00001103-0000-1000-8000-00805F9B34FB}'

#信息同步服务 
IrMCSyncServiceClass_UUID = '{00001104-0000-1000-8000-00805F9B34FB}' 
SDP_OBEXObjectPushServiceClass_UUID = '{00001105-0000-1000-8000-00805F9B34FB}'

#文件传输服务 
OBEXFileTransferServiceClass_UUID = '{00001106-0000-1000-8000-00805F9B34FB}' 
IrMCSyncCommandServiceClass_UUID = '{00001107-0000-1000-8000-00805F9B34FB}'

  蓝牙的连接有主从设备,提供服务的可以认为是从设备。主设备通过UUID访问从设备提供具有相同UUID的服务,从而建立客服端—服务器(C/S)模式。

 2、编程步骤

Android使用蓝牙,需要获得权限,蓝牙权限获得代码如下:

<!-- 蓝牙权限 --><uses-permission android:name="android.permission.BLUETOOTH"/><uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

获取本地蓝牙适配器,如果蓝牙未开启,开启蓝牙设备:

 

bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();  if (bluetoothAdapter == null) {  // Device does not support Bluetooth  return;}// 开启蓝牙int REQUEST_ENABLE_BT = 1;if (!bluetoothAdapter.isEnabled()) {  Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);  startActivityForResult(intent, REQUEST_ENABLE_BT);}

 搜索已配对的蓝牙设备,并添加到已配对列表中:

// 查询配对设备List<String> devices = new ArrayList<String>();Set<BluetoothDevice> bondedDevices = bluetoothAdapter.getBondedDevices();for (BluetoothDevice device : bondedDevices) {  devices.add(device.getName() + "-" + device.getAddress());}

搜索未配对蓝牙设备,并添加到未配对列表:

mBluetoothAdapter.startDiscovery();//开始收索 搜索接收函数: final BroadcastReceiver mReceiver = new BroadcastReceiver() {  public void onReceive(Context context, Intent intent) {             String action = intent.getAction();          // When discovery finds a device              if (BluetoothDevice.ACTION_FOUND.equals(action)) {      // Get the BluetoothDevice object from the Intent       BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);       // Add the name and address to an array adapter to show in a ListView       mArrayAdapter.add(device.getName() + "\n" + device.getAddress());      }  }};// 收索接收函数需要注册:  // Register the BroadcastReceiver   IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy

如果是服务器端,需要建立监听,注意监听的是某个服务的UUID,服务器监听类如下:

private class ConnectThread extends Thread {  private final String MY_UUID = "00001101-0000-1000-8000-00805F9B34FB";  private final BluetoothSocket socket;  private final BluetoothDevice device;  public ConnectThread(BluetoothDevice device) {    this.device = device;    BluetoothSocket tmp = null;    try {      tmp = device.createRfcommSocketToServiceRecord(UUID.fromString(MY_UUID));    } catch (IOException e) {      e.printStackTrace();    }    this.socket = tmp;  }  public void run() {    bluetoothAdapter.cancelDiscovery();    try {      socket.connect();      connectedThread = new ConnectedThread(socket);      connectedThread.start();    } catch (IOException e) {      try {        socket.close();      } catch (IOException ee) {        ee.printStackTrace();      }      return;    }    //manageConnectedSocket(socket);  }  public void cancel() {    try {      socket.close();    } catch (IOException e) {      e.printStackTrace();    }  }}

客户端与服务器端建立连接成功后,需要ConnectedThread类接收发送数据:

// 客户端与服务器建立连接成功后,用ConnectedThread收发数据private class ConnectedThread extends Thread {  private final BluetoothSocket socket;  private final InputStream inputStream;  private final OutputStream outputStream;  public ConnectedThread(BluetoothSocket socket) {    this.socket = socket;    InputStream input = null;    OutputStream output = null;    try {      input = socket.getInputStream();      output = socket.getOutputStream();    } catch (IOException e) {      e.printStackTrace();    }    this.inputStream = input;    this.outputStream = output;  }  public void run() {    byte[] buff = new byte[1024];    int bytes;    while (true) {      try {        bytes = inputStream.read(buff);        String str = new String(buff, "ISO-8859-1");        str = str.substring(0, bytes);        Log.e("recv", str);      } catch (IOException e) {        e.printStackTrace();        break;      }    }  }  public void write(byte[] bytes) {    try {      outputStream.write(bytes);    } catch (IOException e) {      e.printStackTrace();    }  }  public void cancel() {    try {      socket.close();    } catch (IOException e) {      e.printStackTrace();    }  }}

  到此为止就是蓝牙开发的大致步骤,其中没有涉及到蓝牙客户端建立连接类,不过可查阅BLE和经典蓝牙Android开发。

 3、毕设蓝牙APP介绍

  毕设蓝牙APP需要接收单片机通过蓝牙模块发送上来的数据,并且蓝牙APP也可以给单片机发送数据来进行控制。页面布局如下,一个是整体页面,一个是设置页面,测试手机是魅蓝note。因为毕设做的是十字路口红绿灯控制系统,所有页面布局有4个LED灯,分别代表路口的4个红绿灯,会根据时间不同显示不同的颜色(红/绿/黄),并且会显示倒计时,最后来一张红绿灯系统整体图。

   

activity_main.

<?

MainActivity.java文件如下:

package com.luoxn28.bluetooth;import android.bluetooth.BluetoothAdapter;import android.bluetooth.BluetoothDevice;import android.bluetooth.BluetoothSocket;import android.content.Intent;import android.graphics.Color;import android.os.Bundle;import android.os.Message;import android.support.v7.app.ActionBarActivity;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.ScrollView;import android.widget.TextView;import android.widget.Toast;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.util.ArrayList;import java.util.List;import java.util.Set;import java.util.UUID;public class MainActivity extends ActionBarActivity implements View.OnClickListener {  public static final int RECV_VIEW = 0;  public static final int NOTICE_VIEW = 1;  private BluetoothAdapter bluetoothAdapter = null;  private ConnectThread connectThread = null;  private ConnectedThread connectedThread = null;  private TextView noticeView = null;  private Button turnOnOff = null;  private TextView led0, led1, led2, led3, led4;  ScrollView scrollView = null;  private TextView recvView = null;  private Button clearRecvView = null;  private EditText sendText = null;  private Button send = null;  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    // 获取BluetoothAdapter    bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();    if (bluetoothAdapter == null) {      // Device does not support Bluetooth      return;    }    // 注册监听事件    noticeView = (TextView) findViewById(R.id.notice_view);    turnOnOff = (Button) findViewById(R.id.turn_on_off);    led0 = (TextView) findViewById(R.id.led0);    led1 = (TextView) findViewById(R.id.led1);    led2 = (TextView) findViewById(R.id.led2);    led3 = (TextView) findViewById(R.id.led3);    led4 = (TextView) findViewById(R.id.led4);    scrollView = (ScrollView) findViewById(R.id.scroll_view);    recvView = (TextView) findViewById(R.id.recv_view);    clearRecvView = (Button) findViewById(R.id.clear_recv_view);    sendText = (EditText) findViewById(R.id.send_text);    send = (Button) findViewById(R.id.send);    turnOnOff.setOnClickListener(this);    clearRecvView.setOnClickListener(this);    send.setOnClickListener(this);    if (!bluetoothAdapter.isEnabled()) {      noticeView.setText("蓝牙未开启");    }    else {      noticeView.setText("蓝牙已开启");    }    noticeView.setBackgroundColor(Color.GRAY);    led0.setBackgroundColor(Color.GRAY);    led1.setBackgroundColor(Color.GRAY);    led2.setBackgroundColor(Color.GRAY);    led3.setBackgroundColor(Color.GRAY);    led4.setBackgroundColor(Color.GRAY);  }  private boolean isOn = false;  @Override  public void onClick(View view) {    switch (view.getId()) {      case R.id.turn_on_off: // 发送'0'或者'1'都可以        if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {          Toast.makeText(this, "蓝牙未开启", Toast.LENGTH_SHORT).show();          break;        }        if (connectedThread == null) {          Toast.makeText(this, "未连接设备", Toast.LENGTH_SHORT).show();          break;        }        String turn_string = "1@#";        connectedThread.write(turn_string.getBytes());        if (isOn == false) {          isOn = true; // 打开了          turnOnOff.setText("OFF");          led1.setText("");          led2.setText("");          led3.setText("");          led4.setText("");        }        else {          isOn = false; // 关闭了          turnOnOff.setText("ON");          led1.setText("LED1");          led2.setText("LED2");          led3.setText("LED3");          led4.setText("LED4");        }        break;      case R.id.clear_recv_view: // 清空接收框        recvView.setText("");        break;      case R.id.send: // 发送数据,默认以"@#"结尾        if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {          Toast.makeText(this, "蓝牙未开启", Toast.LENGTH_SHORT).show();          return;        }        if (connectedThread == null) {          Toast.makeText(this, "未连接设备", Toast.LENGTH_SHORT).show();          break;        }        String inputText = sendText.getText().toString() + "@#"; // 发送给单片机数据以"@#结尾",这样单片机知道一条数据发送结束        //Toast.makeText(MainActivity.this, inputText, Toast.LENGTH_SHORT).show();        connectedThread.write(inputText.getBytes());        break;      default:        break;    }  }  private android.os.Handler handler = new android.os.Handler() {    public void handleMessage(Message msg) {      Bundle bundle = null;      switch (msg.what) {        case RECV_VIEW:          if (isOn == false) {            isOn = true;            turnOnOff.setText("OFF");          }          bundle = msg.getData();          String recv = bundle.getString("recv");          recvView.append(recv + "\n");          scrollView.fullScroll(ScrollView.FOCUS_DOWN); // 滚动到底部          if (recv.isEmpty() || recv.contains(" ") || recv.contains("#")) {            break;          }          int num = Integer.valueOf(recv) / 2; // 0-60s          if (num <= 20) {            led1.setText("");            led2.setText("");            led3.setText("");            led4.setText("");            led1.setBackgroundColor(Color.RED);            led2.setBackgroundColor(Color.RED);            led3.setBackgroundColor(Color.GREEN);            led4.setBackgroundColor(Color.GREEN);          }          else if (num < 30) {            int n = 30 - num;            led1.setText("" + n);            led2.setText("" + n);            if (num < 28) {              led3.setBackgroundColor(Color.GREEN);              led4.setBackgroundColor(Color.GREEN);            }            else {              led3.setBackgroundColor(Color.YELLOW);              led4.setBackgroundColor(Color.YELLOW);            }          }          else if (num <= 50) {            led1.setText("");            led2.setText("");            led3.setText("");            led4.setText("");            led1.setBackgroundColor(Color.GREEN);            led2.setBackgroundColor(Color.GREEN);            led3.setBackgroundColor(Color.RED);            led4.setBackgroundColor(Color.RED);          }          else {            int n = 60 - num;            led3.setText("" + n);            led4.setText("" + n);            if (num < 58) {              led1.setBackgroundColor(Color.GREEN);              led2.setBackgroundColor(Color.GREEN);            }            else {              led1.setBackgroundColor(Color.YELLOW);              led2.setBackgroundColor(Color.YELLOW);            }          }          break;        case NOTICE_VIEW:          bundle = msg.getData();          String notice = bundle.getString("notice");          noticeView.setText(notice);          break;        default:          break;      }    }  };  @Override  public boolean onCreateOptionsMenu(Menu menu) {    // Inflate the menu; this adds items to the action bar if it is present.    getMenuInflater().inflate(R.menu.menu_main, menu);    return true;  }  @Override  public boolean onOptionsItemSelected(MenuItem item) {    int id = item.getItemId();    if (id == R.id.start_bluetooth) {      if (bluetoothAdapter != null) {        // 开启蓝牙        int REQUEST_ENABLE_BT = 1;        if (!bluetoothAdapter.isEnabled()) {          Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);          startActivityForResult(intent, REQUEST_ENABLE_BT);          noticeView.setText("开启蓝牙成功");          //Toast.makeText(this, "开启蓝牙成功", Toast.LENGTH_SHORT).show();        } else {          Toast.makeText(this, "蓝牙已开启", Toast.LENGTH_SHORT).show();        }      }      return true;    }    else if (id == R.id.show_devices) {      if (bluetoothAdapter != null) {        if (!bluetoothAdapter.isEnabled()) {          Toast.makeText(this, "蓝牙未开启", Toast.LENGTH_SHORT).show();          return true;        }        // 查询配对设备        List<String> devices = new ArrayList<String>();        Set<BluetoothDevice> bondedDevices = bluetoothAdapter.getBondedDevices();        for (BluetoothDevice device : bondedDevices) {          devices.add(device.getName() + "-" + device.getAddress());        }        StringBuilder text = new StringBuilder();        for (String device : devices) {          text.append(device + "\n");        }        Toast.makeText(this, text, Toast.LENGTH_SHORT).show();      }      return true;    }    else if (id == R.id.find_devices) {      Toast.makeText(this, "该功能暂时不可用", Toast.LENGTH_SHORT).show();    }    else if (id == R.id.connect_devices) {      if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {        Toast.makeText(this, "蓝牙未开启", Toast.LENGTH_SHORT).show();        return true;      }      // 查询配对设备 建立连接,只能连接第一个配对的设备      List<String> devices = new ArrayList<String>();      Set<BluetoothDevice> bondedDevices = bluetoothAdapter.getBondedDevices();      for (BluetoothDevice device : bondedDevices) {        connectThread = new ConnectThread(device);        connectThread.start();        //Toast.makeText(this, "连接成功", Toast.LENGTH_SHORT).show();        break;      }    }    return super.onOptionsItemSelected(item);  }  private class ConnectThread extends Thread {    private final String MY_UUID = "00001101-0000-1000-8000-00805F9B34FB";    private final BluetoothSocket socket;    private final BluetoothDevice device;    public ConnectThread(BluetoothDevice device) {      this.device = device;      BluetoothSocket tmp = null;      try {        tmp = device.createRfcommSocketToServiceRecord(UUID.fromString(MY_UUID));      } catch (IOException e) {        e.printStackTrace();      }      this.socket = tmp;    }    public void run() {      bluetoothAdapter.cancelDiscovery();      try {        socket.connect();        connectedThread = new ConnectedThread(socket);        connectedThread.start();      } catch (IOException e) {        try {          socket.close();        } catch (IOException ee) {          ee.printStackTrace();        }        return;      }      //manageConnectedSocket(socket);    }    public void cancel() {      try {        socket.close();      } catch (IOException e) {        e.printStackTrace();      }    }  }  // 客户端与服务器建立连接成功后,用ConnectedThread收发数据  private class ConnectedThread extends Thread {    private final BluetoothSocket socket;    private final InputStream inputStream;    private final OutputStream outputStream;    public ConnectedThread(BluetoothSocket socket) {      this.socket = socket;      InputStream input = null;      OutputStream output = null;      try {        input = socket.getInputStream();        output = socket.getOutputStream();      } catch (IOException e) {        e.printStackTrace();      }      this.inputStream = input;      this.outputStream = output;    }    public void run() {      StringBuilder recvText = new StringBuilder();      byte[] buff = new byte[1024];      int bytes;      Bundle tmpBundle = new Bundle();      Message tmpMessage = new Message();      tmpBundle.putString("notice", "连接成功");      tmpMessage.what = NOTICE_VIEW;      tmpMessage.setData(tmpBundle);      handler.sendMessage(tmpMessage);      while (true) {        try {          bytes = inputStream.read(buff);          String str = new String(buff, "ISO-8859-1");          str = str.substring(0, bytes);          // 收到数据,单片机发送上来的数据以"#"结束,这样手机知道一条数据发送结束          //Log.e("read", str);          if (!str.endsWith("#")) {            recvText.append(str);            continue;          }          recvText.append(str.substring(0, str.length() - 1)); // 去除'#'          Bundle bundle = new Bundle();          Message message = new Message();          bundle.putString("recv", recvText.toString());          message.what = RECV_VIEW;          message.setData(bundle);          handler.sendMessage(message);          recvText.replace(0, recvText.length(), "");        } catch (IOException e) {          e.printStackTrace();          break;        }      }    }    public void write(byte[] bytes) {      try {        outputStream.write(bytes);      } catch (IOException e) {        e.printStackTrace();      }    }    public void cancel() {      try {        socket.close();      } catch (IOException e) {        e.printStackTrace();      }    }  }}

 menu_main.

<menu 

strings.

<resources>  <string name="app_name">BlueTooth</string>  <string name="action_settings">Settings</string>  <string name="start_bluetooth">开启蓝牙</string>  <string name="show_devices">查询配对设备</string>  <string name="find_devices">搜索设备</string>  <string name="connect_devices">连接设备</string></resources>

   到这里整个APP已经开发完成,亲测可用,如果有什么错误,欢迎评论指正谈论^_^。

参考资料

   1、BLE和经典蓝牙Android开发

   2、http://developer.android.com/intl/zh-cn/guide/topics/connectivity/bluetooth.html

  3、《Android第一行代码》