Search code examples
androidbluetoothbluetooth-lowenergyesp32

Android BLE, scan started, finds devices but does not connect with filter (ESP32 & Samsung)


I have been working through several BLE tutorials to develop an app to connect to an ESP32, but I cannot get the code to connect to the ESP32. I am using a Samsung phone which requires a time delay, but I have tried other phones and still cannot connect the ESP32 to the mobile app.

If I run a BLE scanner app I can connect to the ESP32, so I believe the ESP32 side is okay. If we scan for devices we can see it in the bluetooth device list.

The code is setup to detect and connect, I have tried a UUID and device name filer, but it will not connect. The ScanCallback is been triggered and we get the function onBatchScanResults been called, so we can see a list of devices but it will not connect to the ESP32. I think it should connect automatically with the gatt functions.

I cannot workout why it will not auto connect to the ESP32, as it's seen the devices and the scan connect is been trigged. Any help would be highly appreciated to fix this issue, as I have ran out of idea to fix it.

package com.example.sandpit_ble002;

import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;

import android.Manifest;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.ParcelUuid;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

// Based on the following resource
// https://medium.com/@martijn.van.welie/making-android-ble-work-part-1-a736dcd53b02
// https://developer.android.com/guide/topics/connectivity/bluetooth-le#java


public class MainActivity extends AppCompatActivity
{
   UUID BLP_SERVICE_UUID = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");
   private static final int REQUEST_ENABLE_BT = 1;
   private static final int ACCESS_COARSE_LOCATION_REQUEST = 2;
   private boolean mScanning;
   private Handler handler = new Handler();


  @Override
  protected void onCreate(Bundle savedInstanceState)
  {

    BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    BluetoothLeScanner scanner = bluetoothAdapter.getBluetoothLeScanner();

    CheckPermissions();
    hasPermissions();

    if (!bluetoothAdapter.isEnabled())
    {
        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
    }

    ScanSettings scanSettings = new ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
            .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
            .setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
            .setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
            .setReportDelay(10)
            .build();
    /*
    UUID[] serviceUUIDs = new UUID[]{BLP_SERVICE_UUID};
    List<ScanFilter> filters = null;
    Log.d("Debug", "debug 005");

    if(serviceUUIDs != null)
    {
        Log.d("Debug", "debug 004");
        filters = new ArrayList<>();
        for (UUID serviceUUID : serviceUUIDs) {
            ScanFilter filter = new ScanFilter.Builder()
                    .setServiceUuid(new ParcelUuid(serviceUUID))
                    .build();

            filters.add(filter);
        }
    }
    */
    String[] names = new String[]{"ESP32 UART Test"};
    List<ScanFilter> filters = null;
    if(names != null) {
        filters = new ArrayList<>();
        for (String name : names) {
            ScanFilter filter = new ScanFilter.Builder()
                    .setDeviceName(name)
                    .build();
            filters.add(filter);
        }
    }


    if (scanner != null)
    {
        scanner.startScan(filters, scanSettings, scanCallback);
        Log.d("Debug", "scan started");
    }  else {
        Log.e("Debug", "could not get scanner object");
    }

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

private boolean hasPermissions() {
    Log.i("Debug", "Debug 020");
        if (getApplicationContext().checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            Log.i("Debug", "Debug 021");
            requestPermissions(new String[] { Manifest.permission.ACCESS_COARSE_LOCATION }, ACCESS_COARSE_LOCATION_REQUEST);
            return false;
        }
return false;
}

public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState)
{
    if (newState == BluetoothProfile.STATE_CONNECTED)
    {
        Log.i("Debug", "Debug 014");
        gatt.discoverServices();
    } else {
        Log.i("Debug", "Debug 015");
        gatt.close();
    }
}

private final ScanCallback scanCallback = new ScanCallback()
{

    @Override
    public void onScanResult(int callbackType, ScanResult result)
    {
        BluetoothDevice device = result.getDevice();
        Log.i("Debug", "fScanCallback");
        // ...do whatever you want with this found device
        Log.i("Debug", "found something 1");

        BluetoothGatt gatt = device.connectGatt(  getApplicationContext(), true, mGattCallback, BluetoothDevice.TRANSPORT_LE);
        Log.d("Debug", "Trying to create a new connection.");

    }

    public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState)
    {
        Log.i("Debug", "Debug 017");
        if (status == BluetoothGatt.GATT_SUCCESS) {
            if (newState == BluetoothProfile.STATE_CONNECTED)
            {
                // We successfully connected, proceed with service discovery
                Log.i("Debug", "Debug 013");
                gatt.discoverServices();
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED)
            {
                // We successfully disconnected on our own request
                Log.i("Debug", "Debug 012");
                gatt.close();
            } else
                {
                // We're CONNECTING or DISCONNECTING, ignore for now
                Log.i("Debug", "Debug 011");
            }
        } else {
            // An error happened...figure out what happened!
            Log.i("Debug", "Debug 010");
            gatt.close();
        }
    }

    // Implements callback methods for GATT events that the app cares about.  For example,
    // connection change and services discovered.
    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback()
    {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
        {
            String intentAction;
            if (newState == BluetoothProfile.STATE_CONNECTED)
            {

                Log.i("Debug", "Connected to GATT server.");
                // Attempts to discover services after successful connection.
            }
            else if (newState == BluetoothProfile.STATE_DISCONNECTED)
            {
                Log.i("Debug", "Debug 009");
            }
        }
    };

        @Override
    public void onBatchScanResults(List<ScanResult> results)
    {
        Log.i("Debug", "found something 2");
    }

    @Override
    public void onScanFailed(int errorCode) {
        Log.i("Debug", "found something 3");
    }
};

private boolean CheckPermissions() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
    {
        if (getApplicationContext().checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
        {
            Log.i("Debug", "Debug 001");
            requestPermissions(new String[] { Manifest.permission.ACCESS_COARSE_LOCATION }, ACCESS_COARSE_LOCATION_REQUEST);
            return false;
        }
        Log.i("Debug", "Debug 002");
    }
    return true;
   }
   }

Manifest file

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.sandpit_ble002">

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

<application

    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

Log file

 2020-10-28 20:02:58.856 3867-4291/? I/SurfaceFlinger: createSurf start. lock debugging [Surface(name=AppWindowToken{5872671 token=Token{bc8c18 ActivityRecord{8ccf3fb u0 com.sec.android.app.launcher/.activities.LauncherActivity t256}}})/@0x319a60 - animation-bounds]
 2020-10-28 20:02:58.859 3867-4291/? I/SurfaceFlinger: createSurf start. lock debugging [Surface(name=AppWindowToken{49e6a47 token=Token{9d5c186 ActivityRecord{4490261 u0 com.example.sandpit_ble002/.MainActivity t333}}})/@0x5fb6af6 - animation-leash]
 2020-10-28 20:02:58.860 3867-4291/? I/SurfaceFlinger: createSurf start. lock debugging [Surface(name=AppWindowToken{49e6a47 token=Token{9d5c186 ActivityRecord{4490261 u0 com.example.sandpit_ble002/.MainActivity t333}}})/@0x5fb6af6 - animation-bounds]
 2020-10-28 20:02:59.075 10090-10090/com.example.sandpit_ble002 I/Debug: Debug 002
 2020-10-28 20:02:59.075 10090-10090/com.example.sandpit_ble002 I/Debug: Debug 020
 2020-10-28 20:02:59.085 10090-10090/com.example.sandpit_ble002 D/Debug: scan started
 2020-10-28 20:02:59.295 3867-4291/? I/SurfaceFlinger: createSurf start. lock debugging [1aaa92c com.example.sandpit_ble002/com.example.sandpit_ble002.MainActivity]
 2020-10-28 20:02:59.322 3867-4291/? I/SurfaceFlinger: createSurf start. lock debugging [com.example.sandpit_ble002/com.example.sandpit_ble002.MainActivity$_10090]
 2020-10-28 20:02:59.382 3867-3941/? I/SurfaceFlinger: createSurf start. lock debugging [Surface(name=e0e9827 Splash Screen com.example.sandpit_ble002)/@0x3d1cb48 - animation-leash]
 2020-10-28 20:03:04.132 10090-10090/com.example.sandpit_ble002 I/Debug: found something 2
 2020-10-28 20:03:09.159 10090-10090/com.example.sandpit_ble002 I/Debug: found something 2
 2020-10-28 20:03:14.180 10090-10090/com.example.sandpit_ble002 I/Debug: found something 2

Solution

  • For example you can do something like this in your onBatchScanResults()

    @Override
            public void onBatchScanResults(List<ScanResult> results)
            {
                for (ScanResult result: results) {
    
                    BluetoothDevice myDevice = result.getDevice();
    
                    Log.i("Debug", "onBatchScanResults: " + myDevice.getName());
    
                    if(myDevice.getName().equals("ESP32 UART Test")){
                        BluetoothGatt bluetoothGatt =  myDevice.connectGatt(getApplicationContext(), true, mGattCallback, BluetoothDevice.TRANSPORT_LE);
                    }
                }
            }