如何在同一个界面页面上读取两个服务的两个BLE特征?

时间:2017-10-30 13:18:00

标签: android bluetooth bluetooth-lowenergy gatt

我正在尝试在同一个Android界面上管理我的BLE设备的温度服务和LED服务。但是,每当我尝试打开LED时,应用程序就会死机,我不知道如何解决这个问题(我不擅长Java或BLE)。

我拥有我需要的所有特征和服务的UUID。我正在尝试创建两个BluetoothGattCharacteristic

private BluetoothGattCharacteristic characteristic;
private BluetoothGattCharacteristic characteristicCTRL2;

当我调用温度特性并进行温度测量时,它工作正常。但是,每当我尝试打开LED /调用LED特性时,应用程序就会死机。

10-30 08:48:33.026 1033-1033/com.example.android.bluetoothlegatt E/AndroidRuntime: FATAL EXCEPTION: main
                                                                               Process: com.example.android.bluetoothlegatt, PID: 1033
                                                                               java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.bluetooth.BluetoothGattCharacteristic.setValue(byte[])' on a null object reference
                                                                                   at com.example.android.bluetoothlegatt.AutoConnectActivity.turnLEDon(AutoConnectActivity.java:71)
                                                                                   at com.example.android.bluetoothlegatt.AutoConnectActivity$6.onClick(AutoConnectActivity.java:264)
                                                                                   at android.view.View.performClick(View.java:4756)
                                                                                   at android.view.View$PerformClick.run(View.java:19754)
                                                                                   at android.os.Handler.handleCallback(Handler.java:739)
                                                                                   at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                                   at android.os.Looper.loop(Looper.java:135)
                                                                                   at android.app.ActivityThread.main(ActivityThread.java:5219)
                                                                                   at java.lang.reflect.Method.invoke(Native Method)
                                                                                   at java.lang.reflect.Method.invoke(Method.java:372)
                                                                                   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
                                                                                   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

以下代码是我如何转移所有特征&一起服务。

 // Loops through available Characteristics.
        for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
            charas.add(gattCharacteristic);
            if ( gattCharacteristic.getUuid().equals(UUID.fromString(SampleGattAttributes.CTRL_2))){ //CTRL_2 is the UUID of my LED characteristic
                Log.e(TAG, "GattCharacteristics= " + gattCharacteristic.getUuid());
                Intent intent = new Intent(DeviceControlActivity.this, AutoConnectActivity.class);
                intent.putExtra("character_id", "F000AA01-0451-4000-B000-000000000000"); //UUID of my temperature characteristic
                intent.putExtra("service_id", "F000AA00-0451-4000-B000-000000000000");//UUID of my temperature service
                intent.putExtra("address", mDeviceAddress);
                intent.putExtra("ctrl2_character_id", gattCharacteristic.getUuid().toString()); //UUID of my LED characteristics
                intent.putExtra("ctrl2_service_id", gattCharacteristic.getService().getUuid().toString());//UUID of my LED service
                startActivity(intent);
            }  

我失败的部分是我尝试打开LED的功能。

public boolean turnLEDon()
    {

    byte[] value = {(byte)1};
    characteristicCTRL2.setValue(value);//This is the line I failed

    boolean status = mBluetoothLeService.mBluetoothGatt.writeCharacteristic(characteristicCTRL2);

    return status;
};

以下是我尝试在同一界面处理两个特征的完整代码。

package com.example.android.bluetoothlegatt;



public class AutoConnectActivity extends Activity {
private BluetoothGattCharacteristic characteristic;
private BluetoothGattCharacteristic characteristicCTRL2;
private String address;
private UUID serviceId;
private UUID charId;
private UUID ctrl2ServiceId;
private UUID ctrl2CharId;
private TextView debugText;
private Button startBtn;
private Button stopBtn;
private Button ctrlStartBtn;
private Button ctrlStopBtn;
private Timer timer = new Timer();
private boolean started = false;
private ArrayAdapter<String> adapter;
private String Entry;
private File file;
private boolean check= true;


private BluetoothLeService mBluetoothLeService;



public boolean turnLEDon()
{

    byte[] value = {(byte)1};
    characteristicCTRL2.setValue(value);//This is the line I failed

    boolean status = mBluetoothLeService.mBluetoothGatt.writeCharacteristic(characteristicCTRL2);

    return status;
};
public boolean turnLEDoff()
{

    byte[] value = {(byte)0};
    characteristicCTRL2.setValue(value);

    boolean status = mBluetoothLeService.mBluetoothGatt.writeCharacteristic(characteristicCTRL2);

    return !status;
};

private TimerTask timerTask = new TimerTask() {
    @Override
    public void run() {

        mBluetoothLeService.connect(address);
    }
};


// Code to manage Service lifecycle.
private final ServiceConnection mServiceConnection = new ServiceConnection() {

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder service) {
        mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();
        if (!mBluetoothLeService.initialize()) {
            Log.e(TAG, "Unable to initialize Bluetooth");
            finish();
        }
        mBluetoothLeService.connect(address);
        List<BluetoothGattService> list = mBluetoothLeService.getSupportedGattServices();
        for(BluetoothGattService s : list){
            if(s.getUuid().compareTo(serviceId) == 0){
                if (check==true) {
                    characteristic = s.getCharacteristic(charId);
                    mBluetoothLeService.disconnect();
                }


                return;
            }
            if(s.getUuid().compareTo(ctrl2ServiceId) == 0){

                if(check==false) {
                    characteristicCTRL2 = s.getCharacteristic(ctrl2CharId);
                }

                return;
            }
        }
        mBluetoothLeService.disconnect();
        Log.e(TAG, "not find device");
        debugText.setText("not find device");
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        mBluetoothLeService = null;
    }
};

private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        debugText.setText(action);
        if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
            // if temperature measurement
            if (check==true){
            mBluetoothLeService.readCharacteristic(characteristic);}
            // if control LED
            if(check==false){
            mBluetoothLeService.readCharacteristic(characteristicCTRL2);
                turnLEDon();

            }

        } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
        } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
        } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action))

        {
            if (check==false) {
            String CTRL_Status = intent.getStringExtra(BluetoothLeService.EXTRA_DATA);
            adapter.add("CTRL Status: " + CTRL_Status + "     " + new Date(System.currentTimeMillis()));

            turnLEDoff();
                mBluetoothLeService.disconnect();
            }
        if(check==true) {
            String temperature = intent.getStringExtra(BluetoothLeService.EXTRA_DATA);
            adapter.add("Temperature: " + temperature + "°C     " + new Date(System.currentTimeMillis()));

                Entry = address + "," + new Date(System.currentTimeMillis()).toString() + "," + temperature.toString() + "\n";

                try {
                    FileOutputStream out = new FileOutputStream(file, true);
                    out.write(Entry.getBytes());
                    out.close();
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                mBluetoothLeService.disconnect();
            }
        }
    }
};

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.autoconnect);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

    Intent intent = getIntent();
    address = intent.getStringExtra("address");
    serviceId = UUID.fromString(intent.getStringExtra("service_id"));
    charId = UUID.fromString(intent.getStringExtra("character_id"));
    ctrl2ServiceId = UUID.fromString(intent.getStringExtra("ctrl2_service_id"));
    Log.e(TAG, " CTRL2 SERVICE: " + ctrl2ServiceId);
    ctrl2CharId = UUID.fromString(intent.getStringExtra("ctrl2_character_id"));
    Log.e(TAG, " CTRL2 CHARAC: " + ctrl2CharId);
    ((TextView)findViewById(R.id.address_txt)).setText(address);
    ((TextView)findViewById(R.id.service_txt)).setText(serviceId.toString());
    ((TextView)findViewById(R.id.char_txt)).setText(charId.toString());
    debugText = (TextView)findViewById(R.id.debug_txt);
    startBtn = (Button)findViewById(R.id.start_btn);
    stopBtn = (Button)findViewById(R.id.stop_btn);
    ctrlStartBtn = (Button)findViewById(R.id.ctrl_start_btn);
    ctrlStopBtn = (Button)findViewById(R.id.ctrl_stop_btn);
    ListView listView = (ListView)findViewById(R.id.result_list);
    adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1);
    listView.setAdapter(adapter);

    startBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(started) return;
            started = true;
            check=true;

            //External Storage
            String state;
            state = Environment.getExternalStorageState();
            if (Environment.MEDIA_MOUNTED.equals(state)) {
                File root = Environment.getExternalStorageDirectory();
                File Dir = new File(root.getAbsolutePath() + "/MeasurementDataFile");
                if (!Dir.exists()) {
                    Dir.mkdir();
                }
                file = new File(Dir, "Temperature.csv");

            }
            if (check==true){
            timer.schedule(timerTask, 0, 1000 * 5); }
        }
    });

    stopBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(!started) return;
            started = false;
            check=true;

            timer.cancel();
        }
    });

    ctrlStartBtn.setOnClickListener(new View.OnClickListener()
    {

        @Override
        public void onClick(View v)
        {
            mBluetoothLeService.connect(address);
            check=false;
            if(started) return;
            started = true;



            Toast.makeText (getBaseContext(), "Turn CTRL 1 ON", Toast.LENGTH_SHORT).show();

            Log.e(TAG, " On?: " + turnLEDon());

        }
    });

    ctrlStopBtn.setOnClickListener(new View.OnClickListener() {
        //mBluetoothLeService.connect(address);
        @Override

        public void onClick(View v) {
            if(!started) return;
            started = false;
            check=false;

            Log.e(TAG, " Off?: " + turnLEDoff());
            Toast.makeText (getBaseContext(), "Turn CTRL 1 OFF", Toast.LENGTH_SHORT).show();

        }
    });

    Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
    bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
}

@Override
protected void onResume() {
    super.onResume();
    registerReceiver(mGattUpdateReceiver, DeviceControlActivity.makeGattUpdateIntentFilter());
    if (mBluetoothLeService != null) {
        final boolean result = mBluetoothLeService.connect(address);
        Log.d(TAG, "Connect request result=" + result);
    }
}

@Override
protected void onPause() {
    super.onPause();
    unregisterReceiver(mGattUpdateReceiver);
}

}

1 个答案:

答案 0 :(得分:0)

  1. 所有BLE调用都是异步的。这真的很痛苦,但我们拥有的东西。所以你不能只写:

    {     boolean status = writeCharacteristic(characteristic);     返回状态; }

  2. 希望一切顺利。在这种情况下,只能获得 writeCharacteristic 指令的结果,而不是实际的写操作结果!最后,您需要将代码的一部分注册为 BluetoothGattCallback 后代,并阅读 OnCharacteristicWrite 事件,您可以在其中真正了解写操作的结果。

    1. 所有BLE读写操作都应该在严格的逐个序列中完成,并且只能从主应用程序线程完成。

    2. 不同的设备具有不同的功能和生产力,因此您可以获得不同的结果 - 恕我直言,这不像谷歌决定以这种方式实现BLE堆栈那样奇怪:)

    3. 适用于Android的漂亮BLE指南可在此处找到:https://droidcon.de/sites/global.droidcon.cod.newthinking.net/files/media/documents/practical_bluetooth_le_on_android_0.pdf