安卓通过蓝牙通信小案例

624 阅读9分钟

安卓通过自身的蓝牙模块(驱动)与接入了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来试试看看手机能否跟蓝牙模块正常通信。

如果有错,欢迎指正,谢谢

新手可以参考看看,老手就可以忽略了,谢谢观看


引用

(Android Studio)Android 手机设备与HC05 蓝牙设备的通信(成功案例+源码)

简单的蓝牙数据交互案例