2020/7/16更新:项目翻盘
一 项目流程概览:(已经将本项目中的敏感技术剔除,转化成通用的技术方案)
- 申请权限(位置权限、提示使用蓝牙需要打开位置信息。打开蓝牙的权限,)
- 扫描蓝牙
- 连接/断开蓝牙
- 接收/发送数据
- 解析数据
- 以图表动态的显示数据的变化
- 保存数据到excel中
1.申请权限:
蓝牙权限:在manifest/AndroidManifest.xml中添加需要的所有权限(建议直接复制):
<!-- 蓝牙、位置所需权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <!-- 在Android6.0 定位权限申请 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- 在Android10.0里,获取定位权限需要增加以下权限。 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/> -->
<!-- 过滤掉没有蓝牙BLE功能的蓝牙按时required为true时,则应用只能在支持BLE的Android设备上安装运行;required为false时,Android设备均可正常安装运行,需要在代码运行时判断设备是否支持BLE feature: -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="true" />
2.扫描蓝牙
- 扫描过程通过回调来实现,因此基础不孬者希望你自己去补充这方面的知识。
【笔者比较笨,学了一周才理清楚一点】
大致思路:在扫描页面中去实现bleutil类(一个将ble所有操作集中到一个地方的类,见源代码)的回调函数。
/**
* 扫描回调注册
*/
private void scanBle() {
bleConnectUtil.bluetoothIsAble(new MyBleCallBack() {
@Override
public void callbleBack(BluetoothDevice device) {
Log.d(TAG,"--->搜索出蓝牙名字为:"+ device.getName());//device.getName():bleutil扫描到的蓝牙名字
if (device == null) {
return;
}
boolean NoAdd = true;//健壮性,可忽略
for (BluetoothDevice oldDevice : listDevice) {//若多次扫描到同一设备。只显示一次
if (oldDevice.getAddress().equals(device.getAddress())) {
NoAdd = false;
break;
}
}
if (NoAdd) {//第一次扫描则将其加入到设备列表中去,listDevice等杂七杂八的见源代码
listDevice.add(device);
listDeviceName.add(device.getName());
adapter.notifyDataSetChanged();//更新页面中的dataset
}
}
});
}
3.连接蓝牙
- 用户通过点击列表中的设备从而进行蓝牙连接
//这个是重载。抄下来就Ok了
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
bleConnectUtil.stopScan();//stop scan
selectPos = position;
dialog.show();//不用管,流畅性处理。
bleConnectUtil.connectBle(listDevice.get(position));//连接蓝牙,在bleutil那边会返回一个handler数值,在本页面中接受这个handler就知道是否接受成功了。
}
//-------------------------------------------------检查连接结果----------------------------------------------------
1.子线程发送连接请求
/**
* Connet子线程、当点击蓝牙名字后重复向蓝牙发送链接请求,超过2次未成功发送连接超时msg给handler,成功则不用管
*/
Runnable checkConnetRunnable = new Runnable() {
@Override
public void run() {
if (!bleFlag) {
//>2
if (regainBleDataCount > 2) {
//发送带标记的消息(内部创建了message,并设置msg.what = 1000)
handler.sendEmptyMessage(1000);
} else {
regainBleDataCount++;
sendDataByBle(currentSendOrder, "");
//延时三秒发送消息
handler.postDelayed(checkConnetRunnable, 3000);
}
}
}
};
2.连接成功由bleutil通过eventbus向mainActivity发送成功信息和特性来完成。
/**
* 扫描蓝牙事件处理
* @param eventMsg :接收蓝牙连接情况
*/
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(EventMsg eventMsg) {
switch (eventMsg.getMsg()) {
case Constants.BLE_CONNECTION_FINISH_MSG:
if (dialog != null) {
dialog.dismiss();
}
bleConnectUtil.stopScan();
btnScan.setText("扫描设备");
// todo 已连接,此处的bleConnectUtil是如何变成充满连接信息的呢?我理解的是bleutil用了main的bleConnectUtil去执行连接操作,人走,茶在。
if (bleConnectUtil.isConnected()) {
Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_SHORT).show();
tvBleName.setVisibility(View.VISIBLE);
tvBleName.setText("您所连接的设备是:" + listDeviceName.get(selectPos));
listview.setVisibility(View.GONE);
// 写一个接受数据的回调。bleUtil一旦接受到数据便使用mainActivity里面的blecallback方法来处理数据.
bleConnectUtil.setCallback(blecallback);
}
//未连接成功
else Toast.makeText(MainActivity.this, "连接失败", Toast.LENGTH_SHORT).show();
break;
default:
break;
}
}
3.连接蓝牙相关其他情况(断开连接,超时)用Handler处理----------------------------------------------
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
//收到消息,不是连接成功的提示,是收到蓝牙发送过来的消息!
case 10:
dialog.dismiss();
tvReceiver.setText(msg.obj.toString());
Log.d(TAG, "hand receive:"+msg.toString());
EventBus.getDefault().postSticky(new DataMsg(msg.obj.toString()));
break;
//连接超时 ,子线程向蓝牙发送链接请求,超时会给它发送1000,便是在这儿接收
case 1000:
regainBleDataCount = 0;//这些是我自己写的标志符。不需要你去理解
bleFlag = false;//这些是我自己写的标志符。不需要你去理解
handler.removeCallbacks(checkConnetRunnable);//不用再发送链接请求
if (dialog != null) {
dialog.dismiss();
}
//超时处理
//Toast.makeText(MainActivity.this, "超时请重试!", Toast.LENGTH_SHORT).show();
break;
//断开连接,或者发送数据失败
case 1111:
//断开连接处理
bleConnectUtil.disConnect();
break;
default:
break;
}
}
};
4.发送数据
发送数据未经过脱敏。这里放一个发送demo函数
private void sendDataByBle_hex(final byte[] currentSendAllOrder) {
final boolean[] isSuccess = new boolean[1];//一个bool大小的发送状态标志符
mBluetoothGattCharacteristic.setValue(currentSendAllOrder);//currentSendAllOrder即发送的字符串
isSuccess[0] = bleConnectUtil.sendData(mBluetoothGattCharacteristic);//接受发送状态
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (!isSuccess[0]) {
dialog.dismiss();
handler.sendEmptyMessage(1111);//发送失败
}
Log.e("--->", "是否发送成功:" + isSuccess[0]);
}
}, (currentSendAllOrder.length / 40 + 1) * 15);//间隔一段时间再发送
}
5.接受数据
由3.连接部分知我已经写好了接收数据的流程了。接下来补充一下具体的处理函数
/**
* 接受数据后的回调处理函数(mainactivity提供环境,bleutil来执行函数)
*/
private BleConnectionCallBack blecallback = new BleConnectionCallBack() {
@Override
public void onRecive(BluetoothGattCharacteristic data_char) {
bleFlag = true;
//收到的数据
byte[] receive_byte = data_char.getValue();
// Log.d(TAG, "接受到的原始数据:"+receive_byte);
//
String str = CheckUtils.toHexString1(receive_byte);
// Log.d(TAG, "数据转化成16进制 is:"+str);
//利用handler传值
Message message = new Message();
message.obj = str;
message.what = 10;
handler.sendMessage(message);
}
@Override
public void onSuccessSend() {
//数据发送成功
Log.e(TAG, "onSuccessSend: ");
}
@Override
public void onDisconnect() {
//设备断开连接
Log.e(TAG, "onDisconnect: ");
Message message = new Message();
message.what = 1111;
handler.sendMessage(message);
}
};
项目已经完成,但是时间关系无法一次讲完。努力将代码脱敏,上传,更新ing。任何疑问可留言。笔者在CSDN买了房的。哈哈哈
- 蓝牙扫描
- 蓝牙连接,断开
- 发送,接受数据
- 根据CRC8算法解析,校验数据,利用折线图(MPchart)动态实时展示数据如电压,电流,压力等,对折线图的造作数据导出成excel并保存在本地
- 批量发送设置参数信息到单片机串口,从而完成APP远程设置单片机的外设相关参数
- 待续
骚话:
毕业菜鸟刚入职,在网上找了大大小小的安卓蓝牙BLE开源项目十几个了。都没有自己喜欢的,现在的计划是自己动手做一个。并且每天将自己学会的东西、认为项目中很重要的东西记录下来。(下面的项目都能运行,但是别人写的都是后台demo。美工和功能性不支持拿来即用,也怪不了别人。我自己加把劲弄一个出来吧)
- 前三天的开发全是去Pilipli找视频资料、百度一些开源项目-努力让他们运行-测试手机和电脑能不能通过软件联通(这之间的蓝牙通讯称为经典蓝牙)。
【因为发现自己做的蓝牙芯片叫JDY 10M,是BLE低功耗蓝牙,几天的努力化为泡沫】
2020/6/11日自己下定决心找一篇写的好一点的教程狠狠的阅读。十遍不行就二十遍!
奉上我拜读的文章: Android开发蓝牙与ble设备的通讯.
#这篇文章我也下载了他的源码来解读,后台功能很职场,需要有一些开发基本思维。(只是涉及到作者将功能模块分成许多文件,目的是解耦合和多复用性)
这里我不讲怎么去做。只是记录下自己每天学习的哪些关键点。项目完成再开贴去讲从头到尾做一个ble蓝牙安卓调试器。
2020/6/12:
- 改变自己一贯的白嫖、能用就Ok的思想,决定死抓一篇文章,分析他用了哪些函数(而不是关注函数内部是怎么运行的)
- 读懂流程之后分析项目是怎样运行的。重点分析了安卓的回调机制。
- 分析hander在安卓中的作用。
2020/6/13:
- 昨天将app的功能基本实现了数据收发功能。死磕了两天的安卓回调。慢慢的心里面对回调机制有一定的了解,但是感觉自己还是不能分析出回调在源码中的运用。先跳过这个
- 晚上2点多还在改导航栏。哎,自己太笨了,一直没把它和原来的项目结合着弄出来。心里别提多难过了、加油吧。
待记录问题:
安卓部分:
- 蓝牙通讯数据包-进制与数据类型关系/数据包的解析/
- char数组/String/怎么输出的
- 单片机给上位机发送的数据通过CRC8循环校验的,我在APP里怎么去验证数据是否正确呢?
- 折线图表要想显示成功,需要在界面处将多余的表的VISIBLE设置成GONE
- 设置界面的数据输入。digitsl参数利用正则可以限定输入的内容、inputtype参数可以设置输入的软键盘类型。
调试部分:
- USB转TTL上面有几个按键,记得把USB播成on、将48拨成off、蓝牙是接3.3V.这些一旦弄错便会烧掉芯片。切记
- 将蓝牙芯片和USB转TTL、串口波特率选115200、勾中发送新行(电脑上的助手才这样)、打开串口,做完以上步骤开始调试。
- (更名)发送 AT+NAME=BlueTest( 也可以这样:AT+NAMEBlueTest)
- (波特率)因为单片机无法发送太快,需要设置波特率为9600、发送AT+BAUD=4(AT+BAUD4)
- (保存设置)AT+RESET
附波特率参数设置表
Param:(0-7)
0:115200 bps
1:57600 bps
2:38400 bps
3:19200 bps
4:9600 bps
5:4800 bps
默认值:0
硬件部分:
- DC电源牢记先连线再通电
备注:
我知道可以通过把原来的数据部分截取出来进行CRC算法得到的值和原来的尾部的校验值比较,来实现验证。但是我觉得接收数据没必要这么复杂,
我在网上找的CRC8资料可以:(数据+校验码)%多项式特征值==0。
如果余为零的话就证明数据无误。
- APP界面跳转的时候以下两句一定要在OnCreate()前面。
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
总结: byte 8位,int 32位,char 16位,short 16位 都是数,只是长度不同,可以相互转换
2进制,8进制,16进制,只是一个数输出的进制方法,数值还是那个数
byte,int,char等的都是数据类型,进制只是数的表现形式
一个BYTE占8位。而16进制由四个二进制表示。所以说呀、一个BYTE能表示两位16进制数据。例如:byte[0]=‘2’ -》 0000 0010
任何疑问联系QQ: 498661202