安卓通过自身的蓝牙模块(驱动)与接入了HC-06蓝牙模块的20路机器人专用舵机控制板通信,具体是向控制板发送指令,控制板控制舵机完成相应动作后再回发指令给安卓,安卓再接收到信息并确认完成指令。
简介
本猿是一个实习生,前一段时间刚刚来到公司,被要求学习安卓用蓝牙通信的方式和USB串口的通信方式与Arduino的20路机器人专用舵机控制板通信,从而控制控制板实现机器人做出相应的动作。这篇文章主要是简单介绍我是如何成功实现安卓与HC-06蓝牙模块通信的。 这是本猿第一次写文章,不会写,写的不好请见谅;也觉得不会有人看我的文章,毕竟现在关于这方面的文章一大堆,我也是网找了许多再整理出来的,引用不是很记得了,可能引用的不正确,还是见谅;变量命名不规范还是请见谅,本猿喜欢大驼峰加下划线命名方式,以后尽量改,没办法,毕竟要提高程序猿的修养。
权限
安卓通过蓝牙通信是需要权限的
权限如下:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
XML布局
代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<ListView
android:id="@+id/ListView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"></ListView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#FF000000"
android:orientation="horizontal">
<TextView
android:id="@+id/TextView"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:textSize="30dp"
android:textColor="#FFFFFFFF"
android:gravity="center|start"
android:background="#FF000000" />
<Button
android:id="@+id/Button"
android:layout_width="150dp"
android:layout_height="match_parent"
android:text="断 开 蓝 牙"
android:textSize="20dp"/>
</LinearLayout>
</LinearLayout>
声明全局变量
代码如下:
// 获取到蓝牙适配器
private BluetoothAdapter BlueToothAdapter;
private BluetoothManager BlueToothManager;
// 用来保存搜索到的设备信息
private List<String> BlueToothDevices = new ArrayList<String>();
// ListView组件
private ListView ListView;
// ListView的字符串数组适配器
private ArrayAdapter<String> ArrayAdapter;
// UUID,蓝牙建立链接需要的
private final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
// 为其链接创建一个名称
private final String NAME = "BlueTooth_Socket";
// 选中发送数据的蓝牙设备,全局变量
private BluetoothDevice BlueToothDevice;
// 获取到选中设备的客户端串口,全局变量
private BluetoothSocket BlueToothSocket;
// 获取到向设备写的输入流,全局变量
private InputStream InputStream;
// 获取到向设备写的输出流,全局变量
private OutputStream OutputStream;
private Button Button = null;
private TextView TextView = null;
private Handler Handler = new Handler() {
@Override
public void handleMessage(Message msg) {
TextView.setText(msg.obj.toString());
super.handleMessage(msg);
}
};
初始化一些数据
代码如下:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
initListener();
}
//获取控件资源ID
private void initView() {
ListView = findViewById(R.id.ListView);
TextView = findViewById(R.id.TextView);
Button = findViewById(R.id.Button);
}
//初始化部分需要初始数据的控件
private void initData() {
checkSelfPermission();
BlueToothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BlueToothAdapter = BlueToothManager.getAdapter();
if (BlueToothAdapter == null) {
Toast.makeText(getBaseContext(), "当前设备不支持蓝牙", Toast.LENGTH_LONG).show();
finish();
return;
}
myRegisterReceiver();
startScan();
ArrayAdapter = new ArrayAdapter<String>(getBaseContext(), android.R.layout.simple_list_item_1, android.R.id.text1, BlueToothDevices);
// 为listView绑定适配器
ListView.setAdapter(ArrayAdapter);
TextView.setText("请选择蓝牙");
}
//每个控件的监听方法
private void initListener() {
ListView.setOnItemClickListener(List_ItemClick);
Button.setOnClickListener(Button_Click);
}
private void checkSelfPermission() {
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, 10);
}
}
private void startScan() {
if (BlueToothAdapter.isEnabled()) {
if (BlueToothAdapter.isDiscovering()) {
BlueToothAdapter.cancelDiscovery();
}
BlueToothAdapter.startDiscovery();
} else {
if (BlueToothAdapter.enable()) {
Handler.postDelayed(new Runnable() {
@Override
public void run() {
if (BlueToothAdapter.isDiscovering()) {
BlueToothAdapter.cancelDiscovery();
}
BlueToothAdapter.startDiscovery();
}
}, 1500);
} else {
TextView.setText("请求蓝牙权限被拒绝,请授权");
}
}
}
扫描蓝牙(未匹配的和已匹配的)
本猿是实现搜索附近的蓝牙,无论是否有匹配过的蓝牙,都会一起显示;并且会显示蓝牙设备的名字和MAC地址。
搜索中的如下图所示:
搜索结束的如下图所示:
扫描蓝牙的代码如下:
//广播接收者相关属性
private void myRegisterReceiver() {
IntentFilter IntentFilter = new IntentFilter();
IntentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
IntentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
IntentFilter.addAction(BluetoothDevice.ACTION_FOUND);
IntentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
getApplicationContext().registerReceiver(BroadcastReceiver, IntentFilter);
}
// 注册广播接收者
private BroadcastReceiver BroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context arg0, Intent intent) {
// 获取到广播的action
String action = intent.getAction();
switch (action) {
case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
setTitle("正在搜索附近的蓝牙设备中,请稍后。。。");
break;
case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
setTitle("搜索结束");
break;
case BluetoothDevice.ACTION_FOUND:
//找到设备后获取其设备
BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//将设备保存到BlueToothDevices集合中
BlueToothDevices.add(bluetoothDevice.getName() + "\n" + bluetoothDevice.getAddress() + "\n");
ArrayAdapter.notifyDataSetChanged();
break;
default:
break;
}
}
};
这时候,你只需要点击你想要连接的HC-06蓝牙模块,就可以进行下一步的连接。
连接蓝牙
点击了你想要连接的HC-06蓝牙模块后,效果如下图:
连接成功后,效果如下图:
至于其它状态,如连接失败等,我就不一一展示了。
(如果是未匹配过的蓝牙,会先匹配再连接;如果是已匹配的蓝牙,则会直接连接)
连接选中的蓝牙的代码如下:
private AdapterView.OnItemClickListener List_ItemClick = new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, final int position, long id) {
Thread Thread = new Thread() {
@Override
public void run() {
// 获取到这个设备的信息
String s = ArrayAdapter.getItem(position);
// 对其进行分割,获取到这个设备的地址
String address = s.substring(s.indexOf("\n") + 1).trim();
//对其进行分割,获取到这个设备的名字
s = s.substring(0, s.indexOf("\n"));
Message Message = new Message();
Message.obj = s + "连接中,请稍候。。。";
Handler.sendMessage(Message);
// 判断当前是否还是正在搜索周边设备,如果是则暂停搜索
if (BlueToothAdapter.isDiscovering()) {
BlueToothAdapter.cancelDiscovery();
}
// 如果选择设备为空则代表还没有选择设备
if (BlueToothDevice == null) {
//通过地址获取到该设备
BlueToothDevice = BlueToothAdapter.getRemoteDevice(address);
}
// 这里需要try catch一下,以防异常抛出
try {
// 判断客户端接口是否为空
if (BlueToothSocket == null) {
// 获取到客户端接口
BlueToothSocket = BlueToothDevice.createRfcommSocketToServiceRecord(MY_UUID);
// 向服务端发送连接
BlueToothSocket.connect();
// 吐司一下,告诉用户发送成功
Message = new Message();
Message.obj = s + "连接成功";
Handler.sendMessage(Message);
} else {
Message = new Message();
Message.obj = s + "连接失败";
Handler.sendMessage(Message);
BlueToothSocket.close();
BlueToothSocket = null;
BlueToothDevice = null;
}
} catch (IOException e) {
// TODO Auto-generated catch block
try {
BlueToothSocket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
BlueToothSocket = null;
BlueToothDevice = null;
e.printStackTrace();
// 如果发生异常则告诉用户发送失败
Message = new Message();
Message.obj = s + "连接异常";
Handler.sendMessage(Message);
}
super.run();
}
};
Thread.start();
}
};
建议开个子线程,不要阻塞主线程。
与蓝牙模块通信
你只要成功拿到BlueToothSocket,就非常简单了,就是流的输入输出了。
先将InputStream和OutputStream赋值先。
代码如下:
InputStream = BlueToothSocket.getInputStream();
OutputStream = BlueToothSocket.getOutputStream();
发送数据
代码如下:
(记得判断OutputStream是否为空和try catch)
if (OutputStream != null) {
String String = "需要发送的信息"
OutputStream.write(String.getBytes("UTF-8"));// 需要发送的信息,以UTF-8的格式发送出去
}
接收数据
如果你成功发送了数据,控制板有反应,那么你就成功一大半了,接下来是接收并处理数据了。
代码如下:
(记得判断InputStream是否为空和try catch。)
byte[] buffer = new byte[1024];
int bytes;
String String = new String;
while (true) {
if (InputStream != null) {
bytes = InputStream.read(buffer);
String = new String(buffer, 0, bytes, "UTF-8");
Log.d(TAG, "String:" + String);
}
成功实现
只要完成了上述的所有的步骤,一般都是可以正常实现的,本猿亲测有效 如果不能正常实现,请告诉本猿,本猿愿跟你一起讨论,一起解决。
接下来是完整的代码:
(会跟上述的代码有些出入,但出入不大)
public class MainActivity extends AppCompatActivity {
// 获取到蓝牙适配器
private BluetoothAdapter BlueToothAdapter;
private BluetoothManager BlueToothManager;
// 用来保存搜索到的设备信息
private List<String> BlueToothDevices = new ArrayList<String>();
// ListView组件
private ListView ListView;
// ListView的字符串数组适配器
private ArrayAdapter<String> ArrayAdapter;
// UUID,蓝牙建立链接需要的
private final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
// 为其链接创建一个名称
private final String NAME = "BlueTooth_Socket";
// 选中发送数据的蓝牙设备,全局变量
private BluetoothDevice BlueToothDevice;
// 获取到选中设备的客户端串口,全局变量
private BluetoothSocket BlueToothSocket;
// 获取到向设备写的输入流,全局变量
private InputStream InputStream;
// 获取到向设备写的输出流,全局变量
private OutputStream OutputStream;
private Button Button = null;
private TextView TextView = null;
private Handler Handler = new Handler() {
@Override
public void handleMessage(Message msg) {
TextView.setText(msg.obj.toString());
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
initListener();
}
//获取控件资源ID
private void initView() {
ListView = findViewById(R.id.ListView);
TextView = findViewById(R.id.TextView);
Button = findViewById(R.id.Button);
}
//初始化部分需要初始数据的控件
private void initData() {
checkSelfPermission();
BlueToothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BlueToothAdapter = BlueToothManager.getAdapter();
if (BlueToothAdapter == null) {
Toast.makeText(getBaseContext(), "当前设备不支持蓝牙", Toast.LENGTH_LONG).show();
finish();
return;
}
myRegisterReceiver();
startScan();
ArrayAdapter = new ArrayAdapter<String>(getBaseContext(), android.R.layout.simple_list_item_1, android.R.id.text1, BlueToothDevices);
// 为listView绑定适配器
ListView.setAdapter(ArrayAdapter);
TextView.setText("请选择蓝牙");
}
//每个控件的监听方法
private void initListener() {
ListView.setOnItemClickListener(List_ItemClick);
Button.setOnClickListener(Button_Click);
}
private void checkSelfPermission() {
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, 10);
}
}
//广播接收者相关属性
private void myRegisterReceiver() {
IntentFilter IntentFilter = new IntentFilter();
IntentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
IntentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
IntentFilter.addAction(BluetoothDevice.ACTION_FOUND);
IntentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
getApplicationContext().registerReceiver(BroadcastReceiver, IntentFilter);
}
// 注册广播接收者
private BroadcastReceiver BroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context arg0, Intent intent) {
// 获取到广播的action
String action = intent.getAction();
switch (action) {
case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
setTitle("正在搜索附近的蓝牙设备中,请稍后。。。");
break;
case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
setTitle("搜索结束");
break;
case BluetoothDevice.ACTION_FOUND:
//找到设备后获取其设备
BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//将设备保存到BlueToothDevices集合中
BlueToothDevices.add(bluetoothDevice.getName() + "\n" + bluetoothDevice.getAddress() + "\n");
ArrayAdapter.notifyDataSetChanged();
break;
default:
break;
}
}
};
private void startScan() {
if (BlueToothAdapter.isEnabled()) {
if (BlueToothAdapter.isDiscovering()) {
BlueToothAdapter.cancelDiscovery();
}
BlueToothAdapter.startDiscovery();
} else {
if (BlueToothAdapter.enable()) {
Handler.postDelayed(new Runnable() {
@Override
public void run() {
if (BlueToothAdapter.isDiscovering()) {
BlueToothAdapter.cancelDiscovery();
}
BlueToothAdapter.startDiscovery();
}
}, 1500);
} else {
TextView.setText("请求蓝牙权限被拒绝,请授权");
}
}
}
private void Execution(final String string) {
//string是需要发送的信息,以UTF-8的格式发送给控制板
if (OutputStream != null) {
try {
OutputStream.write(string.getBytes("UTF-8"));
} catch (IOException e) {
e.printStackTrace();
}
}
//读取控制板返回来的数据
byte[] buffer = new byte[1024];
int bytes;
String String = "";
while (true) {
if (InputStream != null) {
try {
bytes = InputStream.read(buffer);
String = new String(buffer, 0, bytes, "UTF-8");
Log.d("TAG", "String:" + String);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private View.OnClickListener Button_Click = new View.OnClickListener() {
@Override
public void onClick(View v) {
if (BlueToothSocket != null) {
try {
BlueToothSocket.close();
BlueToothSocket = null;
BlueToothDevice = null;
TextView.setText("请选择蓝牙");
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
private AdapterView.OnItemClickListener List_ItemClick = new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, final int position, long id) {
Thread Thread = new Thread() {
@Override
public void run() {
// 获取到这个设备的信息
String s = ArrayAdapter.getItem(position);
// 对其进行分割,获取到这个设备的地址
String address = s.substring(s.indexOf("\n") + 1).trim();
//对其进行分割,获取到这个设备的名字
s = s.substring(0, s.indexOf("\n"));
Message Message = new Message();
Message.obj = s + "连接中,请稍候。。。";
Handler.sendMessage(Message);
// 判断当前是否还是正在搜索周边设备,如果是则暂停搜索
if (BlueToothAdapter.isDiscovering()) {
BlueToothAdapter.cancelDiscovery();
}
// 如果选择设备为空则代表还没有选择设备
if (BlueToothDevice == null) {
//通过地址获取到该设备
BlueToothDevice = BlueToothAdapter.getRemoteDevice(address);
}
// 这里需要try catch一下,以防异常抛出
try {
// 判断客户端接口是否为空
if (BlueToothSocket == null) {
// 获取到客户端接口
BlueToothSocket = BlueToothDevice.createRfcommSocketToServiceRecord(MY_UUID);
// 向服务端发送连接
BlueToothSocket.connect();
// 吐司一下,告诉用户发送成功
Message = new Message();
Message.obj = s + "连接成功";
Handler.sendMessage(Message);
//获取BlueToothSocket的输入输出流
InputStream = BlueToothSocket.getInputStream();
OutputStream = BlueToothSocket.getOutputStream();
Execution("#1GC2\r\n");
} else {
Message = new Message();
Message.obj = s + "连接失败";
Handler.sendMessage(Message);
BlueToothSocket.close();
BlueToothSocket = null;
BlueToothDevice = null;
}
} catch (IOException e) {
// TODO Auto-generated catch block
try {
BlueToothSocket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
BlueToothSocket = null;
BlueToothDevice = null;
e.printStackTrace();
// 如果发生异常则告诉用户发送失败
Message = new Message();
Message.obj = s + "连接异常";
Handler.sendMessage(Message);
}
super.run();
}
};
Thread.start();
}
};
@Override
protected void onDestroy() {
super.onDestroy();
Handler.removeCallbacksAndMessages(null);
// unregisterReceiver(BroadcastReceiver );
if (BlueToothAdapter.isDiscovering()) {
BlueToothAdapter.cancelDiscovery();
}
}
}
这个就是极其简洁的安卓与HC-06蓝牙模块通信的小DEMO,大家在用之前可以下载一个蓝牙串口调试工具的APP来试试看看手机能否跟蓝牙模块正常通信。
如果有错,欢迎指正,谢谢
新手可以参考看看,老手就可以忽略了,谢谢观看
引用