目录
想搞一个蓝牙app与单片机通信,控制小车的前进后退。在Git上找了好多源码,这看看那看看整得,好像都不是那么回事。以前也没整过android,整得我好费劲呀!还是老老实实选一份看看、好好的梳理梳理。
用android定义几个个按钮(前后左右),按下按钮则通过android蓝牙发出一个字符给单片机的蓝牙模块接收。蓝牙模块与单片机是串口通信。故比较接收的字符与定义的字符是否一致,一致则执行指定操作。
通过android蓝牙t调试宝测得CH06的的UUID为:00001101-0000-1000-8000-00805f9b34fb(同一类型设备通用)
MAC:98:DA:E0:01:0B:4E(每个蓝牙模块MAC唯一)
单片机部分代码:
- u8 reclen=0;
- if(USART2_RX_STA&0X8000) //接收到一次数据了
- {
- reclen=USART2_RX_STA&0X7FFF; //得到数据长度
- USART2_RX_BUF[reclen]=0; //加入结束符
- printf("USART2_RX_BUF:%s\n",USART2_RX_BUF);
- printf("reclen:%d\n",reclen);
- if(reclen==1||reclen==2) //控制DS1检测
- {
- if(strcmp((const char*)USART2_RX_BUF,"A")==0)
- {
- u2_printf("go forward!");
- ZYSTM32_run(80,100);
- }
- else if(strcmp((const char*)USART2_RX_BUF,"E")==0)
- {
- u2_printf("go back!");
- ZYSTM32_back(80,100);
- }
- else if(strcmp((const char*)USART2_RX_BUF,"C")==0)
- {
- u2_printf("go right!");
- ZYSTM32_Right(80,100);
- }
- else if(strcmp((const char*)USART2_RX_BUF,"G")==0)
- {
- u2_printf("go left!");
- ZYSTM32_Left(80,100);
- }
- else if(strcmp((const char*)USART2_RX_BUF,"X")==0)
- {
- u2_printf("Stop!");
- ZYSTM32_brake(100);
- }
- else if(strcmp((const char*)USART2_RX_BUF,"Y")==0)
- {
- u2_printf("Stop!");
- ZYSTM32_brake(100);
- }
- }
- USART2_RX_STA=0;
- }
- "1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- package="com.example.blueboothproject_01">
-
-
- <uses-permission android:name="android.permission.BLUETOOTH" />
- <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
-
- <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
-
-
-
- <uses-permission android:name="android.permission.BLUETOOTH"/>
-
- <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
-
- <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
-
- <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
-
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
-
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
-
- <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
- <uses-feature android:name="android.hardware.location.gps"/>
-
- <application
- android:dataExtractionRules="@xml/data_extraction_rules"
- android:fullBackupContent="@xml/backup_rules"
- android:icon="@mipmap/ic_launcher"
- android:label="@string/app_name"
- android:roundIcon="@mipmap/ic_launcher_round"
- android:supportsRtl="true"
- android:theme="@style/Theme.BlueBoothProject_01"
- tools:targetApi="31">
- <activity android:name=".MainActivity"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- intent-filter>
- activity>
- <activity android:name=".Ble_Activity" android:exported="true"/>
- <service
- android:name="com.example.blueboothproject_01.service.BluetoothleService"
- android:enabled="true">
-
- service>
- <activity android:name=".DebugActivity"
- android:resizeableActivity="true"
- />
- application>
-
- manifest>
- // 通过按键扫描蓝牙
- private Button BlueBooth_Scan_btn;
- // 获取蓝牙适配器
- private BluetoothAdapter mbluetoothAdapter;
- // 低功耗蓝牙扫描回调类
- private ScanCallback mScanCalBack;
- // 低功耗蓝牙扫描装置
- private BluetoothLeScanner mBluetoothLeScanner;
-
- // 蓝牙信号强度
- private ArrayList
rssis; - // 自定义适配器
- private LeDeviceListAdapter mleDeviceListAdpater;
- // 显示扫描到的蓝牙信息 Listview
- private ListView lv;
- // 蓝牙的扫描状态
- // 扫描中
- private boolean mScanming;
- // 扫描标记
- private boolean Scan_Flag;
- // 操作类
- private Handler mhandler;
- // 请求使能
- int REQUEST_ENABLE_BIT = 1;
- // 蓝牙扫描时间
- private static final long SCAN_PERIOD = 4000;
-
- /**
- * 一个activity启动调用的第一个函数就是onCreate,
- * 它主要做这个activity启动时一些必要的初始化工作
- * @param savedInstanceState
- */
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- // 加载activity_main.xml
- setContentView(R.layout.activity_main);
-
- // 申请位置权限
- Permissions_Init();
-
- // 控件初始化
- UI_Init();
-
- // 蓝牙初始化
- BlueTooth_Init();
-
- // 绑定蓝牙 Build.VERSION_CODES.R sdk版本
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
- // 回调函数
- setScanCallBack();
-
- // 开始扫描
- boolean Scan_Flag = true;
-
- // 创建一个自定义适配器类型对象
- mleDeviceListAdpater = new LeDeviceListAdapter();
-
- // 为listview指定适配器
- lv.setAdapter(mleDeviceListAdpater);
-
- // listview点击函数
- lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView> parent, View view, int position, long id) {
- // 获取列表中指定位置的元素
- final BluetoothMessage bluetoothMessage = mleDeviceListAdpater.getDevice(position);
- // 判空
- if(bluetoothMessage==null)
- return;
- final Intent intent = new Intent(MainActivity.this, com.example.blueboothproject_01.Ble_Activity.class);
- // 向意图添加扩展数据。名称必须包含包前缀,
- // 例如应用程序com.android.contacts将使用“com.android.contacts. showall”这样的名称。
- // 蓝牙名称
- intent.putExtra(com.example.blueboothproject_01.Ble_Activity.EXTRAS_DEVICE_NAME,
- bluetoothMessage.getName()!=null?bluetoothMessage.getName():bluetoothMessage.getDevice().getName());
- // 蓝牙地址
- intent.putExtra(com.example.blueboothproject_01.Ble_Activity.EXTRAS_DEVICE_ADDRESS,
- bluetoothMessage.getDevice().getAddress());
- // 信号强度
- intent.putExtra(com.example.blueboothproject_01.Ble_Activity.EXTRAS_DEVICE_RSSI,
- rssis.get(position).toString());
-
- if(mScanming){
- // 停止扫描
- if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.R){
- // 停止正在进行的蓝牙LE扫描
- mBluetoothLeScanner.stopScan(mScanCalBack);
- }else{
- // 停止正在进行的蓝牙LE设备扫描。
- mbluetoothAdapter.stopLeScan(mLeScanCallback);
- }
- mScanming= false;
- }
-
- try{
- // 启动booth_activity组
- startActivity(intent);
- }catch (Exception e){
- // 异常处理
- e.printStackTrace();
- }
-
- }
- });
- // 蓝牙扫描 设置一个点击事件
- BlueBooth_Scan_btn.setOnLongClickListener(new View.OnLongClickListener(){
- @Override
- public boolean onLongClick(View v) {
- startActivity(new Intent(MainActivity.this,DebugActivity.class));
- return false;
- }
- });
-
- }
- }
- private void Permissions_Init() {
- // 确定是否已授予您特定的权限。
- if (ContextCompat.checkSelfPermission(this,
- Manifest.permission_group.LOCATION)!= PackageManager.PERMISSION_GRANTED) {
- // 获取wifi连接需要定位权限,没有获取权限
- ActivityCompat.requestPermissions((Activity) this, new String[]{
- Manifest.permission.ACCESS_FINE_LOCATION,
- Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_WIFI_STATE,
- }, 1);
- }
- }
- /**
- * ********************************************************************************
- * 控件初始化
- * ********************************************************************************
- */
- private void UI_Init() {
- BlueBooth_Scan_btn = this.findViewById(R.id.scan_btn);
- BlueBooth_Scan_btn.setOnClickListener(this);
- ListView lv = this.findViewById(R.id.lv);
- mhandler = new Handler();
- }
- /**
- * ********************************************************************************
- * 蓝牙初始化
- * ********************************************************************************
- */
- private void BlueTooth_Init() {
- // 一、手机硬件是否支持蓝牙
- if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
- Toast.makeText(this, "Non-Supported BlueBooth-ble-devices", Toast.LENGTH_SHORT).show();
- finish();
- }
- // 二、从手机获取本机蓝牙适配器
- final BluetoothManager mbluetootManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
- // AnBuild.VERSION.SDK_INT是一个静态变量,代表运行该应用的手机系统的SDK版本
- // Build.VERSION_CODES.R 当前编译器的版本号
- // startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
- // 从系统服务中获取蓝牙管理器
- mbluetoothAdapter = mbluetootManager.getAdapter();
- } else {
- // 获取系统默认的蓝牙适配器
- mbluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
- }
-
- // 三、 le设备的扫描
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
- mBluetoothLeScanner = mbluetoothAdapter.getBluetoothLeScanner();
- // 打开蓝牙权限
- if (mbluetoothAdapter == null || !mbluetoothAdapter.isEnabled()) {
- Intent enableBtIntent = new Intent(
- BluetoothAdapter.ACTION_REQUEST_ENABLE);
- startActivityForResult(enableBtIntent, REQUEST_ENABLE_BIT);
-
- /* 打开蓝牙权*/
-
- // Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
- //,打开一个 Activity ,在打开的 Activity 中操作之后并获得返回结果。
- // startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
- /*
- * 1、Log.v 的调试颜色为黑色的,任何消息都会输出,这里的v代表verbose啰嗦的意思,平时使用就是Log.v("","");
- 2、Log.d的输出颜色是蓝色的,仅输出debug调试的意思,但他会输出上层的信息,过滤起来可以通过DDMS的Logcat标签来选择.
- 3、Log.i的输出为绿色,一般提示性的消息information,它不会输出Log.v和Log.d的信息,但会显示i、w和e的信息
- 4、Log.w的意思为橙色,可以看作为warning警告,一般需要我们注意优化Android代码,同时选择它后还会输出Log.e的信息。
- 5、Log.e为红色,可以想到error错误,这里仅显示红色的错误信息,这些错误就需要我们认真的分析,查看栈的信息了。*/
- // 打开蓝牙
- }
- }
- /**
- * ********************************************************************************
- * 高版本回调函数
- * ********************************************************************************
- */
- private void setScanCallBack() {
- // 蓝牙LE扫描回调。使用这些回调来报告扫描结果。
- mScanCalBack = new ScanCallback() {
- // 重写扫描结果 回调类型 扫描结果集
- public void onScanResult(int callbackType, final ScanResult result) {
- // 获取蓝牙设备
- final BluetoothDevice device = result.getDevice();
- // 获取蓝牙设备信息
- final BluetoothMessage bluetoothMessage = new BluetoothMessage(device);
- // 获取蓝牙设备信息
- if (null != device || null != result.getScanRecord()) {
- try {
- // 蓝牙设备名字非空
- if (device.getName() != null) {
- // 获取设备名称
- byte[] name = ParseLeAdvDate.adv_report_parse(
- ParseLeAdvDate.BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME,
- result.getScanRecord().getBytes());
- if (name != null)
- bluetoothMessage.setName(new String(name, "utf-8"));
- }
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- }
- }
- // 在UI线程上运行指定的操作。如果当前线程是UI线程,那么操作将立即执行。
- // 如果当前线程不是UI线程,则动作被提交到UI线程的事件队列中。
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- // 讲扫描到的信息输出到ListView的适配器 蓝牙信号强度
- mleDeviceListAdpater.addDevice(bluetoothMessage, result.getRssi());
- // 数据更新 通知附加的观察者基础数据已经被更改,任何反映数据集的视图都应该刷新自己。
- mleDeviceListAdpater.notifyDataSetChanged();
- }
- });
- }
- @Override
- public void onScanFailed(final int errorCode)
- {
- super.onScanFailed(errorCode);
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- // LENGTH_SHORT:显示短时间内的视图或文本通知。这一次可能是用户可定义的。这是默认值。
- Toast.makeText(MainActivity.this,"扫描出错:"+errorCode,Toast.LENGTH_SHORT).show();
- }
- });
- }
- };
- }
- /***************************************************************
- * 低版本蓝牙回调函数
- * ************************************************************/
- 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的适配器
- BluetoothMessage bluetoothMessage = new BluetoothMessage(device);
- mleDeviceListAdpater.addDevice(bluetoothMessage, rssi);
- mleDeviceListAdpater.notifyDataSetChanged();
- }
- });
-
- }
- };
- /********************************************************************
- * 点击事件 ble_active
- ********************************************************************/
- @Override
- public void onClick(View v) {
- if(!isOpenGPS(this)){
- AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this,AlertDialog.THEME_DEVICE_DEFAULT_LIGHT);
- builder.setTitle("提示")
- .setMessage("请前往打开手机的位置权限!")
- .setCancelable(false)
- .setPositiveButton("确定", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
- startActivityForResult(intent, 10);
- }
- }).show();
- return;
- }
-
- if (Scan_Flag){
- mleDeviceListAdpater = new LeDeviceListAdapter(); //自定义适配器
- lv.setAdapter(mleDeviceListAdpater); //设置listview为自定义适配器
- scanLeDevice(true); //点击按键开始扫描设备
- } else {
- scanLeDevice(false);
- BlueBooth_Scan_btn.setText("扫描设备");
- }
- }
- private void scanLeDevice(boolean enable) {
- //将定义的蓝牙适配器进行版本适配操作
- if (mbluetoothAdapter == null) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
- mbluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
- else {
- BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
- mbluetoothAdapter = bluetoothManager.getAdapter();
- }
- }
- //将蓝牙扫描器进行适配操作
- if (mBluetoothLeScanner == null){
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
- mBluetoothLeScanner = mbluetoothAdapter.getBluetoothLeScanner();
- }
-
-
- if (enable) {
- // 10s后停止扫描
- mhandler.postDelayed(new Runnable()
- {
- @Override
- public void run()
- {
- mScanming = false;
- Scan_Flag = true;
- BlueBooth_Scan_btn.setText("扫描设备");
- Log.i("SCAN", "stop.....................");
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
- mBluetoothLeScanner.stopScan(mScanCalBack);
- else
- mbluetoothAdapter.stopLeScan(mLeScanCallback);
- }
- }, SCAN_PERIOD);//10s后启动停止线程
-
-
- /* 开始扫描蓝牙设备,带mLeScanCallback 回调函数 */
- Log.i("SCAN", "begin.....................");
- mScanming = true;
- Scan_Flag = false;
- BlueBooth_Scan_btn.setText("停止扫描");
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
- mBluetoothLeScanner.startScan(mScanCalBack);
- else
- mbluetoothAdapter.startLeScan(mLeScanCallback);
- } else {
- Log.i("Stop", "stoping................");
- mScanming = false;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
- mBluetoothLeScanner.stopScan(mScanCalBack);
- else
- mbluetoothAdapter.stopLeScan(mLeScanCallback);
- Scan_Flag = true;
- }
- }
- private boolean isOpenGPS(final Context context) {
- LocationManager locationManager
- = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
- // GPS定位
- boolean gps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
- // 网络服务定位
- boolean network = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
- if (gps || network) {
- return true;
- } return false;
- }
- /********************************************************************
- * 自定义适配器作为Listview的适配器
- ********************************************************************/
- private class LeDeviceListAdapter extends BaseAdapter {
- /**ArrayList:
- * 实现所有可选的列表操作,并允许所有元素,包括null。除了实现List接口之外,
- *这个类还提供了一些方法来操作用于内部存储列表的数组的大小。
- *(这个类大致相当于Vector,只是它是非同步的。)
- * 泛型:
- * 标记符号ArrayL
T--java类 - * E - Element (在集合中使用,因为集合中存放的是元素)
- * T - Type(Java 类)
- * K - Key(键)
- * V - Value(值)
- * N - Number(数值类型)
- * ? - 表示不确定的 java 类型
- * */
- // 低功耗设备
- private ArrayList
mLeDevice; - // 将布局XML文件实例化为相应的View对象。
- // 它从不直接使用。相反,使用android.app.Activity.getLayoutInflater()或Context。getSystemService
- // 来检索一个标准的LayoutInflater实例,该实例已经连接到当前上下文,并为正在运行的设备正确配置
- private LayoutInflater mInflator;
-
- public LeDeviceListAdapter(){
- super();
- rssis = new ArrayList
(); - mLeDevice = new ArrayList
(); - // 快速访问此窗口从其上下文检索的LayoutInflater实例。
- //Layout: 一个用于加载布局的系统服务,就是实例化与Layout XML文件对应的View对象,不能直接使用,
- // 需要通过getLayoutInflater( )方法或getSystemService( )方法来获得与当前Context绑定的 LayoutInflater实例!
- mInflator = getLayoutInflater();
- }
-
- // 添加设备
- public void addDevice(BluetoothMessage device,int rssi) {
- // 变量已添加的设备剔除相同地址的蓝牙
- // 遍历已添加的设备 剔除相同地址的蓝牙
- for(BluetoothMessage mLeDevice:mLeDevice) {
- if(mLeDevice.getDevice().getAddress().equals(device.getDevice().getAddress())) {
- return;
- }
- }
- if(device.getName()!=null){
- // 将指定的元素追加到列表的末尾。
- mLeDevice.add(device);
- rssis.add(rssi);
- }
- }
-
- // 获取列表中元素的位置信息
- public BluetoothMessage getDevice(int position){
- // 返回位于列表中指定位置的元素
- return mLeDevice.get(position);
- }
-
- // 从列表中删除所有元素。这个调用返回后,列表将为空。
- public void Clear(){
- mLeDevice.clear();
- rssis.clear();
- }
-
- // 返回列表中元素的个数。
- @Override
- public int getCount() {
- return mLeDevice.size();
- }
-
- // 返回位于列表中指定位置的元素。
- @Override
- public Object getItem(int i) {
- return mLeDevice.get(i);
- }
-
- @Override
- public long getItemId(int i) {
- return i;
- }
-
- @Override
- public View getView(int i, View view, ViewGroup parent) {
- view = mInflator.inflate(R.layout.listtiem,null);
-
- // 初始化三个textview 显示蓝牙信息
- // 地址
- TextView deviceAddress = view.findViewById(R.id.tv_deviceAddr);
- // 设备名称
- TextView deviceName = view.findViewById(R.id.tv_deviceName);
- // 信号强度
- TextView rssi = view.findViewById(R.id.tv_rssi);
-
- BluetoothMessage bluetoothMessage = mLeDevice.get(i);
- // 返回此蓝牙设备的硬件地址。
- // 例如,“00:11:22:AA: BB: CC”。
- deviceAddress.setText(bluetoothMessage.getDevice().getAddress());
- // 如果布尔表达式的值为 true ,则返回 表达式1 的值,否则返回 表达式2 的值
- // 布尔表达式 (bluetoothMessage.getName()!=null) ? 表达式1(bluetoothMessage.getName()):表达式2(bluetoothMessage.getDevice().getName())
- deviceName.setText(bluetoothMessage.getName()!=null?bluetoothMessage.getName():bluetoothMessage.getDevice().getName());
- rssi.setText(""+rssis.get(i));
- return view;
- }
- }
- public class BluetoothMessage {
- // 蓝牙设备
- private BluetoothDevice mDevice;
- // 设备名称
- private String mName;
-
- // 蓝牙远程设备
- // 表示远程蓝牙设备。BluetoothDevice允许您创建与相应设备的连接,或者查询有关设备的信息,比如名称、地址、类和绑定状态。
- public BluetoothMessage(BluetoothDevice device){
- this.mDevice = device;
-
- }
-
- // 设置名称
- public void setName(String name){
- this.mName = name;
- }
-
- // 获取设备
- public BluetoothDevice getDevice()
- {
- return mDevice;
- }
-
- // 获取名称
- public String getName(){
- return mName;
- }
-
- }