1

먼저 내 앱이 BLE로 누가의 데이터를 읽을 수 없습니다. BlueNRG-MS 모듈을 사용하여 기기에 연결하고 있습니다. 최대 Marshmallow onCharacteristicChanged 메서드가 호출되었으며 데이터를받을 수 있습니다. 그러나 7.0.0 이상의 onCharacteristicChanged 메서드는 호출되지 않습니다.android nougat (7.0.0 이상)의 BLE (Bluetooth Low Energy)에서 데이터를 읽을 수 없습니다.

나는 이것을 검색하고 누군가이 코드를 추가하라고 알려줍니다.

BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_UUID); 
     if (descriptor != null) { 
      descriptor.setValue(enabled ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE); 
      mBluetoothGatt.writeDescriptor(descriptor); 
     } 

하지만 정확히 무엇을 CLIENT_UUID에 넣어야할지 모르겠습니다.

그리고 내 모든 코드입니다.

public class BleManager { 

private static final String TAG = "BleManager"; 

static final private UUID CCCD_ID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); 

public static final int STATE_ERROR = -1; 
public static final int STATE_NONE = 0;  // Initialized 
public static final int STATE_IDLE = 1;  // Not connected 
public static final int STATE_SCANNING = 2; // Scanning 
public static final int STATE_CONNECTING = 13; // Connecting 
public static final int STATE_CONNECTED = 16; // Connected 

public static final int MESSAGE_STATE_CHANGE = 1; 
public static final int MESSAGE_READ = 2; 
public static final int MESSAGE_WRITE = 3; 
public static final int MESSAGE_DEVICE_NAME = 4; 
public static final int MESSAGE_TOAST = 5; 

public static final long SCAN_PERIOD = 5*1000; 

private static Context mContext = null; 
private static BleManager mBleManager = null; 
private final Handler mHandler; 

private final BluetoothAdapter mBluetoothAdapter; 
private BluetoothAdapter.LeScanCallback mLeScanCallback = null; 

private ArrayList<BluetoothDevice> mDeviceList = new ArrayList<BluetoothDevice>(); 
private BluetoothDevice mDefaultDevice = null; 

private BluetoothGatt mBluetoothGatt = null; 

private ArrayList<BluetoothGattService> mGattServices 
     = new ArrayList<BluetoothGattService>(); 
private ArrayList<BluetoothGattCharacteristic> mGattCharacteristics 
     = new ArrayList<BluetoothGattCharacteristic>(); 
private ArrayList<BluetoothGattCharacteristic> mWritableCharacteristics 
     = new ArrayList<BluetoothGattCharacteristic>(); 
private BluetoothGattCharacteristic mDefaultChar = null; 

private int mState = -1; 

/** 
* Constructor. Prepares a new Bluetooth session. 
* @param context The UI Activity Context 
* @param h A Listener to receive messages back to the UI Activity 
*/ 
private BleManager(Context context, Handler h) { 
    mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 
    mState = STATE_NONE; 
    mHandler = h; 
    mContext = context; 

    if(mContext == null) 
     return; 
} 

public synchronized static BleManager getInstance(Context c, Handler h) { 
    if(mBleManager == null) 
     mBleManager = new BleManager(c, h); 

    return mBleManager; 
} 

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) 
public synchronized void finalize() { 
    if (mBluetoothAdapter != null) { 
     mState = STATE_IDLE; 
     mBluetoothAdapter.stopLeScan(mLeScanCallback); 
     disconnect(); 
    } 

    mDefaultDevice = null; 
    mBluetoothGatt = null; 
    mDefaultService = null; 
    mGattServices.clear(); 
    mGattCharacteristics.clear(); 
    mWritableCharacteristics.clear(); 

    if(mContext == null) 
     return; 

} 

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) 
private void stopScanning() { 
    if(mState < STATE_CONNECTING) { 
     mState = STATE_IDLE; 
     mHandler.obtainMessage(MESSAGE_STATE_CHANGE, STATE_IDLE, 0).sendToTarget(); 
    } 
    mBluetoothAdapter.stopLeScan(mLeScanCallback); 
} 

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) 
private int checkGattServices(List<BluetoothGattService> gattServices) { 
    if (mBluetoothAdapter == null || mBluetoothGatt == null) { 
     Log.d(TAG, "# BluetoothAdapter not initialized"); 
     return -1; 
    } 

    for (BluetoothGattService gattService : gattServices) { 
     Log.d(TAG, "# GATT Service: "+gattService.toString()); 
     Toast.makeText(mContext, "" + gattService.toString(), Toast.LENGTH_SHORT).show(); 

     mGattServices.add(gattService); 

     List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics(); 
     for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) { 
      Toast.makeText(mContext, "" + gattCharacteristic.toString(), Toast.LENGTH_SHORT).show(); 
      mGattCharacteristics.add(gattCharacteristic); 
      Log.d(TAG, "# GATT Char: "+gattCharacteristic.toString()); 

      boolean isWritable = isWritableCharacteristic(gattCharacteristic); 
      if(isWritable) { 
       mWritableCharacteristics.add(gattCharacteristic); 
      } 

      boolean isReadable = isReadableCharacteristic(gattCharacteristic); 
      if(isReadable) { 
       readCharacteristic(gattCharacteristic); 
      } 

      if(isNotificationCharacteristic(gattCharacteristic)) { 
       setCharacteristicNotification(gattCharacteristic, true); 
       if(isWritable && isReadable) { 
        mDefaultChar = gattCharacteristic; 
       } 
      } 
     } 
    } 

    return mWritableCharacteristics.size(); 
} 

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) 
private boolean isWritableCharacteristic(BluetoothGattCharacteristic chr) { 
    if(chr == null) return false; 

    final int charaProp = chr.getProperties(); 
    if (((charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE) | 
      (charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE)) > 0) { 
     Log.d(TAG, "# Found writable characteristic"); 
     return true; 
    } else { 
     Log.d(TAG, "# Not writable characteristic"); 
     return false; 
    } 
} 

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) 
private boolean isReadableCharacteristic(BluetoothGattCharacteristic chr) { 
    if(chr == null) return false; 

    final int charaProp = chr.getProperties(); 
    if((charaProp & BluetoothGattCharacteristic.PROPERTY_READ) > 0) { 
     Log.d(TAG, "# Found readable characteristic"); 
     return true; 
    } else { 
     Log.d(TAG, "# Not readable characteristic"); 
     return false; 
    } 
} 

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) 
private boolean isNotificationCharacteristic(BluetoothGattCharacteristic chr) { 
    if(chr == null) return false; 

    final int charaProp = chr.getProperties(); 
    if((charaProp & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) { 
     Log.d(TAG, "# Found notification characteristic"); 
     return true; 
    } else { 
     Log.d(TAG, "# Not notification characteristic"); 
     return false; 
    } 
} 

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) 
public void readCharacteristic(BluetoothGattCharacteristic characteristic) { 
    if (mBluetoothAdapter == null || mBluetoothGatt == null) { 
     Log.d(TAG, "# BluetoothAdapter not initialized"); 
     return; 
    } 
    mBluetoothGatt.readCharacteristic(characteristic); 
} 

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) 
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, 
              boolean enabled) { 
    if (mBluetoothAdapter == null || mBluetoothGatt == null) { 
     Log.d(TAG, "# BluetoothAdapter not initialized"); 
     return; 
    } 

    mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); 

    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CCCD_ID); 
    if (descriptor != null) { 
     descriptor.setValue(enabled ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE); 
     mBluetoothGatt.writeDescriptor(descriptor); 
    } 
} 


public void setScanCallback(BluetoothAdapter.LeScanCallback cb) { 
    mLeScanCallback = cb; 
} 

public int getState() { 
    return mState; 
} 

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) 
public boolean scanLeDevice(final boolean enable) { 
    boolean isScanStarted = false; 
    if (enable) { 
     if(mState == STATE_SCANNING) 
      return false; 

     if(mBluetoothAdapter.startLeScan(mLeScanCallback)) { 
      mState = STATE_SCANNING; 
      mDeviceList.clear(); 

      mHandler.postDelayed(new Runnable() { 
        @Override 
        public void run() { 
         stopScanning(); 
        } 
       }, SCAN_PERIOD); 

      mHandler.obtainMessage(MESSAGE_STATE_CHANGE, STATE_SCANNING, 0).sendToTarget(); 
      isScanStarted = true; 
     } 
    } else { 
     if(mState < STATE_CONNECTING) { 
      mState = STATE_IDLE; 
      mHandler.obtainMessage(MESSAGE_STATE_CHANGE, STATE_IDLE, 0).sendToTarget(); 
     } 
     stopScanning(); 
    } 

    return isScanStarted; 
} 

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) 
public boolean connectGatt(Context c, boolean bAutoReconnect, BluetoothDevice device) { 
    if(c == null || device == null) 
     return false; 

    mGattServices.clear(); 
    mGattCharacteristics.clear(); 
    mWritableCharacteristics.clear(); 

    mBluetoothGatt = device.connectGatt(c, bAutoReconnect, mGattCallback); 
    mDefaultDevice = device; 

    mState = STATE_CONNECTING; 
    mHandler.obtainMessage(MESSAGE_STATE_CHANGE, STATE_CONNECTING, 0).sendToTarget(); 
    return true; 
} 

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) 
public boolean connectGatt(Context c, boolean bAutoReconnect, String address) { 
    if(c == null || address == null) 
     return false; 

    if(mBluetoothGatt != null && mDefaultDevice != null 
      && address.equals(mDefaultDevice.getAddress())) { 
     if (mBluetoothGatt.connect()) { 
      mState = STATE_CONNECTING; 
      return true; 
     } 
    } 

    BluetoothDevice device = 
      BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); 
    if (device == null) { 
     Log.d(TAG, "# Device not found. Unable to connect."); 
     return false; 
    } 

    mGattServices.clear(); 
    mGattCharacteristics.clear(); 
    mWritableCharacteristics.clear(); 

    mBluetoothGatt = device.connectGatt(c, bAutoReconnect, mGattCallback); 
    mDefaultDevice = device; 

    mState = STATE_CONNECTING; 
    mHandler.obtainMessage(MESSAGE_STATE_CHANGE, STATE_CONNECTING, 0).sendToTarget(); 
    return true; 
} 

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) 
public void disconnect() { 
    if (mBluetoothAdapter == null || mBluetoothGatt == null) { 
     Log.d(TAG, "# BluetoothAdapter not initialized"); 
     return; 
    } 
    mBluetoothGatt.disconnect(); 
} 

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) 
public boolean write(BluetoothGattCharacteristic chr, byte[] data) { 
    if (mBluetoothGatt == null) { 
     Log.d(TAG, "# BluetoothGatt not initialized"); 
     return false; 
    } 

    BluetoothGattCharacteristic writableChar = null; 

    if(chr == null) { 
     if(mDefaultChar == null) { 
      for(BluetoothGattCharacteristic bgc : mWritableCharacteristics) { 
       if(isWritableCharacteristic(bgc)) { 
        writableChar = bgc; 
       } 
      } 
      if(writableChar == null) { 
       Log.d(TAG, "# Write failed - No available characteristic"); 
       return false; 
      } 
     } else { 
      if(isWritableCharacteristic(mDefaultChar)) { 
       Log.d(TAG, "# Default GattCharacteristic is PROPERY_WRITE | PROPERTY_WRITE_NO_RESPONSE"); 
       writableChar = mDefaultChar; 
      } else { 
       Log.d(TAG, "# Default GattCharacteristic is not writable"); 
       mDefaultChar = null; 
       return false; 
      } 
     } 
    } else { 
     if (isWritableCharacteristic(chr)) { 
      Log.d(TAG, "# user GattCharacteristic is PROPERY_WRITE | PROPERTY_WRITE_NO_RESPONSE"); 
      writableChar = chr; 
     } else { 
      Log.d(TAG, "# user GattCharacteristic is not writable"); 
      return false; 
     } 
    } 

    writableChar.setValue(data); 
    writableChar.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT); 
    mBluetoothGatt.writeCharacteristic(writableChar); 
    mDefaultChar = writableChar; 
    return true; 
} 

private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { 
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) 
    @Override 
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { 
     if (newState == BluetoothProfile.STATE_CONNECTED) { 
      mState = STATE_CONNECTED; 
      Log.d(TAG, "# Connected to GATT server."); 

      mHandler.obtainMessage(MESSAGE_STATE_CHANGE, STATE_CONNECTED, 0).sendToTarget(); 

      gatt.discoverServices(); 

     } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { 
      mState = STATE_IDLE; 
      Log.d(TAG, "# Disconnected from GATT server."); 
      mHandler.obtainMessage(MESSAGE_STATE_CHANGE, STATE_IDLE, 0).sendToTarget(); 
      mBluetoothGatt = null; 
      mGattServices.clear(); 
      mDefaultService = null; 
      mGattCharacteristics.clear(); 
      mWritableCharacteristics.clear(); 
      mDefaultChar = null; 
      mDefaultDevice = null; 
     } 
    } 

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) 
    @Override 
    public void onServicesDiscovered(BluetoothGatt gatt, int status) { 
     if (status == BluetoothGatt.GATT_SUCCESS) { 
      Log.d(TAG, "# New GATT service discovered."); 
      checkGattServices(gatt.getServices()); 
     } else { 
      Log.d(TAG, "# onServicesDiscovered received: " + status); 
     } 
    } 

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) 
    @Override 
    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { 
     if (status == BluetoothGatt.GATT_SUCCESS) { 
      // We've received data from remote 
      Log.d(TAG, "# Read characteristic: "+characteristic.toString()); 

      final byte[] data = characteristic.getValue(); 
      if (data != null && data.length > 0) { 
       final StringBuilder stringBuilder = new StringBuilder(data.length); 
       stringBuilder.append(data); 
       Log.d(TAG, stringBuilder.toString()); 

       mHandler.obtainMessage(MESSAGE_READ, byteArrayToHex(data)).sendToTarget(); 
      } 

      if(mDefaultChar == null && isWritableCharacteristic(characteristic)) { 
       mDefaultChar = characteristic; 
      } 
     } 
    } 

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) 
    @Override 
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { 
     // We've received data from remote 
     Log.d(TAG, "# onCharacteristicChanged: "+characteristic.toString()); 

     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)); 
      stringBuilder.append(data); 
      Log.d(TAG, stringBuilder.toString()); 

      mHandler.obtainMessage(MESSAGE_READ, byteArrayToHex(data)).sendToTarget(); 
     } 

     if(mDefaultChar == null && isWritableCharacteristic(characteristic)) { 
      mDefaultChar = characteristic; 
     } 
    } 
}; 

String byteArrayToHex(byte[] a) { 
    StringBuilder sb = new StringBuilder(); 
    for(final byte b: a) 
     sb.append(String.format("%02x", b&0xff)); 
    return sb.toString(); 
}} 

답변

0

블루투스 저에너지 특성에 대한 알림을 사용하려면 아래 기능을 시도해보십시오. 7.0.0 이상의 Android에서 문제를 발견하지 못했습니다.

특성 업데이트를 구독하면 의 CharacteristicChangedCallback()에 업데이트 된 값을 가져와야합니다. 주변 기기에서 값이 실제로 업데이트되고 있는지 확인하십시오.)

private void enableNotifications(BluetoothGatt bluetoothGatt, BluetoothGattCharacteristic characteristic){ 

    UUID CLIENT_CHARACTERISTIC_CONFIG = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); 

    byte[] payload = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE; 

    bluetoothGatt.setCharacteristicNotification(characteristic, true); 

    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
      CLIENT_CHARACTERISTIC_CONFIG); 

    if (descriptor == null){ 
     Log.w(TAG, "Notification not supported for characteristic"); 
     return; 
    } 

    descriptor.setValue(payload); 

    bluetoothGatt.writeDescriptor(descriptor); 
}