繁体   English   中英

如何从BLE设备连续向Android应用读取数据?

[英]How to read data from a BLE device continuously to android app?

我正在开发一个Android应用,该应用将与与目标板(微控制器)连接的BLE设备(RN4020)进行通信。 我开发了可以通过RN4020将数据发送到目标板的应用程序,并且可以通过UART成功接收到该应用程序。但是我无法从目标设备接收数据到该应用程序。 我每秒钟从微控制器发送一次数据。 但是,通过使用从Play商店下载的MLDP应用,该应用可以同时发送和接收数据。

在调试时,它不会达到onCharacteristicRead

如何从设备到应用程序接收数据?

package com.example.designemb5.tempworking;

import android.Manifest;
    import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
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.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.ParcelUuid;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;

public class MainActivity extends AppCompatActivity {

BluetoothManager btManager;
BluetoothAdapter btAdapter;
BluetoothLeScanner btScanner;
BluetoothDevice mBluetoothDevice;
public BluetoothAdapter mBluetoothAdapter;
public BluetoothGatt mBluetoothGatt;
public BluetoothGattService mBluetoothGattService;
private int mConnectionState = STATE_DISCONNECTED;
private BluetoothGattCharacteristic mWriteCharacteristic;
private static final int STATE_DISCONNECTED = 0;
private static final int STATE_CONNECTING = 1;
private static final int STATE_CONNECTED = 2;
Button startScanningButton;
Button stopScanningButton;
Button connectButton;
Button disconnectButton;
public boolean mConnected = false;
public boolean mCharacteristics = true;
private static final String TAG = "BLUETOOTH_LE";
public static List<ParcelUuid> MY_UUID;
public final static String ACTION_GATT_CONNECTED = "com.example.designemb5.tempworking.ACTION_GATT_CONNECTED";
public final static String ACTION_GATT_DISCONNECTED = "com.example.designemb5.tempworking.ACTION_GATT_DISCONNECTED";
public final static String ACTION_GATT_SERVICES_DISCOVERED = "com.example.designemb5.tempworking.ACTION_GATT_SERVICES_DISCOVERED";
public final static String ACTION_DATA_AVAILABLE = "com.example.designemb5.tempworking.ACTION_DATA_AVAILABLE";
public final static String EXTRA_DATA = "com.example.designemb5.tempworking.EXTRA_DATA";

public final static UUID MY_UUID_RN4020_SERVICE = UUID.fromString("00035b03-58e6-07dd-021a-08123a000300");
public final static UUID MY_UUID_RN4020_CHARACTERISTIC_WRITE = UUID.fromString("00035b03-58e6-07dd-021a-08123a000301");
public final static UUID MY_UUID_RN4020_CHARACTERISTIC_READ = UUID.fromString("00035b03-58e6-07dd-021a-08123a0003ff");
TextView peripheralTextView;
private final static int REQUEST_ENABLE_BT = 1;
private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1;
public String mDeviceAddress;
public int mTestVal = 1;
public static Map<ParcelUuid, byte[]> mDeviceData;
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    connectButton = (Button) findViewById(R.id.ConnectButton);
    disconnectButton = (Button) findViewById(R.id.disonnectButton);

    peripheralTextView = (TextView) findViewById(R.id.PeripheralTextView);
    peripheralTextView.setMovementMethod(new ScrollingMovementMethod());

    startScanningButton = (Button) findViewById(R.id.StartScanButton);
    startScanningButton.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            startScanning();
        }
    });

    stopScanningButton = (Button) findViewById(R.id.StopScanButton);
    stopScanningButton.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            stopScanning();
        }
    });
    stopScanningButton.setVisibility(View.INVISIBLE);

    btManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
    btAdapter = btManager.getAdapter();
    btScanner = btAdapter.getBluetoothLeScanner();



    if (btAdapter != null && !btAdapter.isEnabled()) {
        Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableIntent,REQUEST_ENABLE_BT);
    }


    // Make sure we have access coarse location enabled, if not, prompt the user to enable it
    if (this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("This app needs location access");
        builder.setMessage("Please grant location access so this app can detect peripherals.");
        builder.setPositiveButton(android.R.string.ok, null);
        builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
            @Override
            public void onDismiss(DialogInterface dialog) {
                requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
            }
        });
        builder.show();
    }
}


public void startScanning() {
    System.out.println("start scanning");
    peripheralTextView.setText("");
    startScanningButton.setVisibility(View.INVISIBLE);
    stopScanningButton.setVisibility(View.VISIBLE);
    AsyncTask.execute(new Runnable() {
        @Override
        public void run() {
            btScanner.startScan(leScanCallback);
        }
    });
}

public void stopScanning() {
    System.out.println("stopping scanning");
    peripheralTextView.append("Stopped Scanning");
    startScanningButton.setVisibility(View.VISIBLE);
    stopScanningButton.setVisibility(View.INVISIBLE);
    AsyncTask.execute(new Runnable() {
        @Override
        public void run() {
            btScanner.stopScan(leScanCallback);
        }
    });
}

public void connect(View view) {
    connectButton.setVisibility(View.INVISIBLE);
    disconnectButton.setVisibility(View.VISIBLE);

    final BluetoothManager bluetoothManager =
            (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    mBluetoothAdapter = bluetoothManager.getAdapter();

    final BluetoothDevice device = btAdapter
            .getRemoteDevice(mDeviceAddress);
    mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
}

public void disconnect(View view) {
    connectButton.setVisibility(View.VISIBLE);
    disconnectButton.setVisibility(View.INVISIBLE);
    mBluetoothGatt.disconnect();
}

public void senddata(View view) {
    {
        int value = 0x01;
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
    /*check if the service is available on the device*/
        BluetoothGattService mCustomService = mBluetoothGatt.getService(MY_UUID_RN4020_SERVICE);
        if (mCustomService == null) {
            Log.w(TAG, "Custom BLE Service not found");
            return;
        }
    /*get the read characteristic from the service*/
        BluetoothGattCharacteristic mWriteCharacteristic = mCustomService.getCharacteristic(MY_UUID_RN4020_CHARACTERISTIC_WRITE);
        mWriteCharacteristic.setValue(value, BluetoothGattCharacteristic.FORMAT_UINT8, 0);
        if (mBluetoothGatt.writeCharacteristic(mWriteCharacteristic) == false) {
            Log.w(TAG, "Failed to write characteristic");
        }
    }
}

public void receivedata(View view) {
    {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
    /*check if the service is available on the device*/
        BluetoothGattService mCustomService = mBluetoothGatt.getService(MY_UUID_RN4020_SERVICE);
        if (mCustomService == null) {
            Log.w(TAG, "Custom BLE Service not found");
            return;
        }

        BluetoothGattCharacteristic mReadCharacteristic = mCustomService.getCharacteristic(MY_UUID_RN4020_CHARACTERISTIC_READ);
        if (mBluetoothGatt.readCharacteristic(mReadCharacteristic) == false) {
            Log.w(TAG, "Failed to read characteristic");
        }
        mBluetoothGatt.readCharacteristic(mReadCharacteristic);
    }
}

// Device scan callback.
private ScanCallback leScanCallback = new ScanCallback() {
    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        processResult(result);
    }

    private void processResult(ScanResult result){
        mBluetoothDevice = result.getDevice();
        mDeviceAddress = result.getDevice().getAddress();
        mDeviceData = result.getScanRecord().getServiceData();
        MY_UUID = result.getScanRecord().getServiceUuids();
        peripheralTextView.append("Device Name: " + mDeviceAddress + "\n");
        stopScanning();
    }
};

private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {

    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        super.onCharacteristicWrite(gatt, characteristic, status);
//            updateStatus(characteristic);
        Log.e("gatt", "writeChar");
    }

    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status,
                                        int newState) {
        String intentAction;
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            intentAction = ACTION_GATT_CONNECTED;
            mConnectionState = STATE_CONNECTED;
            broadcastUpdate(intentAction);
            Log.i(TAG, "Connected to GATT server.");
            // Attempts to discover services after successful connection.
            Log.i(TAG, "Attempting to start service discovery:"
                    + mBluetoothGatt.discoverServices());
            TimerTask task = new TimerTask() {
                @Override
                public void run() {
                    if (mBluetoothGatt != null)
                        mBluetoothGatt.readRemoteRssi();
                }
            };
            Timer mRssiTimer = new Timer();
            mRssiTimer.schedule(task, 1000, 1000);

        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            intentAction = ACTION_GATT_DISCONNECTED;
            mConnectionState = STATE_DISCONNECTED;

            Log.i(TAG, "Disconnected from GATT server.");
            broadcastUpdate(intentAction);
        }
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {

        if (status == BluetoothGatt.GATT_SUCCESS) {
            broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
        } else {
            Log.w(TAG, "onServicesDiscovered received: " + status);
        }

        BluetoothGattService mCustomService = mBluetoothGatt.getService(MY_UUID_RN4020_SERVICE);

        BluetoothGattCharacteristic mReadCharacteristic = mCustomService.getCharacteristic(MY_UUID_RN4020_CHARACTERISTIC_READ);

        gatt.readCharacteristic(mReadCharacteristic);
        gatt.setCharacteristicNotification(mReadCharacteristic, true);
    }

    @Override
    public void onCharacteristicRead(BluetoothGatt gatt,
                                     BluetoothGattCharacteristic characteristic, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            gatt.readCharacteristic(characteristic);
            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
        }
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt,
                                        BluetoothGattCharacteristic characteristic) {
        broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
    }

};

@Override
public void onRequestPermissionsResult(int requestCode,
                                       String permissions[], int[] grantResults) {
    switch (requestCode) {
        case PERMISSION_REQUEST_COARSE_LOCATION: {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                System.out.println("coarse location permission granted");
            } else {
                final AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setTitle("Functionality limited");
                builder.setMessage("Since location access has not been granted, this app will not be able to discover beacons when in the background.");
                builder.setPositiveButton(android.R.string.ok, null);
                builder.setOnDismissListener(new DialogInterface.OnDismissListener() {

                    @Override
                    public void onDismiss(DialogInterface dialog) {
                    }

                });
                builder.show();
            }
            return;
        }
    }
}

public void writeCharacteristic(BluetoothGattCharacteristic characteristic) {
    mBluetoothGatt.writeCharacteristic(characteristic);
}


private void broadcastUpdate(final String action) {
    final Intent intent = new Intent(action);
    sendBroadcast(intent);
}

private void broadcastUpdate(final String action,
                             final BluetoothGattCharacteristic characteristic) {
    final Intent intent = new Intent(action);

    // This is special handling for the Heart Rate Measurement profile. Data
    // parsing is
    // carried out as per profile specifications:
    // http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
   /* if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
        int flag = characteristic.getProperties();
        int format = -1;
        if ((flag & 0x01) != 0) {
            format = BluetoothGattCharacteristic.FORMAT_UINT16;
            Log.d(TAG, "Heart rate format UINT16.");
        } else {
            format = BluetoothGattCharacteristic.FORMAT_UINT8;
            Log.d(TAG, "Heart rate format UINT8.");
        }
        final int heartRate = characteristic.getIntValue(format, 1);
        Log.d(TAG, String.format("Received heart rate: %d", heartRate));
        intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
    } else {
        // For all other profiles, writes the data formatted in HEX.
        final byte[] data = characteristic.getValue();
        if (data != null && data.length > 0) {
            final StringBuilder stringBuilder = new StringBuilder(
                    data.length);
            for (byte byteChar : data)
                stringBuilder.append(String.format("%02X ", byteChar));
            intent.putExtra(EXTRA_DATA, new String(data) + "\n"
                    + stringBuilder.toString());
        }
    }*/


    final byte[] data = characteristic.getValue();

    Log.v(TAG, "data.length: " + data.length);

    if (data != null && data.length > 0) {
        final StringBuilder stringBuilder = new StringBuilder(data.length);
        for(byte byteChar : data) {
            stringBuilder.append(String.format("%02X ", byteChar));

            Log.v(TAG, String.format("%02X ", byteChar));
        }
        intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
    }

    sendBroadcast(intent);
}



private void showMessage(String str){
    Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
}

}

要启用通知:

 gatt.setCharacteristicNotification(yourCharacteristic, true);



 BluetoothGattDescriptor desc = yourCharacteristic.getDescriptor(UUID);

然后:

desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(desc);

之后,每次发送数据时都应调用onCharacteristicChanged()。 通过重写onDescriptorWrite()方法来确认启用通知是否成功。

暂无
暂无

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM