2017-10-30 44 views
0

BLE 장치의 온도 서비스 및 LED 서비스를 동일한 Android 인터페이스에서 관리하려고합니다. 그러나 LED를 켜려고 할 때마다 응용 프로그램이 종료되고이 문제를 해결하는 방법을 알지 못합니다. Java 또는 BLE이 좋지 않습니다.동일한 인터페이스 페이지에서 두 서비스의 두 가지 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); 
} 

}

답변

0
  1. 모든 BLE 호출은 비동기. 이것은 정말로 고통 스럽지만 우리에게는 가지고있는 것이 있습니다. 따라서 다음과 같이 쓸 수 없습니다.

    { 부울 상태 = writeCharacteristic (특성); 반송 상태; }

및 모든 괜찮을 바랍니다. 이 경우 writeCharacteristic 명령어 명령어의 결과 만 얻지 만 실제 쓰기 작업 결과는 얻지 못합니다! 마지막으로 코드의 일부를 BluetoothGattCallback 자손으로 등록하고 OnCharacteristicWrite 이벤트를 읽어야합니다. 여기에서 쓰기 작업의 결과를 실제로 알 수 있습니다.

  1. 모든 BLE 읽기 - 쓰기 작업은 엄격한 1x1 시퀀스 및 메인 앱 스레드에서만 수행해야합니다. 이럴 고글의 결정은이 방식 :

예쁜 BLE 가이드에 BLE 스택을 실현하는 것보다,이 덜 이상하다 - 당신은 다른 결과를 얻을 수 있도록

  • 다른 장치는 다른 기능과 생산성을 가지고 안드로이드는 다음에서 찾을 수 있습니다 : https://droidcon.de/sites/global.droidcon.cod.newthinking.net/files/media/documents/practical_bluetooth_le_on_android_0.pdf

  • +0

    "null 포인터 예외가 발생하는 이유는 무엇입니까?" 하지만 당신이 쓰는 것은 달리 말해서 메인 스레드에서 gatt 연산을 수행해야한다는 점을 제외하고는 대부분 정확합니다 (그러한 요구 사항은 없습니다). – Emil

    +0

    @Emil, 예, 필수 사항은 아니며 단지 133 번과 같은 추악한 문제를 방지하는 데 도움이되는 권장 사항입니다 (예 :https://stackoverflow.com/questions/38136103/android-bluetooth-error-133?noredirect=1&lq=1) – Miamy

    +0

    나는 내 코드로 무엇을 할 지 아직 확실하지 않다고 생각한다. 내 turnLEDon 및 turnLEDoff 함수는 한 번만 LED 특성을 호출 할 때 작동하지만, 하나의 단추를 사용하여 LED 특성 및 온도 특성을 모두 트리거하지는 않습니다. 내 코드는 샘플 코드 인 BluetoothLeGatt (Android Studio에서 가져옴)를 기반으로하므로 기본적으로 PDF 파일에 모든 내용이 포함되어 있습니다. 조금 더해야 할 일을 명확하게 설명해 주시겠습니까? – Natalie