文章目录

  • 前言
  • 一、低功耗蓝牙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)来学习低功耗蓝牙

基于低功耗蓝牙的手机APP(文末有整个工程的下载连接哦!)相关推荐

  1. 基于51单片机的智能停车场管理车位引导系统红外检测无线蓝牙/WiFi手机APP控制设计

    本系统由STC89C52单片机.(无线蓝牙/WIFI模块-可选).红外对管传感器.LCD1602液晶显示.LED指示灯及电源组成. 通过红外对管模块实时检测车位是否占用,车位分为1车位.2车位.3车位 ...

  2. 基于低功耗蓝牙和微信小程序的门禁系统(FPGA课设设计)

    基于低功耗蓝牙和微信小程序的门禁系统(FPGA课设设计) 文章目录 基于低功耗蓝牙和微信小程序的门禁系统(FPGA课设设计) 一.低功耗蓝牙(BLE)的配置和与FPGA通信代码 1.1 低功耗蓝牙的介 ...

  3. 基于STM32蓝牙控制的app智能台灯设计

    提示:记录毕设 文章目录 前言 一.任务书 1.1设计(研究)目标: 1.2设计(研究)内容: 二.代码思路 三.硬件设计 3.1总体设计 3.2蓝牙部分设计 四.联系我们 五.部分代码 喜欢请点赞哦 ...

  4. 基于AS开发的手机App源码 可以配合单片机wifi通信接收单片机数据

    基于AS开发的手机App源码 可以配合单片机wifi通信接收单片机数据 注:只有文字描述的功能,其它功能无法实现,也不会制作 ID:6939642638150328Uun丶

  5. 一款基于Kotlin+MVP+组件化的麻雀App(文末有彩蛋)

    热文导读 | 点击标题阅读 金九银十跳槽季如何进阶找到合适满意的工作? 美团面试失败(Java开发) 程序员年度总结:2019年,你的出路在哪? 作者:CysionLiu (源码下载地址见文末) 来源 ...

  6. 分布式人工智能:基于TensorFlow RTOS与群体智能体系(文末留言赠书)

    今天,我们向各位读者介绍一本今年9月出版的<分布式人工智能:基于TensorFlow RTOS 与群体智能体系>这本书!欢迎大家在文末留言,集赞最多的5位读者将会免费获得这本书!你可以分享 ...

  7. 树莓派蓝牙与手机APP通信

    文章目录 前言 一.树莓派的蓝牙基本配置 二.安装pybluez库 1.安装pybluez的必要编译环境 2.示例传输代码 3.运行文件 4.可能存在的问题 4.1 无法连接蓝牙 4.2 命令端口显示 ...

  8. 基于STM32动态密码锁(手机APP)_2022

    1. 前言 前一版设计了一款物联网的密码锁,采用MQTT协议连接物联网服务器进行交互,这一版是本地动态密码锁.采用局域网方式完成网络连接,与门锁进行交互,通信设置,生成密码种子,进行动态密匙比对. 这 ...

  9. 基于J2ME蓝牙的手机与计算机通信系统设计1

    第一章 项目概述 1.1背景 随着网络和无线通信技术的发展,以及手机等无线移动设备的处理能力的不断增强,手机已经不再是一个单一的通信设备,它融合了上网.个人信息处理.移动办公.娱乐游戏等功能.各种无线 ...

最新文章

  1. 交易所频频被盗,你该如何保护自己的数字资产?
  2. 风向变了 网络安全又占股市风口
  3. 【开源】接口管理平台eoLinker AMS 开源版3.1.5同步线上版!免费增加大量功能!...
  4. postman接口参数化
  5. 单机搭建Android开发环境(五)
  6. numpy基础(part9)--矩阵
  7. 手机安装python的步骤_小白入门:Python安装的10个步骤,极其细致!!
  8. Free Code Camp现在有本地组
  9. 单机版简易考试系统开发过程讲解(C#注册机、用户注册、考试系统、×××全部源码)...
  10. 硬件基础 —— 电阻
  11. dell r730 xd 安装vmware esxi 5.5 u1
  12. 数据通信技术初级工程师证题库
  13. gis如何加入emf图片_ArcGIS教程:地图导出格式,教你如何选择
  14. Android模拟登陆学信网获取四六级成绩
  15. 使用ESP8266数据上onenet云平台
  16. 学Java需要数学好吗?数学基础差就不能学Java吗?
  17. 在matlab中,利用for循环来生成一个矩阵,并分别对该矩阵的行和列进行求和
  18. nginx日志---log_format详解
  19. 每日一句英语 2012-9-15
  20. 互联网思维根深蒂固,新技术发展方兴未艾

热门文章

  1. Laravel中的多对多关系
  2. java毕业设计的自助旅游导航系统mybatis+源码+调试部署+系统+数据库+lw
  3. Vue3.x动态路由、命名路由和编程式导航
  4. 淘宝天猫除了领优惠券,还可以领商家返利,这个你们知道吗?
  5. Navicat如何导出Excel格式表结构
  6. 08自定义异常类的两个例子(例子2为异常在继承中的应用,且包含模板类的应用)
  7. 易语言到字节集c,易语言字节集到十六进制文本转换方式 - 小R网络
  8. 使用FLuke福禄克MicroScanner2 POE(MS-POE)检测以太网供电
  9. Python读取excel文件和绘画饼图
  10. python爬取58上的招聘信息