简体   繁体   English

将 BLE 与 Unity 连接不起作用。 (扫描有效。)

[英]connecting BLE with Unity doesn't work. (scan works.)

I have a trouble with connecting BLE devices with Unity.将 BLE 设备与 Unity 连接时遇到问题。

Actually, scanning was successful.实际上,扫描是成功的。 But connecting doesn't work at all.但是连接根本不起作用。

To use BLE in Unity, I made a plugin using Android Studio.为了在 Unity 中使用 BLE,我使用 Android Studio 制作了一个插件。

I don't know why device.connectGatt(current_activity, false, gattClientCallback);我不知道为什么 device.connectGatt(current_activity, false, gattClientCallback); doesn't make my BLE peripheral device(Arduino Nano 33 BLE) connects even though it returns BluetoothGATT correctly.即使它正确返回 BluetoothGATT 也不会使我的 BLE 外围设备(Arduino Nano 33 BLE)连接。 The text "set ble_gatt: true" shows when I called BLE.bleControllerObj.Call("connect", address);当我调用 BLE.bleControllerObj.Call("connect", address); 时显示文本“set ble_gatt: true”; in Unity.在统一。 (This calls connect function of plugin/) However, the connection doesn't happen, ie the callback onConnectionStateChange isn't called at all. (这调用了plugin/的connect function) 但是,连接没有发生,即根本没有调用回调onConnectionStateChange。 (I know because "Connection success." doesn't appear.) The strange thing is that after device,connectGatt(current_activity, false; gattClientCallback), executed, then the ble peripheral device stops advertising without connecting. (我知道是因为“连接成功。”没有出现。)奇怪的是,在设备,connectGatt(current_activity,false; gattClientCallback)执行之后,ble外围设备停止广告而不连接。 so I can't scan the ble device by other apps before terminating Unity app.所以在终止 Unity 应用程序之前,我无法通过其他应用程序扫描 ble 设备。 I guess connecting request is sent to the ble peripheral device.我猜连接请求被发送到 ble 外围设备。

(Here, BLE is the name of Unity script, and bleControllerObj is an AndroidJavaObject instance in Unity.) (这里,BLE 是 Unity 脚本的名称,bleControllerObj 是 Unity 中的 AndroidJavaObject 实例。)

Also, I checked my device and peripheral ble device can be connected with existing nRF Connect app.此外,我检查了我的设备和外围设备可以与现有的 nRF Connect 应用程序连接。

Please let me know what I'm missing.请让我知道我错过了什么。 Or should I upload my Unity script too?还是我也应该上传我的 Unity 脚本?

Here is a full code of java plugin I used.这是我使用的 java 插件的完整代码。

package com.example.ble;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.util.Log;
import android.widget.Toast;


public class BLEcontroller {

    public static Activity current_activity;
    public static Context context;
    public static BLEcontroller instance;

    public BluetoothAdapter ble_adapter;
    public BluetoothLeScanner leScanner;
    public List<BluetoothDevice> scanned_devices = new ArrayList<BluetoothDevice>();
    public boolean connected_ = false;
    public BluetoothGatt ble_gatt;
    public BluetoothManager ble_manager;

    private void showText(String msg) {
        Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
    }


    public static BLEcontroller getInstance() {
        if (instance == null) {
            instance = new BLEcontroller();
            current_activity = UnityPlayer.currentActivity;
            context = UnityPlayer.currentActivity.getApplicationContext();
        }

        instance.scanned_devices= new ArrayList<BluetoothDevice>();

        return instance;
    }

    public void setContext(Activity _activity, Context _context){
        current_activity=_activity;
        context=_context;
    }

    public void init() {
        Log.e("Unity", "init start!!!" );
        ble_manager = (BluetoothManager) current_activity.getSystemService(Context.BLUETOOTH_SERVICE);
        ble_adapter = ble_manager.getAdapter();

        Toast.makeText(context, "init start and scanner : "+(leScanner!=null), Toast.LENGTH_SHORT).show();
        Log.e("Unity", "init start and scanner : "+(leScanner!=null) );
        checkBLEAvailable();

        leScanner = ble_adapter.getBluetoothLeScanner();

        if (!current_activity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            Toast.makeText(context, "BlE isn't supported.", Toast.LENGTH_SHORT).show();
            return;
        }

        if (leScanner == null) {
            Toast.makeText(context, "scanner is null", Toast.LENGTH_SHORT).show();
        }
    }




    private void checkBLEAvailable() {

        if (ble_adapter == null || !ble_adapter.isEnabled()) {
            showText("ble is disabled");
            requestEnableBle();
        }

        requestPermissions();
    }

    @SuppressLint("MissingPermission")
    public void startScan() {
        showText("There were " + scanned_devices.size() + " devices");
        scanned_devices.clear();
        Log.e("Unity", "clear devices");
        if (leScanner == null) {
            showText("leScanner was null!");
            Log.e("Unity", "leScanner is null!");
        }
        Log.e("Unity", "startScan in java.");
        leScanner.startScan(mScanCallback);
        showText("start scan");

    }


    public int getScanned_devicesNum(){
        return scanned_devices.size();
    }

    public boolean isConnected(){
        return connected_;
    }

    public BluetoothDevice getScanned_device(int i){
        return scanned_devices.get(i);
    }


    @SuppressLint("MissingPermission")
    public void stopScan(){
        leScanner.stopScan(mScanCallback);
        showText("scan stop.");
    }

    private ScanCallback mScanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            processResult(result);
        }

        @Override
        public void onBatchScanResults(List<ScanResult> results) {
            for (ScanResult result : results) {
                processResult(result);
            }
        }

        @Override
        public void onScanFailed(int errorCode) {
            showText("scanFailed with "+errorCode);
        }

        @SuppressLint("MissingPermission")
        private void processResult(final ScanResult result) {
            boolean add_result=true;
            if(result.getDevice().getName()==null) {
                Log.e("Unity", "No device name");
                add_result = false;
            }
            for (BluetoothDevice device : scanned_devices){
                if (device.getAddress()==result.getDevice().getAddress()){
                    Log.e("Unity", "Already exists : " +device.getName());
                    add_result=false;
                }
            }
            if (add_result){
                scanned_devices.add(result.getDevice());
            }
            tidyUpScanned_devices();
        }
    };

    public void tidyUpScanned_devices(){
        ArrayList<BluetoothDevice> arrayList=new ArrayList<>();

        for (int i=0; i<scanned_devices.size(); i++){
            if (getDeviceIndex(arrayList, scanned_devices.get(i))==-1){
                arrayList.add(scanned_devices.get(i));
            }
        }
        scanned_devices.clear();
        scanned_devices=arrayList;
    }

    private int getDeviceIndex(List<BluetoothDevice> list, BluetoothDevice device){
        for (int i=0; i<list.size(); i++){
            if (device.getAddress().equalsIgnoreCase(list.get(i).getAddress())){
                return i;
            }
        }
        return -1;
    }

    @SuppressLint("MissingPermission")
    public void connect(String address) {
        if (!ble_adapter.isEnabled()) {
            showText("Turn on BLE");
            return;
        }

        for (BluetoothDevice device : scanned_devices) {
            if (device.getAddress().equals(address)) {
                showText(("try connecting device : " + address));
                disconnectGattServer();
                ble_gatt = device.connectGatt(current_activity, false, gattClientCallback);
                showText("set ble_gatt : " + (ble_gatt != null));
            }
        }
    }

    @SuppressLint("MissingPermission")
    public void connect() {
        if (!ble_adapter.isEnabled()) {
            showText("Turn on BLE");
            return;
        }

        for (BluetoothDevice device : scanned_devices) {
            showText(("try connecting device : " + device.getAddress()));
            disconnectGattServer();
            ble_gatt = device.connectGatt(current_activity, false, gattClientCallback);
            showText("set ble_gatt : " + (ble_gatt != null));
            return;
        }
    }

    @SuppressLint("MissingPermission")
    public void disconnectGattServer() {
        /*showText("Closing Gatt connection");
        // reset the connection flag
        connected_ = false;
        // disconnect and close the gatt
        if (ble_gatt != null) {
            ble_gatt.disconnect();
            ble_gatt.close();
        }*/
    }

    @SuppressLint("MissingPermission")
    // Gatt Client Callback class
    private BluetoothGattCallback gattClientCallback=new BluetoothGattCallback() {

        @Override
        public void onConnectionStateChange(BluetoothGatt _gatt, int _status, int _new_state) {
            super.onConnectionStateChange(_gatt, _status, _new_state);
            showText("Connection Success!");
            if (_status != BluetoothGatt.GATT_SUCCESS) {
                showText("failed connecting");
                disconnectGattServer();
                return;
            }
            if (_new_state == BluetoothProfile.STATE_CONNECTED) {
                // set the connection flag
                connected_ = true;
                showText("Connected to the GATT server");
                _gatt.discoverServices();
            } else if (_new_state == BluetoothProfile.STATE_DISCONNECTED) {
                disconnectGattServer();
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt _gatt, int _status) {
            super.onServicesDiscovered(_gatt, _status);
            // check if the discovery failed
            if (_status != BluetoothGatt.GATT_SUCCESS) {
                showText("Device service discovery failed, status: " + _status);
                return;
            }
            showText("service discovered : "+_gatt.getServices().get(0).getUuid());
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt _gatt, BluetoothGattCharacteristic _characteristic) {
            super.onCharacteristicChanged(_gatt, _characteristic);

            showText( "characteristic changed: " + _characteristic.getValue().toString());

        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt _gatt, BluetoothGattCharacteristic _characteristic, int _status) {
            super.onCharacteristicWrite(_gatt, _characteristic, _status);
            if (_status == BluetoothGatt.GATT_SUCCESS) {
                showText("Characteristic written successfully");
            } else {
                showText("Characteristic write unsuccessful, status: " + _status);
                disconnectGattServer();
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicRead(gatt, characteristic, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                showText("Characteristic read successfully");
            } else {
                showText("Characteristic read unsuccessful, status: " + status);
            }
        }

        // Log the value of the characteristic
        // @param characteristic
        private void readCharacteristic(BluetoothGattCharacteristic _characteristic) {
            byte[] msg = _characteristic.getValue();
            showText( "read: " + msg.toString());
        }
    };


    @SuppressLint("MissingPermission")
    private void requestEnableBle() {
        Intent ble_enable_intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        current_activity.startActivityForResult(ble_enable_intent, 1);

    }

    private void requestPermissions(){
        current_activity.requestPermissions(
                new String[] {Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.BLUETOOTH_SCAN,
                Manifest.permission.BLUETOOTH_CONNECT}, 2);
    }

}

I found the solution!我找到了解决方案!

The problem was that I tried to use Toast method which should be used in UI thread, but the callback is actually called in binder thread, so the thread was terminated before starting discoverServices.问题是我尝试使用应该在 UI 线程中使用的 Toast 方法,但实际上回调是在 binder 线程中调用的,因此在启动 discoverServices 之前线程已终止。

Also, be sure that connectGatt, disconnect, close should be run in the main thread, maybe in some devices, so using new Handler(Looper.getMainLooper()).post is recommended.另外,请确保 connectGatt、disconnect、close 应该在主线程中运行,可能在某些设备中,因此建议使用 new Handler(Looper.getMainLooper()).post。

ConnectGatt must be called in ui thread on some kind of phones。In Android-BLE-Library ,BleManagerHandler::enqueue can be called in worker thread, BluetoothGattCallback::onConnectionStateChange is also called in binder thread. ConnectGatt 必须在某些手机的 ui 线程中调用。在 Android-BLE-Library 中,BleManagerHandler::enqueue 可以在 worker 线程中调用,BluetoothGattCallback::onConnectionStateChange 也可以在 binder 线程中调用。 so this two condition could cause this issue!所以这两个条件可能会导致这个问题!

( https://github.com/NordicSemiconductor/Android-BLE-Library/issues/159 ) https://github.com/NordicSemiconductor/Android-BLE-Library/issues/159

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 就地交换逻辑在快速排序中不起作用,但使用临时变量进行交换有效。 为什么? - Inplace swap logic doesn't work in Quick sort but swapping using temp variable works. Why? 该应用程序不起作用。 它适用于 Andorid 5.1,但不适用于 android 10.0 - The application doesn't work. It works with Andorid 5.1 but it doesn't with android 10.0 Pojo 验证有效。 Poko (kotlin) 没有。 为什么? - Pojo validation works. Poko (kotlin) doesn't. Why? 多线程不起作用。 什么不对? - Multithreading doesn't work. What is incorrect? 开关盒不起作用。 爪哇 - Switch case doesn't work. Java 无需连接即可扫描设备的 Ble Scan 服务 - Ble Scan service of a device without connecting to it Spring安全过滤器不起作用。 春虫? - Spring security filter doesn't work. Spring bug? 拆分字符串不起作用。 (未使用的局部变量的值) - Splitting String doesn't work. (value of local variable not used) Android Button ArrayList onClickListener不起作用。 为什么? - Android Button ArrayList onClickListener doesn't work. Why? 基本的EJB3项目无法正常工作。 NameNotFound异常 - Basic EJB3 project doesn't work. NameNotFound Exception
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM