Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

【周记-Android移动端开发】手机蓝牙与下位机HC-05蓝牙模块通信系统

$
0
0

【周记-Android移动端开发】手机蓝牙与下位机HC-05蓝牙模块通信系统

很久没有写博客了,计划一直都有,但总是被这样或者那样的事情给耽搁了,在此写下文字监督自己:不论长短,每周至少一篇!本文根据自己的实践总结而来,参考前人博客之余,也自己总结和开发了一些功能,在这里给自己备份也分享给大家。不同之处在于:自动打开并搜索蓝牙、修改蓝牙名字、完整接收蓝牙传输数据、修改蓝牙密码、解除蓝牙绑定。

  • 系统框架简介
  • 软件界面设计
  • 蓝牙开发

一、系统框架简介

系统由上、下位机两部分构成,旨在实现移动端app通过蓝牙通信,将app发送过来的数据存储在下位机的存储单元,与此同时,app也可以通过指令查询下位机的参数设置。系统框架图如下:

这里写图片描述

二、软件界面设计

话不多说,先上软件界面:

这里写图片描述

界面主要涉及到按键的监听和Activity的跳转:
①按键监听有四种方法,详情可以参考博客
②Activity的跳转包括:直接跳startActivity转和带返回跳startActivityForResult转,详情可以参考博客

二、蓝牙开发

蓝牙功能 函数名称
(1)开启&关闭蓝牙 enable()/disable()
(2)修改蓝牙名字 changeName()
(3)搜索&显示蓝牙设备 DeviceListActivity
(4)配对&连接蓝牙 connectToServerSocket(address)
(5)数据收发 write/ConnectedThread
(6)修改蓝牙密码 changeMima()
(7)取消蓝牙配对 removeBond

本系统中实现的功能如表所示,这里不打算赘述已经开发成熟的功能,可以参考这篇博客,讲得比较清晰,这里只针对自己在开发中添加的功能做一下记录,开发之前需要提醒的有两点:
①不要忘记蓝牙权限的设置:

<uses-permission android:name="android.permission.BLUETOOTH"/>  
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>  

②让主程序弹出搜索对话框时,不要忘了intent设置;

<activity android:name=".DeviceListActivity" 
android:theme="@android:style/Theme.Dialog" 
android:label="选取连接设备" 
android:configChanges="orientation|keyboardHidden"/>

(1)自动打开并搜索蓝牙

一般的做法都是监听按键(打开蓝牙按键和搜索设备按键),先打开蓝牙,接着搜索蓝牙。但是,这样做无疑使操作更加复杂,在实际生产和用户使用时并不方便,我要做的效果是程序启动的时候自动打开蓝牙并开始搜索设备,于是乎,出问题了,蓝牙虽然打开了,但是不接着搜索设备了,汗。。。解决办法是,打开蓝牙后延时1秒后开始搜索,这样就行了

//得到BluetoothAdapter对象
BluetoothAdapter mBtAdapter = BluetoothAdapter.getDefaultAdapter();
mBtAdapter.enable();//打开蓝牙
Timer timer=new Timer();
TimerTask task=new TimerTask(){//定时1k=new TimerTask(){//定时1秒后搜索设备
    @Override
     public void run() {
        Intent serverIntent = new Intent(MainActivity.this, DeviceListActivity.class); //跳转程序设置
        startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);  //设置返回宏定义
    }          
};
timer.schedule(task,1000);

(3)修改蓝牙名字

这一功能有两种实现方式,一种是通过蓝牙传输将名字传输给下位机,用单片机操作蓝牙AT指令直接修改蓝牙名字,这样,以后任何手机搜索该蓝牙设备时,显示的名字都是你所修改的名字;另一种方式是在软件层面实现蓝牙名字的修改,其实是搜索出蓝牙设备的ID,然后将其对应到你想修改的名字,并将这一映射存在本地,那么,以后这部手机下次搜索到该设备时,就会显示你所修改的名字,而不影响其他手机对该设备的显示。这种方法各有特色,就看你的需求了。这里就讲讲第二种方式的实现:

步骤一:搜索蓝牙设备,获取并修改名字

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // TODO Auto-generated method stub
    super.onActivityResult(requestCode, resultCode, data);
    switch(requestCode) {
    case REQUEST_CONNECT_DEVICE:     //连接结果,由DeviceListActivity设置返回
        if (resultCode == Activity.RESULT_OK) { //选择蓝牙
            String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
            device = mBluetoothAdapter.getRemoteDevice(address);// 得到蓝牙设备句柄
            connectToServerSocket(address);
            setting_thread=true;
            ConnectedThread thread=new ConnectedThread();
            thread.start();
        }
        break;
        default:break;
    }
}

在从DeviceListActivity返回的数据中,其实已经包含蓝牙设备的句柄,我们可以通过device.getName()获取当前连接的蓝牙模块的名字,然后将设备的名字或者其唯一标识符(例如,98:D3:34:90:8A:BC)与新修改的名字做映射保存在本地。
步骤二:存储设置到本地,下次搜索蓝牙时做映射显示
使用SharedPreferences存储数据,SharedPreferences是Android平台上一个轻量级的存储类,主要是保存一些常用的配置比如窗口状态,一般在Activity中 重载窗口状态onSaveInstanceState保存一般使用SharedPreferences完成,它提供了Android平台常规的Long长 整形、Int整形、String字符串型的保存。

//获取SharedPreferences对象
Context ctx = MainActivity.this;       
SharedPreferences sp = ctx.getSharedPreferences("SP", MODE_PRIVATE);
//存入数据
Editor editor = sp.edit();
editor.putString("STRING_KEY", "string");
editor.commit();

(4)完整接收蓝牙传输数据

在开发蓝牙数据接收时,发现一个问题:数据有时候会接收不完整。由于项目中的每一位数据返回都十分重要,所以必须保证数据的完整性,要解决这一问题需要清楚的了解如何从InputStream中读取数据,从输入流中读取数据最常用的方法基本上就是如下 3 个 read() 方法:

① read () 方法,这个方法从输入流中读取数据的下一个字节。返回 0 到 255 范围内的 int 字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1 。
② read (byte[] b,int off,int len) 方法,将输入流中最多 len 个数据字节读入 byte 数组。尝试读取 len 个字节,但读取的字节也可能小于该值。以整数形式返回实际读取的字节数。
③ read (byte[] b) 方法, 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。以整数形式返回实际读取的字节数。

对于三种方法的选择,需要看具体项目的需求,如果你能够通过某种方法知道数据的长度,那么建议用第一种方法,每次读取一个字节,保证数据的完整性;如果数据长度不知道,那么就用②或者③,一般③用的比较多,这一方法效率高但是读取数据的长度不确定—-不超过数组b的长度,所以就需要多次读取直到把数据全部读完。

(5)修改蓝牙密码

本项目中用的是HC-05蓝牙模块,该模块有接收蓝牙指令模式(即通信模式),和AT指令集模式,一般默认状态为蓝牙通信模式,要想修改蓝牙连接密码,需要将蓝牙模块切换到AT指令集模式,具体过程见下面的流程图:
这里写图片描述

(6)解除蓝牙绑定(配对)

在真实场景中,要想在程序修改完密码后,之前连接的蓝牙设备都无法与该蓝牙模块建立连接,需要解除该模块与手机的绑定,即取消配对。要想解除所有的配对,当然是需要下位机控制蓝牙模块进入AT指令集模式,解除所有绑定,而与此同时,当前修改密码的手机也需要在软件层面解除绑定(配对),这样交互性更强。

用过Android系统设置(Setting)的人都知道蓝牙搜索之后可以建立配对和解除配对,但是这两项功能的函数没有在SDK中给出,本项目利用Java的反射机制去调用removeBond解除绑定,从而取消本机与该蓝牙模块的配对。具体实现程序如下:当程序接收到下位机传来的修改密码成功的指令后,程序会弹出一个强提示,当用户点击确认按钮之后,程序解除绑定并退出程序。

if(modify_flag){//修改密码成功
      AlertDialog.Builder builder = new Builder(MainActivity.this);
      builder.setTitle("密码修改成功");
      builder.setMessage("点击确定按钮重新启动!");
      builder.setCancelable(false);
      builder.setPositiveButton("确认", new OnClickListener() {
           @Override
           public void onClick(DialogInterface dialog, int which) {
           try {//接触绑定
               Method m = device.getClass().getMethod("removeBond", (Class[]) null);
               m.invoke(device, (Object[]) null);
           } catch (Exception e) {
                Log.e("erro", e.getMessage());
           }
           setting_thread=false;
           dialog.dismiss();
           mBluetoothAdapter.disable();//关闭蓝牙
           finish();
           System.exit(0);//销毁程序
           }
      });
      builder.setCancelable(false);
      builder.create().show();
}else {
     Toast.makeText(MainActivity.this, "set failed!", Toast.LENGTH_SHORT).show();
}
作者:dengpeng0419 发表于2016/10/22 13:06:55 原文链接
阅读:76 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>