ESP32S蓝牙07

发布时间:2023-07-01 19:00

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会触发读的侦听事件,并获取传入的文本内容。

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号