文章目录
- 前言
- 一、低功耗蓝牙BLE是什么?
- 二、基于低功耗蓝牙的手机APP
- 1.工程源码
- 2.蓝牙接收数据函数
- 3.数据处理函数(可自定义)
一、低功耗蓝牙BLE是什么?
低功耗蓝牙是一种全新的技术,是当前可以用来设计和使用的功耗最低的无线技术。
经典蓝牙的设计目的在于统一全球各地的计算和通信设备,让手机与笔记本电脑相互连接。不过事实证明,蓝牙最为广泛的应用还是音频传输,比如将音频从手机传到蓝牙耳机。
低功耗蓝牙选择了完全不同的方向:并非只是增加可达的数据传输速率,而是从尽可能降低功耗方面进行优化。这意味着,也许你无法获得很高的传输速率,但是可以将连接保持数小时或数天的时间。
二、基于低功耗蓝牙的手机APP
1.工程源码
关于低功耗蓝牙的一些操作,包括开启、搜索蓝牙等等
package com.kaka.bluetoothble;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.blankj.utilcode.util.ToastUtils;
import com.kaka.bluetoothble.adapter.BleAdapter;
import com.tbruyelle.rxpermissions2.RxPermissions;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static android.bluetooth.BluetoothDevice.TRANSPORT_LE;
public class MainActivity extends AppCompatActivity {
private static final String TAG ="ble_tag" ;
ProgressBar pbSearchBle;
ImageView ivSerBleStatus;
TextView tvSerBleStatus;
TextView tvSerBindStatus;
ListView bleListView;
private LinearLayout operaView;
private Button btnWrite;
private Button btnRead;
private EditText etWriteContent;
private TextView tvResponse;
private TextView tvR;
private TextView tvxlv;
private TextView tvpa;
private TextView tvgps;
private List<BluetoothDevice> mDatas;
private List<Integer> mRssis;
private BleAdapter mAdapter;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothManager mBluetoothManager;
private boolean isScaning=false;
private boolean isConnecting=false;
private BluetoothGatt mBluetoothGatt;
//服务和特征值
private UUID write_UUID_service;
private UUID write_UUID_chara;
private UUID read_UUID_service;
private UUID read_UUID_chara;
private UUID notify_UUID_service;
private UUID notify_UUID_chara;
private UUID indicate_UUID_service;
private UUID indicate_UUID_chara;
private String hex="7B46363941373237323532443741397D";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search_device);
initView();
initData();
mBluetoothManager= (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
mBluetoothAdapter=mBluetoothManager.getAdapter();
if (mBluetoothAdapter==null||!mBluetoothAdapter.isEnabled()){
Intent intent=new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent,0);
}
}
private void initData() {
mDatas=new ArrayList<>();
mRssis=new ArrayList<>();
mAdapter=new BleAdapter(MainActivity.this,mDatas,mRssis);
bleListView.setAdapter(mAdapter);
mAdapter.notifyDataSetChanged();
}
private void initView(){
pbSearchBle=findViewById(R.id.progress_ser_bluetooth);
ivSerBleStatus=findViewById(R.id.iv_ser_ble_status);
tvSerBindStatus=findViewById(R.id.tv_ser_bind_status);
tvSerBleStatus=findViewById(R.id.tv_ser_ble_status);
bleListView=findViewById(R.id.ble_list_view);
operaView=findViewById(R.id.opera_view);
btnWrite=findViewById(R.id.btnWrite);
btnRead=findViewById(R.id.btnRead);
etWriteContent=findViewById(R.id.et_write);
tvResponse=findViewById(R.id.tv_response);
tvR=findViewById(R.id.tv_R);
tvxlv=findViewById(R.id.tv_XLV);
tvpa=findViewById(R.id.tv_Pa);
btnRead.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
readData();
}
});
btnWrite.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//执行写入操作
writeData();
}
});
ivSerBleStatus.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (isScaning){
tvSerBindStatus.setText("停止搜索");
stopScanDevice();
}else{
checkPermissions();
}
}
});
bleListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (isScaning){
stopScanDevice();
}
if (!isConnecting){
isConnecting=true;
BluetoothDevice bluetoothDevice= mDatas.get(position);
//连接设备
tvSerBindStatus.setText("连接中");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mBluetoothGatt = bluetoothDevice.connectGatt(MainActivity.this,
true, gattCallback, TRANSPORT_LE);
} else {
mBluetoothGatt = bluetoothDevice.connectGatt(MainActivity.this,
true, gattCallback);
}
}
}
});
}
private void readData() {
BluetoothGattCharacteristic characteristic=mBluetoothGatt.getService(read_UUID_service)
.getCharacteristic(read_UUID_chara);
mBluetoothGatt.readCharacteristic(characteristic);
}
/**
* 开始扫描 10秒后自动停止
* */
private void scanDevice(){
tvSerBindStatus.setText("正在搜索");
isScaning=true;
pbSearchBle.setVisibility(View.VISIBLE);
mBluetoothAdapter.startLeScan(scanCallback);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//结束扫描
mBluetoothAdapter.stopLeScan(scanCallback);
runOnUiThread(new Runnable() {
@Override
public void run() {
isScaning=false;
pbSearchBle.setVisibility(View.GONE);
tvSerBindStatus.setText("搜索已结束");
}
});
}
},10000);
}
/**
* 停止扫描
* */
private void stopScanDevice(){
isScaning=false;
pbSearchBle.setVisibility(View.GONE);
mBluetoothAdapter.stopLeScan(scanCallback);
}
BluetoothAdapter.LeScanCallback scanCallback=new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
Log.e(TAG, "run: scanning...");
if (!mDatas.contains(device)){
mDatas.add(device);
mRssis.add(rssi);
mAdapter.notifyDataSetChanged();
}
}
};
private BluetoothGattCallback gattCallback=new BluetoothGattCallback() {
/**
* 断开或连接 状态发生变化时调用
* */
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
Log.e(TAG,"onConnectionStateChange()");
if (status==BluetoothGatt.GATT_SUCCESS){
//连接成功
if (newState== BluetoothGatt.STATE_CONNECTED){
Log.e(TAG,"连接成功");
//发现服务
gatt.discoverServices();
}
}else{
//连接失败
Log.e(TAG,"失败=="+status);
mBluetoothGatt.close();
isConnecting=false;
}
}
/**
* 发现设备(真正建立连接)
* */
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
//直到这里才是真正建立了可通信的连接
isConnecting=false;
Log.e(TAG,"onServicesDiscovered()---建立连接");
//获取初始化服务和特征值
initServiceAndChara();
//订阅通知
mBluetoothGatt.setCharacteristicNotification(mBluetoothGatt
.getService(notify_UUID_service).getCharacteristic(notify_UUID_chara),true);
runOnUiThread(new Runnable() {
@Override
public void run() {
bleListView.setVisibility(View.GONE);
operaView.setVisibility(View.VISIBLE);
tvSerBindStatus.setText("已连接");
}
});
}
/**
* 读操作的回调
* */
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
Log.e(TAG,"onCharacteristicRead()");
}
/**
* 写操作的回调
* */
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
Log.e(TAG,"onCharacteristicWrite() status="+status+",value="+HexUtil.encodeHexStr(characteristic.getValue()));
}
/**
* 接收到硬件返回的数据
* */
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.e(TAG,"onCharacteristicChanged()"+characteristic.getValue());
final byte[] data=characteristic.getValue();
runOnUiThread(new Runnable() {
@Override
public void run() {
// byte[] 转 string
String res = new String(data);
// addText(tvResponse,bytes2hex(data));
// addText(tvR,bytes2hex(data));
String[] stra=new String[10];
stra=getString(res);
tvResponse.setText(stra[1].toString()+" ℃");
tvR.setText(stra[2].toString()+" RH");
tvxlv.setText(stra[3].toString()+" ℃");
tvpa.setText(stra[4].toString()+" RH");
}
});
}
};
/**
* 检查权限
*/
private void checkPermissions() {
RxPermissions rxPermissions = new RxPermissions(MainActivity.this);
rxPermissions.request(android.Manifest.permission.ACCESS_FINE_LOCATION)
.subscribe(new io.reactivex.functions.Consumer<Boolean>() {
@Override
public void accept(Boolean aBoolean) throws Exception {
if (aBoolean) {
// 用户已经同意该权限
scanDevice();
} else {
// 用户拒绝了该权限,并且选中『不再询问』
ToastUtils.showLong("用户开启权限后才能使用");
}
}
});
}
private void initServiceAndChara(){
List<BluetoothGattService> bluetoothGattServices= mBluetoothGatt.getServices();
for (BluetoothGattService bluetoothGattService:bluetoothGattServices){
List<BluetoothGattCharacteristic> characteristics=bluetoothGattService.getCharacteristics();
for (BluetoothGattCharacteristic characteristic:characteristics){
int charaProp = characteristic.getProperties();
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
read_UUID_chara=characteristic.getUuid();
read_UUID_service=bluetoothGattService.getUuid();
Log.e(TAG,"read_chara="+read_UUID_chara+"----read_service="+read_UUID_service);
}
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0) {
write_UUID_chara=characteristic.getUuid();
write_UUID_service=bluetoothGattService.getUuid();
Log.e(TAG,"write_chara="+write_UUID_chara+"----write_service="+write_UUID_service);
}
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) > 0) {
write_UUID_chara=characteristic.getUuid();
write_UUID_service=bluetoothGattService.getUuid();
Log.e(TAG,"write_chara="+write_UUID_chara+"----write_service="+write_UUID_service);
}
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
notify_UUID_chara=characteristic.getUuid();
notify_UUID_service=bluetoothGattService.getUuid();
Log.e(TAG,"notify_chara="+notify_UUID_chara+"----notify_service="+notify_UUID_service);
}
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_INDICATE) > 0) {
indicate_UUID_chara=characteristic.getUuid();
indicate_UUID_service=bluetoothGattService.getUuid();
Log.e(TAG,"indicate_chara="+indicate_UUID_chara+"----indicate_service="+indicate_UUID_service);
}
}
}
}
//将字符串数据进行显示的函数
private void addText(TextView textView, String content) {
textView.setText(content+"℃");
}
private void writeData(){
BluetoothGattService service=mBluetoothGatt.getService(write_UUID_service);
BluetoothGattCharacteristic charaWrite=service.getCharacteristic(write_UUID_chara);
byte[] data;
String content=etWriteContent.getText().toString();
if (!TextUtils.isEmpty(content)){
data=HexUtil.hexStringToBytes(content);
}else{
data=HexUtil.hexStringToBytes(hex);
}
if (data.length>20){//数据大于个字节 分批次写入
Log.e(TAG, "writeData: length="+data.length);
int num=0;
if (data.length%20!=0){
num=data.length/20+1;
}else{
num=data.length/20;
}
for (int i=0;i<num;i++){
byte[] tempArr;
if (i==num-1){
tempArr=new byte[data.length-i*20];
System.arraycopy(data,i*20,tempArr,0,data.length-i*20);
}else{
tempArr=new byte[20];
System.arraycopy(data,i*20,tempArr,0,20);
}
charaWrite.setValue(tempArr);
mBluetoothGatt.writeCharacteristic(charaWrite);
}
}else{
charaWrite.setValue(data);
mBluetoothGatt.writeCharacteristic(charaWrite);
}
}
private static final String HEX = "0123456789abcdef";
public static String bytes2hex(byte[] bytes)
{
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (byte b : bytes)
{
// 取出这个字节的高4位,然后与0x0f与运算,得到一个0-15之间的数据,通过HEX.charAt(0-15)即为16进制数
sb.append(HEX.charAt((b >> 4) & 0x0f));
// 取出这个字节的低位,与0x0f与运算,得到一个0-15之间的数据,通过HEX.charAt(0-15)即为16进制数
sb.append(HEX.charAt(b & 0x0f));
}
return sb.toString();
}
//下面是一种稍复杂的写法,将单片机传输过来的数据进行分解,最后得到一个字符串数组
public static String[] getString(String str) {
//先定义一个集合来存放分解后的字符
List<String> list = new ArrayList<String>();
String streee = "";
for (int i = 0; i <str.length(); i++) {
streee = str.substring(i, i + 1);
list.add(streee);
}
//定义一个存放最终字符串的数组
String[] strk=new String[10];
//定义一个存放最终字符串的StringBuffer
StringBuffer stra = new StringBuffer();
StringBuffer strb = new StringBuffer();
StringBuffer strc = new StringBuffer();
StringBuffer strd = new StringBuffer();
StringBuffer stre = new StringBuffer();
int i=0;
for (int j = 0; j < list.size(); j++) {
String a = list.get(j).toString();
if (a.equals("$")){
i++;
j++;
}
//如果是'$'就把这个字符加在上面定义的StringBuffer
if (i==1){
String b= list.get(j).toString();
stra.append(b);
}else{
}
//如果是'$'就把这个字符加在上面定义的StringBuffer
if (i==2){
String b= list.get(j).toString();
strb.append(b);
}else{
}
//如果是'$'就把这个字符加在上面定义的StringBuffer
if (i==3){
String b= list.get(j).toString();
strc.append(b);
}else{
}
//如果是'$'就把这个字符加在上面定义的StringBuffer
if (i==4){
String b= list.get(j).toString();
strd.append(b);
}else{
}
}
strk[1]=stra.toString();
strk[2]=strb.toString();
strk[3]=strc.toString();
strk[4]=strd.toString();
return strk;
}
@Override
protected void onDestroy() {
super.onDestroy();
mBluetoothGatt.disconnect();
}
}
2.蓝牙接收数据函数
data为蓝牙接收到的数据
/**
* 接收到硬件返回的数据
* */
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.e(TAG,"onCharacteristicChanged()"+characteristic.getValue());
final byte[] data=characteristic.getValue();
runOnUiThread(new Runnable() {
@Override
public void run() {
// byte[] 转 string
String res = new String(data);
// addText(tvResponse,bytes2hex(data));
// addText(tvR,bytes2hex(data));
String[] stra=new String[10];
stra=getString(res);
tvResponse.setText(stra[1].toString()+" ℃");
tvR.setText(stra[2].toString()+" RH");
tvxlv.setText(stra[3].toString()+" ℃");
tvpa.setText(stra[4].toString()+" RH");
}
});
}
3.数据处理函数
可以自定义数据处理函数
//下面是一种稍复杂的写法,将单片机传输过来的数据进行分解,最后得到一个字符串数组
public static String[] getString(String str) {
//先定义一个集合来存放分解后的字符
List<String> list = new ArrayList<String>();
String streee = "";
for (int i = 0; i <str.length(); i++) {
streee = str.substring(i, i + 1);
list.add(streee);
}
//定义一个存放最终字符串的数组
String[] strk=new String[10];
//定义一个存放最终字符串的StringBuffer
StringBuffer stra = new StringBuffer();
StringBuffer strb = new StringBuffer();
StringBuffer strc = new StringBuffer();
StringBuffer strd = new StringBuffer();
StringBuffer stre = new StringBuffer();
int i=0;
for (int j = 0; j < list.size(); j++) {
String a = list.get(j).toString();
if (a.equals("$")){
i++;
j++;
}
//如果是'$'就把这个字符加在上面定义的StringBuffer
if (i==1){
String b= list.get(j).toString();
stra.append(b);
}else{
}
//如果是'$'就把这个字符加在上面定义的StringBuffer
if (i==2){
String b= list.get(j).toString();
strb.append(b);
}else{
}
//如果是'$'就把这个字符加在上面定义的StringBuffer
if (i==3){
String b= list.get(j).toString();
strc.append(b);
}else{
}
//如果是'$'就把这个字符加在上面定义的StringBuffer
if (i==4){
String b= list.get(j).toString();
strd.append(b);
}else{
}
}
strk[1]=stra.toString();
strk[2]=strb.toString();
strk[3]=strc.toString();
strk[4]=strd.toString();
return strk;
}
总结
工程下载链接:低功耗蓝牙手机APP工程文件
该低功耗蓝牙APP工程源码以及APK文件可通过我的资源进行下载
(1)该工程为基本模板,可以基于本工程进行进一步开发
(2)根据自己的需求进行定制化开发
(3)来学习低功耗蓝牙