好久没有写博客了,因为最近一直在家浪,今天就来记录一下这几天来学校学到的东西(其实在本科就已经开始靴子了,只是还没完全弄明白),以便以后忘了回来看看,也希望对搜索并阅读本文章的朋友有所帮助。
本科毕设做的东西是“基于安卓系统的无人插秧机监控平台”,零基础学习,经过两个月的奋斗终于做的差不多了,遗憾的是没有将它继续完善投入使用,因为是本科学校的项目,毕业将到现在的学校来学习了。本科毕业之际没有事做就学习了一些其他自己感兴趣的东西:手机蓝牙与单片机端的蓝牙模块进行通信。相信学安卓的朋友也都做过这方面的实验。
我是控制出生,学安卓只是顺应潮流,现在很多项目都和安卓APP有关,不然都显得很low。于是自己开启了学习蓝牙之路。参考了很多书籍也搜索了很多网上的资料,但是始终没有搞清楚。于是就淘宝就买了一个淘宝的蓝牙模块(带有APP基本源码),本来就不是计算机出生,所以只能用这种方方式来学习:借鉴别人的代码,读懂并且能自己写出来,并且按照自己的需求进行开发。
唠叨了很多,下面开始步入正题。
****工程基本框架****
如图,工程的基本框架主要如图。两个活动和一个服务。MainActivity是主活动,对应的布局是activity_main.xml(图2),在此界面定义了一个按钮和一个ListView(对应listitem.xml),按钮用于启动扫描或停止扫描蓝牙设备,ListView用于显示扫描到的蓝牙设备的相关信息。Ble_Activity是另一个活动,是和扫描到的蓝牙设备进行连接后进入的界面,在此界面与连接的蓝牙设备进行收发数据的通信,其对应的布局文件是ble_activity.xml,
ble_activity.xml的总体结构如题6,其他细节部分参看代码。ble_activity.xml的第一部分是图3,用于实时显示连接状态;第二部分用于显示接收到来自单片机端发送过来的数据;第三部分是四个按钮,按钮用于启动数据发送线程。BluetoothService顾名思义就是蓝牙服务,用于负责在后台实现蓝牙的连接,数据的发送接受。这就是工程的大体框架,下面细致介绍分别在两个活动和一个服务需要做哪些事情。
****MainActivity****
在这里主要进行蓝牙的扫描,当点击扫描到的蓝牙模块时进入Ble_Activity,并且将蓝牙的先关信息传入Ble_Activity,以便与目标蓝牙模块进行连接。
****Ble_Activity****
当进入该活动时首先接收MainActivity传来的蓝牙信息,然后绑定服务,在绑定服务时与蓝牙模块进行连接,之后就可以利用服务可以和蓝牙模块进行数据传输了。
****BluetoothService****
在这里主要是进行蓝牙的连接、数据的发送与接收。
****权限申明****
在AndroidManifest.xml文件中进行相关申明。查看代码:
注意:
大致的框架与实现如上所述,具体查看以下代码:
**********************************1、AndroidManifest.xml*************************
package="com.example.bluetoothrobot"
android:versionCode="1"
android:versionName="1.2" >
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
android:name=".MainActivity"
android:label="@string/app_name" >
android:name=".Ble_Activity">
************************************2、MainActivity.java*************************
package com.example.bluetoothrobot;
import java.util.ArrayList;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
//import com.example.bluetoothrobot.Ble_Activity;
/**
* 特别说明:HC_BLE助手是广州汇承信息科技有限公司独自研发的手机APP,方便用户调试08蓝牙模块。
* 本软件只能支持安卓版本4.3并且有蓝牙4.0的手机使用。
* 另外对于自家的05、06模块,要使用另外一套蓝牙2.0的手机APP,用户可以在汇承官方网的下载中心自行下载。
* 本软件提供代码和注释,免费给购买汇承08模块的用户学习和研究,但不能用于商业开发,最终解析权在广州汇承信息科技有限公司。
* **/
/**
* @Description: TODO
* @author 广州汇承信息科技有限公司
* @data: 2014-10-12 上午10:28:18
* @version: V1.0
*/
public class MainActivity extends Activity implements OnClickListener {
// 扫描蓝牙按钮
private Button scan_btn;
// 蓝牙适配器
BluetoothAdapter mBluetoothAdapter;
// 蓝牙信号强度
private ArrayList
// 自定义Adapter
LeDeviceListAdapter mleDeviceListAdapter;
// listview显示扫描到的蓝牙信息
ListView lv;
// 描述扫描蓝牙的状态
private boolean mScanning;
private boolean scan_flag;
private Handler mHandler;
int REQUEST_ENABLE_BT = 1;
// 蓝牙扫描时间
private static final long SCAN_PERIOD = 10000;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化控件
init();
// 初始化蓝牙
init_ble();
scan_flag = true;
// 自定义适配器
mleDeviceListAdapter = new LeDeviceListAdapter();
// 为listview指定适配器
lv.setAdapter(mleDeviceListAdapter);
/* listview点击函数 */
lv.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
@Override
public void onItemClick(AdapterView> arg0, View v, int position,
long id)
{
// TODO Auto-generated method stub
final BluetoothDevice device = mleDeviceListAdapter
.getDevice(position);
if (device == null)
return;
final Intent intent = new Intent(MainActivity.this,
Ble_Activity.class);//活动切换
intent.putExtra(Ble_Activity.EXTRAS_DEVICE_NAME,
device.getName());//活动间传递意图参数
intent.putExtra(Ble_Activity.EXTRAS_DEVICE_ADDRESS,
device.getAddress());//活动间传递意图参数
intent.putExtra(Ble_Activity.EXTRAS_DEVICE_RSSI,
rssis.get(position).toString());//活动间传递意图参数
if (mScanning)
{
/* 停止扫描设备 */
mBluetoothAdapter.stopLeScan(mLeScanCallback);
mScanning = false;
}
try
{
// 启动Ble_Activity
startActivity(intent);
} catch (Exception e)
{
e.printStackTrace();
// TODO: handle exception
}
}
});
}
/**
* @Title: init
* @Description: TODO(初始化UI控件)
* @param 无
* @return void
* @throws
*/
private void init()
{
scan_btn = (Button) this.findViewById(R.id.scan_dev_btn);
scan_btn.setOnClickListener(this);
lv = (ListView) this.findViewById(R.id.lv);
mHandler = new Handler();
}
/**
* @Title: init_ble
* @Description: TODO(初始化蓝牙)
* @param 无
* @return void
* @throws
*/
private void init_ble()
{
// 手机硬件支持蓝牙
if (!getPackageManager().hasSystemFeature(
PackageManager.FEATURE_BLUETOOTH_LE))
{
Toast.makeText(this, "不支持BLE", Toast.LENGTH_SHORT).show();
finish();
}
// Initializes Bluetooth adapter.
// 获取手机本地的蓝牙适配器
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
// 打开蓝牙权限
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled())
{
Intent enableBtIntent = new Intent(
BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
/*
* 按钮响应事件
*/
@Override
public void onClick(View v)
{
// TODO Auto-generated method stub
if (scan_flag)
{
mleDeviceListAdapter = new LeDeviceListAdapter();
lv.setAdapter(mleDeviceListAdapter);
scanLeDevice(true);
} else
{
scanLeDevice(false);
scan_btn.setText("扫描设备");
}
}
/**
* @Title: scanLeDevice
* @Description: TODO(扫描蓝牙设备 )
* @param enable
* (扫描使能,true:扫描开始,false:扫描停止)
* @return void
* @throws
*/
private void scanLeDevice(final boolean enable)
{
if (enable)
{
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable()
{
@Override
public void run()
{
mScanning = false;
scan_flag = true;
scan_btn.setText("扫描设备");
Log.i("SCAN", "stop.....................");
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
/* 开始扫描蓝牙设备,带mLeScanCallback 回调函数 */
Log.i("SCAN", "begin.....................");
mScanning = true;
scan_flag = false;
scan_btn.setText("停止扫描");
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else
{
Log.i("Stop", "stoping................");
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
scan_flag = true;
}
}
/**
* 蓝牙扫描回调函数 实现扫描蓝牙设备,回调蓝牙BluetoothDevice,可以获取name MAC等信息
*
* **/
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback()
{
@Override
public void onLeScan(final BluetoothDevice device, final int rssi,
byte[] scanRecord)
{
// TODO Auto-generated method stub
runOnUiThread(new Runnable()
{
@Override
public void run()
{
/* 讲扫描到设备的信息输出到listview的适配器 */
mleDeviceListAdapter.addDevice(device, rssi);
mleDeviceListAdapter.notifyDataSetChanged();
}
});
System.out.println("Address:" + device.getAddress());
System.out.println("Name:" + device.getName());
System.out.println("rssi:" + rssi);
}
};
/**
* @Description: TODO<自定义适配器Adapter,作为listview的适配器>
* @author 广州汇承信息科技有限公司
* @data: 2014-10-12 上午10:46:30
* @version: V1.0
*/
private class LeDeviceListAdapter extends BaseAdapter {
private ArrayList
private LayoutInflater mInflator;
public LeDeviceListAdapter()
{
super();
rssis = new ArrayList
mLeDevices = new ArrayList
mInflator = getLayoutInflater();
}
public void addDevice(BluetoothDevice device, int rssi)
{
if (!mLeDevices.contains(device))
{
mLeDevices.add(device);
rssis.add(rssi);
}
}
public BluetoothDevice getDevice(int position)
{
return mLeDevices.get(position);
}
public void clear()
{
mLeDevices.clear();
rssis.clear();
}
@Override
public int getCount()
{
return mLeDevices.size();
}
@Override
public Object getItem(int i)
{
return mLeDevices.get(i);
}
@Override
public long getItemId(int i)
{
return i;
}
/**
* 重写getview
*
* **/
@Override
public View getView(int i, View view, ViewGroup viewGroup)
{
// General ListView optimization code.
// 加载listview每一项的视图
view = mInflator.inflate(R.layout.listitem, null);
// 初始化三个textview显示蓝牙信息
TextView deviceAddress = (TextView) view
.findViewById(R.id.tv_deviceAddr);
TextView deviceName = (TextView) view
.findViewById(R.id.tv_deviceName);
TextView rssi = (TextView) view.findViewById(R.id.tv_rssi);
BluetoothDevice device = mLeDevices.get(i);
deviceAddress.setText(device.getAddress());
deviceName.setText(device.getName());
rssi.setText("" + rssis.get(i));
return view;
}
}
}
************************************3、Ble_Activity.java*************************
package com.example.bluetoothrobot;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
//import com.huicheng.service.*;
import android.app.Activity;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ScrollView;
import android.widget.TextView;
//import com.huicheng.R;
//import com.huicheng.service.BluetoothLeService;
/**
* 特别说明:HC_BLE助手是广州汇承信息科技有限公司独自研发的手机APP,方便用户调试08蓝牙模块。
* 本软件只能支持安卓版本4.3并且有蓝牙4.0的手机使用。
* 另外对于自家的05、06模块,要使用另外一套蓝牙2.0的手机APP,用户可以在汇承官方网的下载中心自行下载。
* 本软件提供代码和注释,免费给购买汇承08模块的用户学习和研究,但不能用于商业开发,最终解析权在广州汇承信息科技有限公司。
**/
/**
* @author 广州汇承信息科技有限公司
* @Description: TODO
* @data: 2014-10-20 下午12:12:04
* @version: V1.0
*/
public class Ble_Activity extends Activity implements OnClickListener {
private final static String TAG = Ble_Activity.class.getSimpleName();
//蓝牙4.0的UUID,其中0000ffe1-0000-1000-8000-00805f9b34fb是广州汇承信息科技有限公司08蓝牙模块的UUID
public static String HEART_RATE_MEASUREMENT = "0000ffe1-0000-1000-8000-00805f9b34fb";
public static String EXTRAS_DEVICE_NAME = "DEVICE_NAME";
;
public static String EXTRAS_DEVICE_ADDRESS = "DEVICE_ADDRESS";
public static String EXTRAS_DEVICE_RSSI = "RSSI";
//蓝牙连接状态
private boolean mConnected = false;
private String status = "disconnected";
//蓝牙名字
private String mDeviceName;
//蓝牙地址
private String mDeviceAddress;
//蓝牙信号值
private String mRssi;
private Bundle b;
private String rev_str = "";
//蓝牙service,负责后台的蓝牙服务
private static BluetoothLeService mBluetoothLeService;
//文本框,显示接受的内容
private TextView rev_tv, connect_state;
//发送按钮
private Button send_btn1, send_btn2, send_btn3, send_btn4;
//发送内容选择变量
private int sendFlag = 0;
//文本编辑框
//private EditText send_et;
private ScrollView rev_sv;
private ArrayList
//蓝牙特征值
private static BluetoothGattCharacteristic target_chara=null;
public static byte[] revDataForCharacteristic;
private Handler mhandler = new Handler();
private Handler myHandler = new Handler() {
// 2.重写消息处理函数
public void handleMessage(Message msg) {
switch (msg.what) {
// 判断发送的消息
case 1: {
// 更新View
String state = msg.getData().getString("connect_state");
connect_state.setText(state);
break;
}
}
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.ble_activity);
b = getIntent().getExtras();
//从意图获取显示的蓝牙信息
mDeviceName = b.getString(EXTRAS_DEVICE_NAME);
mDeviceAddress = b.getString(EXTRAS_DEVICE_ADDRESS);
mRssi = b.getString(EXTRAS_DEVICE_RSSI);
/* 启动蓝牙service */
Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
init();
}
@Override
protected void onDestroy() {
super.onDestroy();
//解除广播接收器
unregisterReceiver(mGattUpdateReceiver);
mBluetoothLeService = null;
}
// Activity出来时候,绑定广播接收器,监听蓝牙连接服务传过来的事件
@Override
protected void onResume() {
super.onResume();
//绑定广播接收器
registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
if (mBluetoothLeService != null) {
//根据蓝牙地址,建立连接
final boolean result = mBluetoothLeService.connect(mDeviceAddress);
Log.d(TAG, "Connect request result=" + result);
}
}
/**
* @param 无
* @return void
* @throws
* @Title: init
* @Description: TODO(初始化UI控件)
*/
private void init() {
rev_sv = (ScrollView) this.findViewById(R.id.rev_sv);
rev_tv = (TextView) this.findViewById(R.id.rev_tv);
connect_state = (TextView) this.findViewById(R.id.connect_state);
send_btn1 = (Button) this.findViewById(R.id.send_btn1);
send_btn2 = (Button) this.findViewById(R.id.send_btn2);
send_btn3 = (Button) this.findViewById(R.id.send_btn3);
send_btn4 = (Button) this.findViewById(R.id.send_btn4);
//send_et = (EditText) this.findViewById(R.id.send_et);
connect_state.setText(status);
send_btn1.setOnClickListener(this);
send_btn2.setOnClickListener(this);
send_btn3.setOnClickListener(this);
send_btn4.setOnClickListener(this);
}
/* BluetoothLeService绑定的回调函数 */
private final ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName,
IBinder service) {
mBluetoothLeService = ((BluetoothLeService.LocalBinder) service)
.getService();
if (!mBluetoothLeService.initialize()) {
Log.e(TAG, "Unable to initialize Bluetooth");
finish();
}
// Automatically connects to the device upon successful start-up
// initialization.
// 根据蓝牙地址,连接设备
mBluetoothLeService.connect(mDeviceAddress);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mBluetoothLeService = null;
}
};
/**
* 广播接收器,负责接收BluetoothLeService类发送的数据
*/
private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action))//Gatt连接成功
{
mConnected = true;
status = "connected";
//更新连接状态
updateConnectionState(status);
System.out.println("BroadcastReceiver :" + "device connected");
} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED//Gatt连接失败
.equals(action)) {
mConnected = false;
status = "disconnected";
//更新连接状态
updateConnectionState(status);
System.out.println("BroadcastReceiver :"
+ "device disconnected");
} else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED//发现GATT服务器
.equals(action)) {
// Show all the supported services and characteristics on the
// user interface.
//获取设备的所有蓝牙服务
displayGattServices(mBluetoothLeService
.getSupportedGattServices());
System.out.println("BroadcastReceiver :"
+ "device SERVICES_DISCOVERED");
} else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action))//有效数据
{
//处理发送过来的数据
displayData(intent.getExtras().getString(
BluetoothLeService.EXTRA_DATA), intent);
System.out.println("BroadcastReceiver onData:"
+ intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
}
}
};
/* 更新连接状态 */
private void updateConnectionState(String status) {
Message msg = new Message();
msg.what = 1;
Bundle b = new Bundle();
b.putString("connect_state", status);
msg.setData(b);
//将连接状态更新的UI的textview上
myHandler.sendMessage(msg);
System.out.println("connect_state:" + status);
}
/* 意图过滤器 */
private static IntentFilter makeGattUpdateIntentFilter() {
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
intentFilter
.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);
return intentFilter;
}
/**
* @param @param rev_string(接受的数据)
* @return void
* @throws
* @Title: displayData
* @Description: TODO(接收到的数据在scrollview上显示)
*/
private void displayData(String rev_string, Intent intent) {
try {
byte[] data = intent.getByteArrayExtra("BLE_BYTE_DATA");
if (data == null)
System.out.println("data is null!!!!!!");
//GB2312编码
rev_string = new String(data, 0, data.length, "GB2312");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
rev_str += rev_string;
//更新UI
runOnUiThread(new Runnable() {
@Override
public void run() {
rev_tv.setText(rev_str);
rev_sv.scrollTo(0, rev_tv.getMeasuredHeight());
System.out.println("rev:" + rev_str);
}
});
}
/**
* @param 无
* @return void
* @throws
* @Title: displayGattServices
* @Description: TODO(处理蓝牙服务)
*/
private void displayGattServices(List
if (gattServices == null)
return;
String uuid = null;
String unknownServiceString = "unknown_service";
String unknownCharaString = "unknown_characteristic";
// 服务数据,可扩展下拉列表的第一级数据
ArrayList
// 特征数据(隶属于某一级服务下面的特征值集合)
ArrayList
// 部分层次,所有特征值集合
mGattCharacteristics = new ArrayList
// Loops through available GATT Services.
for (BluetoothGattService gattService : gattServices) {
// 获取服务列表
HashMap
uuid = gattService.getUuid().toString();
// 查表,根据该uuid获取对应的服务名称。SampleGattAttributes这个表需要自定义。
gattServiceData.add(currentServiceData);
System.out.println("Service uuid:" + uuid);
ArrayList
// 从当前循环所指向的服务中读取特征值列表
List
.getCharacteristics();
ArrayList
// Loops through available Characteristics.
// 对于当前循环所指向的服务中的每一个特征值
for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
charas.add(gattCharacteristic);
HashMap
uuid = gattCharacteristic.getUuid().toString();
if (gattCharacteristic.getUuid().toString()
.equals(HEART_RATE_MEASUREMENT)) {
// 测试读取当前Characteristic数据,会触发mOnDataAvailable.onCharacteristicRead()
mhandler.postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
mBluetoothLeService
.readCharacteristic(gattCharacteristic);
}
}, 200);
// 接受Characteristic被写的通知,收到蓝牙模块的数据后会触发mOnDataAvailable.onCharacteristicWrite()
mBluetoothLeService.setCharacteristicNotification(
gattCharacteristic, true);
target_chara = gattCharacteristic;
// 设置数据内容
// 往蓝牙模块写入数据
// mBluetoothLeService.writeCharacteristic(gattCharacteristic);
}
List
.getDescriptors();
for (BluetoothGattDescriptor descriptor : descriptors) {
System.out.println("---descriptor UUID:"
+ descriptor.getUuid());
// 获取特征值的描述
mBluetoothLeService.getCharacteristicDescriptor(descriptor);
// mBluetoothLeService.setCharacteristicNotification(gattCharacteristic,
// true);
}
gattCharacteristicGroupData.add(currentCharaData);
}
// 按先后顺序,分层次放入特征值集合中,只有特征值
mGattCharacteristics.add(charas);
// 构件第二级扩展列表(服务下面的特征值)
gattCharacteristicData.add(gattCharacteristicGroupData);
}
}
/**
* 将数据分包
**/
public int[] dataSeparate(int len) {
int[] lens = new int[2];
lens[0] = len / 20;
lens[1] = len % 20;
return lens;
}
/*
* 数据发送线程
*
* */
public class sendDataThread implements Runnable {
public sendDataThread() {
super();
new Thread(this).start();
}
@Override
public void run() {
byte[] buff = null;
try {
buff =String.valueOf(sendFlag).getBytes("utf-8");//将输入框的数据转换成utf-8格式的数据存入字节数组
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
int[] sendDatalens = dataSeparate(buff.length);//分散数据
for (int i = 0; i < sendDatalens[0]; i++)//
{
byte[] dataFor20 = new byte[20];//定义一个长度为20的字节数组
for (int j = 0; j < 20; j++) {
dataFor20[j] = buff[i * 20 + j];//给数组单元赋值
}
target_chara.setValue(dataFor20);
mBluetoothLeService.writeCharacteristic(target_chara);
}
if (sendDatalens[1] != 0) {
System.out.println("here2");
byte[] lastData = new byte[20];
for (int i = 0; i < sendDatalens[1]; i++)
lastData[i] = buff[sendDatalens[0] * 20 + i];
String str = null;
try {
str = new String(lastData, 0, sendDatalens[1], "utf-8");//将输入框的数据转换成utf-8格式的数据存入字节数组
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
target_chara.setValue(lastData);
mBluetoothLeService.writeCharacteristic(target_chara);
}
}
}
/*
* 发送按键的响应事件,主要发送文本框的数据
*/
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.send_btn1:
sendFlag = 1;
//new sendDataThread();
break;
case R.id.send_btn2:
sendFlag = 2;
//new sendDataThread();
break;
case R.id.send_btn3:
sendFlag = 3;
//new sendDataThread();
break;
case R.id.send_btn4:
sendFlag = 4;
//new sendDataThread();
break;
}
new sendDataThread();
}
}
*********************************4、BluetoothLeService.java************************
package com.example.bluetoothrobot;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
//import org.apache.http.entity.ByteArrayEntity;
//import com.example.blutoothhc.Ble_Activity;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.hardware.Sensor;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
/**
* 特别说明:HC_BLE助手是广州汇承信息科技有限公司独自研发的手机APP,方便用户调试08蓝牙模块。
* 本软件只能支持安卓版本4.3并且有蓝牙4.0的手机使用。
* 另外对于自家的05、06模块,要使用另外一套蓝牙2.0的手机APP,用户可以在汇承官方网的下载中心自行下载。
* 本软件提供代码和注释,免费给购买汇承08模块的用户学习和研究,但不能用于商业开发,最终解析权在广州汇承信息科技有限公司。
* **/
/**
* @Description: TODO<蓝牙服务,负责在后台实现蓝牙的连接,数据的发送接受>
* @author 广州汇承信息科技有限公司
* @data: 2014-10-22 下午2:30:38
* @version: V1.0
*/
public class BluetoothLeService extends Service {
private final static String TAG = "BluetoothLeService";// luetoothLeService.class.getSimpleName();
private List
//蓝牙相关类
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private String mBluetoothDeviceAddress;
private BluetoothGatt mBluetoothGatt;
private int mConnectionState = STATE_DISCONNECTED;
private static final int STATE_DISCONNECTED = 0;
private static final int STATE_CONNECTING = 1;
private static final int STATE_CONNECTED = 2;
public final static String ACTION_GATT_CONNECTED = "com.hc_ble.bluetooth.le.ACTION_GATT_CONNECTED";
public final static String ACTION_GATT_DISCONNECTED = "com.hc_ble.bluetooth.le.ACTION_GATT_DISCONNECTED";
public final static String ACTION_GATT_SERVICES_DISCOVERED = "com.hc_ble.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
public final static String ACTION_DATA_AVAILABLE = "com.hc_ble.bluetooth.le.ACTION_DATA_AVAILABLE";
public final static String EXTRA_DATA = "com.hc_ble.bluetooth.le.EXTRA_DATA";
// public final static UUID UUID_HEART_RATE_MEASUREMENT =zzzzzzzzzzzzz
// UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);
private OnDataAvailableListener mOnDataAvailableListener;
// Implements callback methods for GATT events that the app cares about. For
// example,
// connection change and services discovered.
public interface OnDataAvailableListener {
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status);
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic);
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic);
}
public void setOnDataAvailableListener(OnDataAvailableListener l)
{
mOnDataAvailableListener = l;
}
/* 连接远程设备的回调函数 */
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback()
{
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState)
{
String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED)//连接成功
{
intentAction = ACTION_GATT_CONNECTED;
mConnectionState = STATE_CONNECTED;
/* 通过广播更新连接状态 */
broadcastUpdate(intentAction);
Log.i(TAG, "Connected to GATT server.");
// Attempts to discover services after successful connection.
Log.i(TAG, "Attempting to start service discovery:"
+ mBluetoothGatt.discoverServices());
} else if (newState == BluetoothProfile.STATE_DISCONNECTED)//连接失败
{
intentAction = ACTION_GATT_DISCONNECTED;
mConnectionState = STATE_DISCONNECTED;
Log.i(TAG, "Disconnected from GATT server.");
broadcastUpdate(intentAction);
}
}
/*
* 重写onServicesDiscovered,发现蓝牙服务
*
* */
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status)
{
if (status == BluetoothGatt.GATT_SUCCESS)//发现到服务
{
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
Log.i(TAG, "--onServicesDiscovered called--");
} else
{
Log.w(TAG, "onServicesDiscovered received: " + status);
System.out.println("onServicesDiscovered received: " + status);
}
}
/*
* 特征值的读写
* */
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status)
{
if (status == BluetoothGatt.GATT_SUCCESS)
{
Log.i(TAG, "--onCharacteristicRead called--");
//从特征值读取数据
byte[] sucString = characteristic.getValue();
String string = new String(sucString);
//将数据通过广播到Ble_Activity
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
/*
* 特征值的改变
* */
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic)
{
System.out.println("++++++++++++++++");
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
/*
* 特征值的写
* */
@Override
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status)
{
Log.w(TAG, "--onCharacteristicWrite--: " + status);
// 以下语句实现 发送完数据或也显示到界面上
//broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
/*
* 读描述值
* */
@Override
public void onDescriptorRead(BluetoothGatt gatt,
BluetoothGattDescriptor descriptor, int status)
{
// TODO Auto-generated method stub
// super.onDescriptorRead(gatt, descriptor, status);
Log.w(TAG, "----onDescriptorRead status: " + status);
byte[] desc = descriptor.getValue();
if (desc != null)
{
Log.w(TAG, "----onDescriptorRead value: " + new String(desc));
}
}
/*
* 写描述值
* */
@Override
public void onDescriptorWrite(BluetoothGatt gatt,
BluetoothGattDescriptor descriptor, int status)
{
// TODO Auto-generated method stub
// super.onDescriptorWrite(gatt, descriptor, status);
Log.w(TAG, "--onDescriptorWrite--: " + status);
}
/*
* 读写蓝牙信号值
* */
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status)
{
// TODO Auto-generated method stub
// super.onReadRemoteRssi(gatt, rssi, status);
Log.w(TAG, "--onReadRemoteRssi--: " + status);
broadcastUpdate(ACTION_DATA_AVAILABLE, rssi);
}
@Override
public void onReliableWriteCompleted(BluetoothGatt gatt, int status)
{
// TODO Auto-generated method stub
// super.onReliableWriteCompleted(gatt, status);
Log.w(TAG, "--onReliableWriteCompleted--: " + status);
}
};
//广播意图
private void broadcastUpdate(final String action, int rssi)
{
final Intent intent = new Intent(action);
intent.putExtra(EXTRA_DATA, String.valueOf(rssi));
sendBroadcast(intent);
}
//广播意图
private void broadcastUpdate(final String action)
{
final Intent intent = new Intent(action);
sendBroadcast(intent);
}
/* 广播远程发送过来的数据 */
public void broadcastUpdate(final String action,
final BluetoothGattCharacteristic characteristic)
{
final Intent intent = new Intent(action);
//从特征值获取数据
final byte[] data = characteristic.getValue();
Ble_Activity.revDataForCharacteristic =data;
if (data != null && data.length > 0)
{
final StringBuilder stringBuilder = new StringBuilder(data.length);
for (byte byteChar : data)
{
stringBuilder.append(String.format("%02X ", byteChar));
Log.i(TAG, "***broadcastUpdate: byteChar = " + byteChar);
}
intent.putExtra("BLE_BYTE_DATA", data);
intent.putExtra(EXTRA_DATA, new String(data));
System.out.println("broadcastUpdate for read data:"
+ new String(data));
}
sendBroadcast(intent);
}
public class LocalBinder extends Binder {
public BluetoothLeService getService()
{
return BluetoothLeService.this;
}
}
@Override
public IBinder onBind(Intent intent)
{
return mBinder;
}
@Override
public boolean onUnbind(Intent intent)
{
close();
return super.onUnbind(intent);
}
private final IBinder mBinder = new LocalBinder();
/**
* Initializes a reference to the local Bluetooth adapter.
*
* @return Return true if the initialization is successful.
*/
/* service 中蓝牙初始化 */
public boolean initialize()
{
// For API level 18 and above, get a reference to BluetoothAdapter
// through
// BluetoothManager.
if (mBluetoothManager == null)
{ //获取系统的蓝牙管理器
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null)
{
Log.e(TAG, "Unable to initialize BluetoothManager.");
return false;
}
}
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null)
{
Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
return false;
}
return true;
}
/**
* Connects to the GATT server hosted on the Bluetooth LE device.
*
* @param address
* The device address of the destination device.
*
* @return Return true if the connection is initiated successfully. The
* connection result is reported asynchronously through the
* {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
* callback.
*/
// 连接远程蓝牙
public boolean connect(final String address)
{
if (mBluetoothAdapter == null || address == null)
{
Log.w(TAG,
"BluetoothAdapter not initialized or unspecified address.");
return false;
}
// Previously connected device. Try to reconnect.
if (mBluetoothDeviceAddress != null
&& address.equals(mBluetoothDeviceAddress)
&& mBluetoothGatt != null)
{
Log.d(TAG,
"Trying to use an existing mBluetoothGatt for connection.");
if (mBluetoothGatt.connect())//连接蓝牙,其实就是调用BluetoothGatt的连接方法
{
mConnectionState = STATE_CONNECTING;
return true;
} else
{
return false;
}
}
/* 获取远端的蓝牙设备 */
final BluetoothDevice device = mBluetoothAdapter
.getRemoteDevice(address);
if (device == null)
{
Log.w(TAG, "Device not found. Unable to connect.");
return false;
}
// We want to directly connect to the device, so we are setting the
// autoConnect
// parameter to false.
/* 调用device中的connectGatt连接到远程设备 */
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
Log.d(TAG, "Trying to create a new connection.");
mBluetoothDeviceAddress = address;
mConnectionState = STATE_CONNECTING;
System.out.println("device.getBondState==" + device.getBondState());
return true;
}
/**
* Disconnects an existing connection or cancel a pending connection. The
* disconnection result is reported asynchronously through the
* {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
* callback.
*/
/*
* 取消连接
*
* */
/**
* @Title: disconnect
* @Description: TODO(取消蓝牙连接)
* @param 无
* @return void
* @throws
*/
public void disconnect()
{
if (mBluetoothAdapter == null || mBluetoothGatt == null)
{
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.disconnect();
}
/**
* After using a given BLE device, the app must call this method to ensure
* resources are released properly.
*/
/**
* @Title: close
* @Description: TODO(关闭所有蓝牙连接)
* @param 无
* @return void
* @throws
*/
public void close()
{
if (mBluetoothGatt == null)
{
return;
}
mBluetoothGatt.close();
mBluetoothGatt = null;
}
/**
* Request a read on a given {@code BluetoothGattCharacteristic}. The read
* result is reported asynchronously through the
* {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}
* callback.
*
* @param characteristic
* The characteristic to read from.
*/
/**
* @Title: readCharacteristic
* @Description: TODO(读取特征值)
* @param @param characteristic(要读的特征值)
* @return void 返回类型
* @throws
*/
public void readCharacteristic(BluetoothGattCharacteristic characteristic)
{
if (mBluetoothAdapter == null || mBluetoothGatt == null)
{
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.readCharacteristic(characteristic);
}
// 写入特征值
public void writeCharacteristic(BluetoothGattCharacteristic characteristic)
{
if (mBluetoothAdapter == null || mBluetoothGatt == null)
{
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.writeCharacteristic(characteristic);
}
// 读取RSSi
public void readRssi()
{
if (mBluetoothAdapter == null || mBluetoothGatt == null)
{
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.readRemoteRssi();
}
/**
* Enables or disables notification on a give characteristic.
*
* @param characteristic
* Characteristic to act on.
* @param enabled
* If true, enable notification. False otherwise.
*/
/**
* @Title: setCharacteristicNotification
* @Description: TODO(设置特征值通变化通知)
* @param @param characteristic(特征值)
* @param @param enabled (使能)
* @return void
* @throws
*/
public void setCharacteristicNotification(
BluetoothGattCharacteristic characteristic, boolean enabled)
{
if (mBluetoothAdapter == null || mBluetoothGatt == null)
{
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
BluetoothGattDescriptor clientConfig = characteristic
.getDescriptor(UUID
.fromString("00002902-0000-1000-8000-00805f9b34fb"));
if (enabled)
{
clientConfig
.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
} else
{
clientConfig
.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
}
mBluetoothGatt.writeDescriptor(clientConfig);
}
/**
* @Title: getCharacteristicDescriptor
* @Description: TODO(得到特征值下的描述值)
* @param @param 无
* @return void
* @throws
*/
public void getCharacteristicDescriptor(BluetoothGattDescriptor descriptor)
{
if (mBluetoothAdapter == null || mBluetoothGatt == null)
{
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.readDescriptor(descriptor);
}
/**
* Retrieves a list of supported GATT services on the connected device. This
* should be invoked only after {@code BluetoothGatt#discoverServices()}
* completes successfully.
*
* @return A {@code List} of supported services.
*/
/**
* @Title: getSupportedGattServices
* @Description: TODO(得到蓝牙的所有服务)
* @param @return 无
* @return List
* @throws
*/
public List
{
if (mBluetoothGatt == null)
return null;
return mBluetoothGatt.getServices();
}
}
*********************************5、activity_main.xml************************
android:layout_width="match_parent"
android:layout_height="match_parent"
>
*********************************6、ble_activity.xml*****************************
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#2971cf"
android:orientation="horizontal">
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dip"
android:text="状态:" />
android:id="@+id/connect_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="4">
android:id="@+id/rev_sv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1">
android:id="@+id/rev_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="8"
android:orientation="vertical">
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#16248b">
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#16248b"
android:orientation="horizontal">
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#16248b">
android:id="@+id/send_btn4"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/buttonstyle_three"
android:text="后退"
android:textSize="30dp" />
*********************************7、listitem.xml*****************************
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@drawable/bg_button">
android:layout_width="match_parent"
android:layout_height="wrap_content" >
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="设备名称:" />
android:id="@+id/tv_deviceName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView" />
android:layout_width="match_parent"
android:layout_height="wrap_content" >
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="设备地址:" />
android:id="@+id/tv_deviceAddr"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView" />
android:layout_width="match_parent"
android:layout_height="wrap_content" >
android:id="@+id/textView5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="RSSI:" />
android:id="@+id/tv_rssi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView" />
**********************************结束语************************************
因为篇幅有限,没有细致讲解,不过代码能用,注意是汇承HC06模块,其他模块尚未测试。先到这,有时间再做细致解读。
单片机端蓝牙与单片机连接时就是串口的连接,编程时当成串口就行,注意,单片机的该串口除了蓝牙模块之外不能连接其他东西,否则可能接收不到APP发送过来的数据。
本程序的功能是在汇承给的代码稍作修改,主要功能就是按下下面几个按钮将向单片机端的蓝牙模块分别发送'1'、'2'、'3'、'4'。结果运行如下图: