ESP32S蓝牙07

ESP32S蓝牙07

继续ESP32S的BLE蓝牙学习。在上一篇中,我们完成了APP对BLE蓝牙设备的搜索,今天我们继续完成手机APP作为客户端时,怎样连接和读写。

我们还是以之前的手机APP控制BLE蓝牙小车为例子,ESP32S运行的是BLE蓝牙服务器的程序,对外广播服务消息,并开放了一个读和一个写的通道。  手机APP运行的是客户端程序,负责搜索蓝牙设备、连接设备、以及向开发板发送控制指令。  开发板在接收连接后,当接收到来自APP的消息时,把消息返回给APP。

ESP32S蓝牙07_第1张图片

我们还是先上源码吧,里面有注释。

这个是主程序  MainActivity. java

package com.example.bluetooth;

import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

import android.os.Handler;

import android.os.Message;

import android.annotation.SuppressLint;

import android.bluetooth.BluetoothAdapter;

import android.bluetooth.BluetoothDevice;

import android.bluetooth.BluetoothGatt;

import android.bluetooth.BluetoothGattCallback;

import android.bluetooth.BluetoothGattCharacteristic;

import android.bluetooth.BluetoothGattDescriptor;

import android.bluetooth.BluetoothGattService;

import android.bluetooth.BluetoothManager;

import android.bluetooth.BluetoothProfile;

import android.bluetooth.le.BluetoothLeScanner;

import android.bluetooth.le.ScanCallback;

import android.content.Context;

import android.content.Intent;

import android.os.Build;

import android.view.MotionEvent;

import android.view.View;

import android.widget.AdapterView;

import android.widget.ArrayAdapter;

import android.widget.Button;

import android.widget.EditText;

import android.widget.ListView;

import android.widget.TextView;

import java.util.ArrayList;

import java.util.List;

import java.util.UUID;

@SuppressLint("NewApi")

public class MainActivity extends AppCompatActivity {

    //public final static UUID CLIENT_CHARACTERISTIC_CONFIG = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");  //通用蓝牙的标志

    private UUID mServiceUUID = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");

    private UUID mReadUUID = UUID.fromString("6E400003-B5A3-F393-E0A9-E50E24DCCA9E");

    private UUID mWriteUUID = UUID.fromString("6E400002-B5A3-F393-E0A9-E50E24DCCA9E");

   

    private Button scan_button, send_button, discon_button;

    private TextView msgstr;

    private EditText msgstr02;

    private BluetoothAdapter bleadapter;

    private BluetoothGatt bluetoothGatt;

    private BluetoothGattService bluetoothGattServices;

    private BluetoothGattCharacteristic character_read, character_write, character_notify;

    private BluetoothDevice bledevice;

    private List mDeviceList;

    private ArrayList list;

    private ArrayAdapter adapter;

    private ListView mListView;

    private int inval=0;

    private boolean connected = false;

   

    private Handler handler = new Handler() {

    @Override

    public void handleMessage(Message msg) {

        super.handleMessage(msg);

                msgstr02.setText(msgstr02.getText().toString() + "in: " + (String) msg.obj);

    }

    };

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

     

        msgstr = (TextView) findViewById(R.id.txt_msg);

        msgstr02 = (EditText) findViewById(R.id.txt_msg02);

        scan_button = (Button) findViewById(R.id.btn_scan);

        send_button = (Button) findViewById(R.id.btn_send);

        discon_button = (Button) findViewById(R.id.btn_discon);

        mListView = (ListView) findViewById(R.id.listView1);

        msgstr02.setFocusable(false);

       

        //初始化ble设配器

        BluetoothManager manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);

        bleadapter = manager.getAdapter();

        //判断蓝牙是否开启,若是关闭则请求打开蓝牙

        if (bleadapter == null || !bleadapter.isEnabled()) {

            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);

            startActivityForResult(intent, 1);

        }

       

        //搜索按钮

        scan_button.setOnClickListener(new View.OnClickListener() {

        @Override

            public void onClick(View v) {

             msgstr.setText("扫描中,请稍候 ...");

             //初始化列表变量,一个用于存储自定义类,一个用于存储字符串

             mDeviceList=new ArrayList();

             list = new ArrayList();

             //把扫描过程放进一个线程里面进行

             new Thread(new Runnable() {

                    @Override

                    public void run() {

                       //如果发现一个BLE设备,就会执行一次callback回调函数

                       bleadapter.startLeScan(callback);

                        try {

                            Thread.sleep(2000);

                        } catch (InterruptedException e) {

                            e.printStackTrace();

                        }

                        bleadapter.stopLeScan(callback);

                        msgstr.setText("");

                     

                    }

                }).start();               

        }

        });

       

        //发送消息按钮

        send_button.setOnClickListener(new View.OnClickListener() {

        @Override

            public void onClick(View v) {

             if(connected == true) {

                 inval += 1;

                 String str = "hello" + inval + "\n\r";

                 character_write.setValue(str);

                 bluetoothGatt.writeCharacteristic(character_write);

                 msgstr02.setText(msgstr02.getText().toString() + "ou: " + str);

             }

        }

        });

       

        //断开连接按钮

        discon_button.setOnClickListener(new View.OnClickListener() {

        @Override

            public void onClick(View v) {

             //断开连接

             if(connected == true) {

                 bluetoothGatt.disconnect();

                 connected = false;

             }

        }

        });

       

        //列表框侦听,当用户点击选择蓝牙时,连接蓝牙

        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

            @Override

            public void onItemClick(AdapterView parent, View view, int position, long id) {

            //获取用户选中的蓝牙设备

            bledevice = mDeviceList.get(position);

            msgstr.setText("connect: " + bledevice.getAddress());

            //连接设备的方法,返回值为bluetoothgatt类型

            //根据手机的版本,版本较高 或较低的时候

            //回调函数gattcallback,管理蓝牙的连接、获取服务、读写消息

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)

                 bluetoothGatt = bledevice.connectGatt(MainActivity.this, false, gattcallback, BluetoothDevice.TRANSPORT_LE);

            else

                bluetoothGatt = bledevice.connectGatt(MainActivity.this, false, gattcallback);

            }

          });

    }

   

  //这个是蓝牙扫描的回调函数,每当扫描到一个BLE设备时,就会运行一次这个函数

    public BluetoothAdapter.LeScanCallback callback = new BluetoothAdapter.LeScanCallback() {

    @Override

        public void onLeScan(final BluetoothDevice bluetoothDevice, int i, byte[] bytes) {

        

         if (bluetoothDevice != null){

             //这里给大家另外两种显示扫描结果的方法,可以用消息框或标签来显示

             //Toast.makeText(MainActivity.this, bluetoothDevice.getName(), Toast.LENGTH_SHORT).show();

             //showresult.append(bleDevice.getName() + "   " + bleDevice.getMac() + "\n");

                if(!mDeviceList.contains(bluetoothDevice)) {

                  mDeviceList.add(bluetoothDevice);

                  //list是存储字符串的集合,adapter是连接字符串到列表框的工具

                  list.add(bluetoothDevice.getName() + "   " + bluetoothDevice.getAddress());

                  adapter = new ArrayAdapter(MainActivity.this, android.R.layout.simple_list_item_1, list);

                  mListView.setAdapter(adapter);

                }

         }

        }

    };

   

    //这个是蓝牙管理的回调函数,管理BLE的连接、获取服务、读写消息

    private BluetoothGattCallback gattcallback = new BluetoothGattCallback() {

   

    //连接状态,当APPBLE连接成功、或者连接断开时,都会触发这个事件

        @Override

        public void onConnectionStateChange(BluetoothGatt gatt, int status, final int newState) {

            super.onConnectionStateChange(gatt, status, newState);

            runOnUiThread(new Runnable() {

                @Override

                public void run() { 

                    switch (newState) {

                        //已经连接

                        case BluetoothProfile.STATE_CONNECTED:

                            msgstr.setText("已连接" + "\n");

                            //当连接成功,就获取BLE的服务,并触发获取服务的事件

                            bluetoothGatt.discoverServices();

                            break;

                        //正在连接

                        case BluetoothProfile.STATE_CONNECTING:

                           msgstr.setText("正在连接" + "\n");

                            break;

                        //连接断开

                        case BluetoothProfile.STATE_DISCONNECTED:

                           msgstr.setText("已断开" + "\n");

                           bluetoothGatt.close();

                           msgstr02.setText("");

                            break;

                        //正在断开

                        case BluetoothProfile.STATE_DISCONNECTING:

                           msgstr.setText("断开中" + "\n");

                            break;

                    }

                }

            });

        }

        //这个是获取BLE服务的事件,如果获取成功

        @Override

        public void onServicesDiscovered(BluetoothGatt gatt, int status) {

            super.onServicesDiscovered(gatt, status);

            

            if (status == BluetoothGatt.GATT_SUCCESS) {

              BluetoothGattService gattService = bluetoothGatt.getService(mServiceUUID);

                //获取指定的UUID服务不为空时

                if(gattService != null){

                  //获取指定的UUID读写通道

                  bluetoothGattServices = gattService;

                  character_read = gattService.getCharacteristic(mReadUUID);

                  character_write = gattService.getCharacteristic(mWriteUUID);

                  //把读取通道设置为可侦听、可读取状态

                  if (character_read != null)

                       setCharacteristicNotification(character_read, true);

                    connected = true;

                  msgstr.setText("获取服务成功");

                }else{

                  msgstr.setText("获取服务失败");

                }

            }

           

        }

        @Override

        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {

            super.onCharacteristicRead(gatt, characteristic, status);

           

        }

        @Override

        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {

            super.onCharacteristicWrite(gatt, characteristic, status);

        }

       

        //这个是侦听事件,当有数据从BLE设备传入APP的时候,就会引发这个事件

        @Override

        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {

            super.onCharacteristicChanged(gatt, characteristic);

            if(characteristic == character_read) {

              Message mesg = new Message();

                mesg.what = 1;

                mesg.obj = new String(characteristic.getValue()) ;

                MainActivity.this.handler.sendMessage(mesg);

            }

           

        }

        @Override

        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {

            super.onDescriptorRead(gatt, descriptor, status);

        }

        @Override

        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {

            super.onDescriptorWrite(gatt, descriptor, status);

        }

        @Override

        public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {

            super.onReliableWriteCompleted(gatt, status);

        }

        @Override

        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {

            super.onReadRemoteRssi(gatt, rssi, status);

        }

        @Override

        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {

            super.onMtuChanged(gatt, mtu, status);

        }

    } ;

   

    //这个是把某个通道设置为可侦听状态

    public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {

    bluetoothGatt.setCharacteristicNotification(characteristic, enabled);

        boolean isEnableNotificationbluetoothGatt.setCharacteristicNotification(characteristic, enabled);

        if(isEnableNotification) {

        //一个读写通道里面,可能一次就传递多个类型的数值,每个类型数字都要设置侦听属性

            List descriptorList = characteristic.getDescriptors();

            if(descriptorList != null && descriptorList.size() > 0) {

                for(BluetoothGattDescriptor descriptor : descriptorList) {

                    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);

                    bluetoothGatt.writeDescriptor(descriptor);

                }

            }

        }

    }

   

}

   

   

这个是界面设计   activity_main. xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

   xmlns:tools="http://schemas.android.com/tools"

   android:layout_width="match_parent"

   android:layout_height="match_parent"

   android:orientation="vertical"

   android:paddingBottom="@dimen/activity_vertical_margin"

   android:paddingLeft="@dimen/activity_horizontal_margin"

   android:paddingRight="@dimen/activity_horizontal_margin"

   android:paddingTop="@dimen/activity_vertical_margin"

   tools:context=".MainActivity" >

   <ListView

      android:id="@+id/listView1"

      android:layout_width="wrap_content"

      android:layout_height="wrap_content"

      android:visibility="visible" />

   

  

   <Button

      android:id="@+id/btn_scan"

      android:layout_width="wrap_content"

      android:layout_height="wrap_content"

      android:text="扫描蓝牙" />

  

   <Button

      android:id="@+id/btn_send"

      android:layout_width="wrap_content"

      android:layout_height="wrap_content"

      android:text="发送消息" />

   <Button

      android:id="@+id/btn_discon"

      android:layout_width="wrap_content"

      android:layout_height="wrap_content"

      android:text="断开蓝牙" />

  

   <TextView

      android:id="@+id/txt_msg"

      android:layout_width="match_parent"

      android:layout_height="wrap_content" />

  

   <EditText

      android:id="@+id/txt_msg02"

      android:minLines="12"

      android:gravity="top"

      android:layout_width="match_parent"

      android:layout_height="match_parent" />

LinearLayout>

这个是安卓的版本号以及权限申请    在AndroidManifest. xml

<uses-sdk

        android:minSdkVersion="11"

        android:targetSdkVersion="21" />

   

    

    <uses-permission android:name="android.permission.BLUETOOTH" />

    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

    <uses-feature android:name="android.hardware.location.gps" />

   

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

    <uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/>

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

整体回顾一下:BLE蓝牙的控制主要还是“线程 + 回调”,当用户点击扫描按钮时,程序会启动一个扫描回调函数,每搜索到一个BLE蓝牙设备时,就触发运行一次回调函数,在回调函数中,我们把搜索到的设备添加到列表框中。

当用户选择列表框中的某一个BLE蓝牙设备时,程序会连接这个BLE蓝牙设备,并不断地触发BLE的管理回调函数。这个管理回调函数会被不断地触发运行:当连接成功、或者连接断开时,会触发里面的连接侦听事件;当获取到(读取)BLE蓝牙设备的广播服务消息时,会触发获取服务侦听事件;当用户进行写操作时,会触发写的事件(在这个程序中没有关于写侦听的代码);当有消息从BLE服务器(在这个例子中,指的是ESP32S开发板)传入时,APP会触发读的侦听事件,并获取传入的文本内容。

你可能感兴趣的:(android)