基于低功耗蓝牙的手机APP(文末有整个工程的下载连接哦!)
文章目录
- 前言
- 一、低功耗蓝牙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";@Overrideprotected 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() {@Overridepublic void onClick(View v) {readData();}});btnWrite.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//执行写入操作writeData();}});ivSerBleStatus.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (isScaning){tvSerBindStatus.setText("停止搜索");stopScanDevice();}else{checkPermissions();}}});bleListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic 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() {@Overridepublic void run() {//结束扫描mBluetoothAdapter.stopLeScan(scanCallback);runOnUiThread(new Runnable() {@Overridepublic 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() {@Overridepublic 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() {/*** 断开或连接 状态发生变化时调用* */@Overridepublic 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;}}/*** 发现设备(真正建立连接)* */@Overridepublic 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() {@Overridepublic void run() {bleListView.setVisibility(View.GONE);operaView.setVisibility(View.VISIBLE);tvSerBindStatus.setText("已连接");}});}/*** 读操作的回调* */@Overridepublic void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {super.onCharacteristicRead(gatt, characteristic, status);Log.e(TAG,"onCharacteristicRead()");}/*** 写操作的回调* */@Overridepublic void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {super.onCharacteristicWrite(gatt, characteristic, status);Log.e(TAG,"onCharacteristicWrite() status="+status+",value="+HexUtil.encodeHexStr(characteristic.getValue()));}/*** 接收到硬件返回的数据* */@Overridepublic void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {super.onCharacteristicChanged(gatt, characteristic);Log.e(TAG,"onCharacteristicChanged()"+characteristic.getValue());final byte[] data=characteristic.getValue();runOnUiThread(new Runnable() {@Overridepublic void run() {// byte[] 转 stringString 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>() {@Overridepublic 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];//定义一个存放最终字符串的StringBufferStringBuffer 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++;}//如果是'$'就把这个字符加在上面定义的StringBufferif (i==1){String b= list.get(j).toString();stra.append(b);}else{}//如果是'$'就把这个字符加在上面定义的StringBufferif (i==2){String b= list.get(j).toString();strb.append(b);}else{}//如果是'$'就把这个字符加在上面定义的StringBufferif (i==3){String b= list.get(j).toString();strc.append(b);}else{}//如果是'$'就把这个字符加在上面定义的StringBufferif (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;}@Overrideprotected 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() {@Overridepublic void run() {// byte[] 转 stringString 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];//定义一个存放最终字符串的StringBufferStringBuffer 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++;}//如果是'$'就把这个字符加在上面定义的StringBufferif (i==1){String b= list.get(j).toString();stra.append(b);}else{}//如果是'$'就把这个字符加在上面定义的StringBufferif (i==2){String b= list.get(j).toString();strb.append(b);}else{}//如果是'$'就把这个字符加在上面定义的StringBufferif (i==3){String b= list.get(j).toString();strc.append(b);}else{}//如果是'$'就把这个字符加在上面定义的StringBufferif (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)来学习低功耗蓝牙