Android蓝牙实例(和单片机蓝牙模块通信)
2016-05-03 13:32
549 查看
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.xml文件如下:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <LinearLayout android:id="@+id/linear_layout_top" android:layout_alignParentTop="true" android:layout_width="match_parent" android:layout_height="40dp"> <TextView android:id="@+id/notice_view" android:layout_width="0dp" android:layout_height="40dp" android:text="蓝牙未开启" android:layout_weight="3"/> <TextView android:id="@+id/notice_recv_view" android:layout_width="0dp" android:layout_height="30dp" android:layout_weight="3"/> <TextView android:id="@+id/notice_send_view" android:layout_width="0dp" android:layout_height="30dp" android:layout_weight="3"/> <Button android:id="@+id/turn_on_off" android:layout_width="0dp" android:layout_height="40dp" android:layout_weight="2" android:text="ON"/> </LinearLayout> <TextView android:id="@+id/led1" android:layout_centerHorizontal="true" android:layout_below="@+id/linear_layout_top" android:layout_width="40dp" android:layout_height="20dp" android:gravity="center" android:text="LED1"/> <TextView android:id="@+id/led0" android:layout_centerHorizontal="true" android:layout_below="@+id/led1" android:layout_width="40dp" android:layout_height="20dp" android:gravity="center" android:text="+"/> <TextView android:id="@+id/led3" android:layout_below="@+id/led1" android:layout_toLeftOf="@+id/led1" android:layout_width="40dp" android:layout_height="20dp" android:gravity="center" android:text="LED3"/> <TextView android:id="@+id/led2" android:layout_centerHorizontal="true" android:layout_below="@+id/led3" android:layout_width="40dp" android:layout_height="20dp" android:gravity="center" android:text="LED2"/> <TextView android:id="@+id/led4" android:layout_below="@+id/led1" android:layout_toRightOf="@+id/led1" android:layout_width="40dp" android:layout_height="20dp" android:gravity="center" android:text="LED4"/> <ScrollView android:id="@+id/scroll_view" android:layout_below="@+id/led2" android:layout_above="@+id/linear_layout_bottom" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/recv_view" android:text="" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </ScrollView> <LinearLayout android:id="@+id/linear_layout_bottom" android:layout_alignParentBottom="true" android:layout_width="match_parent" android:layout_height="50dp"> <Button android:id="@+id/clear_recv_view" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="clear" /> <EditText android:id="@+id/send_text" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="3" android:hint="输入框,默认@#结尾"/> <Button android:id="@+id/send" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="send" /> </LinearLayout> </RelativeLayout>
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.xml文件如下:
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity"> <item android:id="@+id/show_devices" android:title="@string/show_devices" android:orderInCategory="100" app:showAsAction="never" /> <item android:id="@+id/start_bluetooth" android:title="@string/start_bluetooth" android:orderInCategory="100" app:showAsAction="never" /> <item android:id="@+id/find_devices" android:title="@string/find_devices" android:orderInCategory="100" app:showAsAction="never" /> <item android:id="@+id/connect_devices" android:title="@string/connect_devices" android:orderInCategory="100" app:showAsAction="never" /> </menu>
strings.xml文件如下:
<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第一行代码》
分类: Android
相关文章推荐
- Android ADB命令大全(通过ADB命令查看wifi密码、MAC地址、设备信息、操作文件、查看文件、日志信息、卸载、启动和安装APK等)
- Android:方向传感器
- Android -线程池 批量上传图片 -附php接收代码
- Android jni GetFieldID 和 GetMethodID 函数的说明
- 基于mac,ndk开发,使用android studio 编译ffmpeg(二)
- 兔子-you are attempting to install the android sdk inside your android studio installation
- Android 开发最佳实践
- Android开发--Intent-filter属性详解
- Android Studio下的应用性能优化总结-内存优化
- Android Studio下默认使用release签名
- Android Studio安装genymotion模拟器
- 兔子-you are attempting to install the android sdk inside your android studio installation
- Android Studio 进阶详细配置
- Android群英传帝落篇——程序人生,路漫漫其修远兮,吾将上下而求索!
- Android群英传帝落篇——程序人生,路漫漫其修远兮,吾将上下而求索!
- 常用的网址
- android 自动查找控件id
- Android之NFC(一)
- android 软键盘不能弹出 问题解决
- android studio教程 – 快捷键