title: Sample loss in Android app when retrieving data via BLE
body: <p>I'm developing an Android app which retrieves data via BLE from a device in real-time. </p>
  
  <p>As stated in the device's datasheet, BLE uses a 20-ms connection interval. Twenty user-data bytes (which is equal to 2- samples for each channel and 2-bytes running counter) are sent in GATT notifications. Data from the device is ping-pong buffered and up to six BLE-notification packets are sent every 14 ms based on an OSAL timer. The sample rate is set as 160 samples/sec. Each sample is 3 bytes and is sending 3 channels.</p>
  
  <p>Each notification packet consists of 20 bytes containing the following:</p>
  
  <p>Measurement Sample1 (Raw ADC data)</p>
  
  <ul>
  <li>Channel1 (3 bytes)</li>
  <li>Channel2 (3 bytes)</li>
  <li>Channel3 (3 bytes)</li>
  </ul>
  
  <p>Measurement Sample 2 (Raw ADC data)</p>
  
  <ul>
  <li>Channel1 (3 bytes)</li>
  <li>Channel2 (3 bytes)</li>
  <li>Channel3 (3 bytes)</li>
  </ul>
  
  <p>Afterwards I plot this data, but it looks like that I am only getting a sample rate around 105, while there should be 160 samples/sec. It just looks like some samples are missing looking from the signal.</p>
  
  <p>I'm posting a code that I use below. I was wondering what could be the cause for that, is there a bug or a design flaw in the code? Are there any alternative methods to retrieve the data?</p>
  
  <pre><code>// This is used to register and get callback methods from Bluetooth adapter
   private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
          @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.");
                  Log.i(TAG, "Attempting to start service discovery:" +
                          mBluetoothGatt.discoverServices());
  
              } 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);
              }
          }
  
          //This method gets triggered auto whenever characteristics  is read by BT
          @Override
          public void onCharacteristicRead(BluetoothGatt gatt,
                                           BluetoothGattCharacteristic characteristic,
                                           int status) {
              if (status == BluetoothGatt.GATT_SUCCESS) {
  
              //on successful read of value, call update function.
                  broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
                  Log.d("SOURCE", "charactersitics read");
              }
              Log.d("SOURCE", "error reading: " + Integer.toString(status));
          }
  
          @Override
          public void onCharacteristicChanged(BluetoothGatt gatt,
                                              BluetoothGattCharacteristic characteristic) {
              broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
          }
      };
  
      Intent Sampintent;
      private void broadcastUpdate(final String action,
                                   final BluetoothGattCharacteristic characteristic) {
  
  
          if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
  
              Sampintent = new Intent(action);
              final byte[] sigbytes = characteristic.getValue();
              Message m = Message.obtain();
              Bundle b =new Bundle();
              b.putByteArray("bytes",sigbytes);
              m.setData(b);
              mHandler.sendMessage(m);
  
          }
      }
  
      // handler object used to post process the sample read.
      private PostSampleRead mHandler = new PostSampleRead();
      public  class PostSampleRead extends Handler {
  
          @Override
          public void handleMessage(Message msg) {
              super.handleMessage(msg);
  
              try {
  
              //raw byte array read at a time by BT. rouhgly 20 bytes per packet
                  final byte[] b = msg.getData().getByteArray("bytes");
  
                  // ignoring first two bytes b[0] and b[1] , contain running counter
  
  
                  int b1 = b[2] &amp; 0xff;
                  int b2 = b[3] &amp; 0xff;
                  int b3 = b[4] &amp; 0xff;
  
  
                  if (b1 == 255 &amp;&amp; b1 == b2 &amp;&amp; b2 == b3) {
                  } else {
  
                      // First sample of ch1
                      byte[] channel1 = {b[2], b[3], b[4]};
  
                      // First sample of ch2
                      byte[] channel2 = {b[5], b[6], b[7]};
  
                      // First sample of ch3
                      byte[] channel3 = {b[8], b[9], b[10]};
  
  
                      //pack three bytes to one value
                      BigInteger ch1 = new BigInteger(channel1);
                      BigInteger ch2 = new BigInteger(channel2);
                      BigInteger ch3 = new BigInteger(channel3);
  
  
                      String c1 = ch1.toString();
                      String c2 = ch2.toString();
                      String c3 = ch3.toString();
  
                      try {
  
                      //sneding all three samples to activity via broadcast.
                          Sampintent.putExtra(EXTRA_DATA_CH1, c1);
                          Sampintent.putExtra(EXTRA_DATA_CH2, c2);
                          Sampintent.putExtra(EXTRA_DATA_CH3, c3);
                          sendBroadcast(Sampintent);
  
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
  
  
                      // Second sample of ch1
                      byte[] channel1_ = {b[11], b[12], b[13]};
  
                      // Second sample of ch2
                      byte[] channel2_ = {b[14], b[15], b[16]};
  
                      // Second sample of ch3
                      byte[] channel3_ = {b[17], b[18], b[19]};
  
  
                      packing 3bytes into one value.
                      BigInteger ch1_ = new BigInteger(channel1_);
                      BigInteger ch2_ = new BigInteger(channel2_);
                      BigInteger ch3_ = new BigInteger(channel3_);
  
                      String c1_ = ch1_.toString();
                      String c2_ = ch2_.toString();
                      String c3_ = ch3_.toString();
  
  
                      sending to activity via broadcast.
                      Sampintent.putExtra(EXTRA_DATA_CH1, c1_);
                      Sampintent.putExtra(EXTRA_DATA_CH2, c2_);
                      Sampintent.putExtra(EXTRA_DATA_CH3, c3_);
                      sendBroadcast(Sampintent);
  
                  }
  
              } catch (Exception e) {
                  e.printStackTrace();
              }
  
  
          }
  
      }
  </code></pre>