0

I am developing an android application which requires me to send data to a Bluetooth Low Energy device.After the connection event is successful and after I receive a call back message I want to change the activity and display new GUI where on switch click I want to send data to connected device. The problem is after the activity has changed my BluetoothGatt becomes null and BluetoothGattCharacteristic also becomes null and i am not able to send the data. how can I solve this issue? Below is my code main class which onResume calls the connection activity and connects to first available device and after connection is successful it receives callback message and changes the activity.

public class MainActivity extends Activity implements BluetoothLeUart.Callback {
public TextView messages;   
private BluetoothLeUart uart;

  public void writeLine(final CharSequence text) {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            messages.append(text);
            messages.append("\n");
                        }
    });
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    messages = (TextView) findViewById(R.id.messages);

    // Initialize UART.
    uart = new BluetoothLeUart(getApplicationContext());
    messages.setMovementMethod(new ScrollingMovementMethod());
}


@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

@Override
protected void onResume() {
    super.onResume();
    writeLine("Scanning for devices ...");
    uart.registerCallback(this);
    uart.connectFirstAvailable();

}

@Override
protected void onStop() {
    super.onStop();
    uart.unregisterCallback(this);
    uart.disconnect();

}

@Override
public boolean onOptionsItemSelected(MenuItem item) {

    int id = item.getItemId();
    if (id == R.id.action_settings) {
        return true;
    }
    return super.onOptionsItemSelected(item);
}

// UART Callback event handlers.



// UART Callback event handlers.
@Override
public void onConnected(BluetoothLeUart uart) {
    // Called when UART device is connected and ready to send/receive data.
    //messages.append("connected2");
    writeLine("Connected!");
    Intent intent = new Intent(MainActivity.this,SwitchClass.class);
    startActivity(intent);
}
}

This is my codes BluetoothLeUart class which does connection activity and gives callback message.

    public class BluetoothLeUart extends BluetoothGattCallback implements BluetoothAdapter.LeScanCallback {
    // UUIDs for UART service and associated characteristics.
    public static UUID UART_UUID = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");
    public static UUID TX_UUID   = UUID.fromString("6E400002-B5A3-F393-E0A9-E50E24DCCA9E");
    public static UUID RX_UUID   = UUID.fromString("6E400003-B5A3-F393-E0A9-E50E24DCCA9E");

    // UUID for the UART BTLE client characteristic which is necessary for notifications.
    public static UUID CLIENT_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");

    // UUIDs for the Device Information service and associated characeristics.
    public static UUID DIS_UUID       = UUID.fromString("0000180a-0000-1000-8000-00805f9b34fb");
    public static UUID DIS_MANUF_UUID = UUID.fromString("00002a29-0000-1000-8000-00805f9b34fb");
    public static UUID DIS_MODEL_UUID = UUID.fromString("00002a24-0000-1000-8000-00805f9b34fb");
    public static UUID DIS_HWREV_UUID = UUID.fromString("00002a26-0000-1000-8000-00805f9b34fb");
    public static UUID DIS_SWREV_UUID = UUID.fromString("00002a28-0000-1000-8000-00805f9b34fb");

    // Internal UART state.
    private Context context;
    private WeakHashMap<Callback, Object> callbacks;
    private BluetoothAdapter adapter;
    private BluetoothGatt gatt;
    private BluetoothGattCharacteristic tx;
    private BluetoothGattCharacteristic rx;
    private boolean connectFirst;
    private boolean writeInProgress; // Flag to indicate a write is currently in progress



    // Queues for characteristic read (synchronous)
    private Queue<BluetoothGattCharacteristic> readQueue;

    // Interface for a BluetoothLeUart client to be notified of UART actions.
    public interface Callback {
        public void onConnected(BluetoothLeUart uart);
        public void onConnectFailed(BluetoothLeUart uart);
        public void onDisconnected(BluetoothLeUart uart);
        public void onReceive(BluetoothLeUart uart, BluetoothGattCharacteristic rx);
        public void onDeviceFound(BluetoothDevice device);
        public void onDeviceInfoAvailable();
    }

    public BluetoothLeUart(Context context) {
        super();
        this.context = context;
        this.callbacks = new WeakHashMap<Callback, Object>();
        this.adapter = BluetoothAdapter.getDefaultAdapter();
        this.gatt = null;
        this.tx = null;
        this.rx = null;
        this.disManuf = null;
        this.disModel = null;
        this.disHWRev = null;
        this.disSWRev = null;
        this.disAvailable = false;
        this.connectFirst = false;
        this.writeInProgress = false;
        this.readQueue = new ConcurrentLinkedQueue<BluetoothGattCharacteristic>();
    }


     // Send data to connected UART device.
    public void sendbyte(byte[] data) {
        if (tx == null || data == null || data.length == 0) {
            // Do nothing if there is no connection or message to send.
            return;
        }
        // Update TX characteristic value.  Note the setValue overload that takes a byte array must be used.
        tx.setValue(data);
        writeInProgress = true; // Set the write in progress flag
        gatt.writeCharacteristic(tx);
        // ToDo: Update to include a timeout in case this goes into the weeds
        while (writeInProgress); // Wait for the flag to clear in onCharacteristicWrite

        gatt.readCharacteristic(rx);
    }

    // Send data to connected UART device.
    public void send(String data) {
        if (data != null && !data.isEmpty()) {
            sendbyte(data.getBytes(Charset.forName("UTF-8")));
        }
    }

    // Register the specified callback to receive UART callbacks.
    public void registerCallback(Callback callback) {
        callbacks.put(callback, null);
    }

    // Unregister the specified callback.
    public void unregisterCallback(Callback callback) {
        callbacks.remove(callback);
    }

    // Disconnect to a device if currently connected.
    public void disconnect() {
        if (gatt != null) {
            gatt.disconnect();
        }
        gatt = null;
        tx = null;
        rx = null;
    }

    // Stop any in progress UART device scan.
    public void stopScan() {
        if (adapter != null) {
            adapter.stopLeScan(this);
        }
    }

    // Start scanning for BLE UART devices.  Registered callback's onDeviceFound method will be called
    // when devices are found during scanning.
    public void startScan() {
        if (adapter != null) {
            adapter.startLeScan(this);
        }
    }

    // Connect to the first available UART device.
    public void connectFirstAvailable() {
        // Disconnect to any connected device.
        disconnect();
        // Stop any in progress device scan.
        stopScan();
        // Start scan and connect to first available device.
        connectFirst = true;
        startScan();
    }

    // Handlers for BluetoothGatt and LeScan events.
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        super.onConnectionStateChange(gatt, status, newState);
        if (newState == BluetoothGatt.STATE_CONNECTED) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                // Connected to device, start discovering services.
                if (!gatt.discoverServices()) {
                    // Error starting service discovery.
                    connectFailure();
                }
                else {
                    notifyOnConnected(this);
                }
            }
            else {
                // Error connecting to device.
                connectFailure();
            }
        }
        else if (newState == BluetoothGatt.STATE_DISCONNECTED) {
            // Disconnected, notify callbacks of disconnection.
            rx = null;
            tx = null;
            notifyOnDisconnected(this);
        }
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        super.onServicesDiscovered(gatt, status);
        // Notify connection failure if service discovery failed.
        if (status == BluetoothGatt.GATT_FAILURE) {
            connectFailure();
            return;
        }

        // Save reference to each UART characteristic.
        tx = gatt.getService(UART_UUID).getCharacteristic(TX_UUID);
        rx = gatt.getService(UART_UUID).getCharacteristic(RX_UUID);

        // Save reference to each DIS characteristic.
        disManuf = gatt.getService(DIS_UUID).getCharacteristic(DIS_MANUF_UUID);
        disModel = gatt.getService(DIS_UUID).getCharacteristic(DIS_MODEL_UUID);
        disHWRev = gatt.getService(DIS_UUID).getCharacteristic(DIS_HWREV_UUID);
        disSWRev = gatt.getService(DIS_UUID).getCharacteristic(DIS_SWREV_UUID);

        // Add device information characteristics to the read queue
        // These need to be queued because we have to wait for the response to the first
        // read request before a second one can be processed (which makes you wonder why they
        // implemented this with async logic to begin with???)
        readQueue.offer(disManuf);
        readQueue.offer(disModel);
        readQueue.offer(disHWRev);
        readQueue.offer(disSWRev);

        // Request a dummy read to get the device information queue going
      //  gatt.readCharacteristic(disManuf);

        // Setup notifications on RX characteristic changes (i.e. data received).
        // First call setCharacteristicNotification to enable notification.
        if (!gatt.setCharacteristicNotification(rx, true)) {
            // Stop if the characteristic notification setup failed.
            connectFailure();
            return;
        }
        // Next update the RX characteristic's client descriptor to enable notifications.
        BluetoothGattDescriptor desc = rx.getDescriptor(CLIENT_UUID);
        if (desc == null) {
            // Stop if the RX characteristic has no client descriptor.
            connectFailure();
            return;
        }
        desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        if (!gatt.writeDescriptor(desc)) {
            // Stop if the client descriptor could not be written.
            connectFailure();
            return;
        }
        // Notify of connection completion.
        notifyOnConnected(this);
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        super.onCharacteristicChanged(gatt, characteristic);
        notifyOnReceive(this, characteristic);
    }

    @Override
    public void onCharacteristicRead (BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        super.onCharacteristicRead(gatt, characteristic, status);

        if (status == BluetoothGatt.GATT_SUCCESS) {
            //Log.w("DIS", characteristic.getStringValue(0));
            // Check if there is anything left in the queue
            BluetoothGattCharacteristic nextRequest = readQueue.poll();
            if(nextRequest != null){
                // Send a read request for the next item in the queue
               // gatt.readCharacteristic(nextRequest);
            }
            else {
                // We've reached the end of the queue
                disAvailable = true;
               // notifyOnDeviceInfoAvailable();
            }
        }
        else {
            //Log.w("DIS", "Failed reading characteristic " + characteristic.getUuid().toString());
        }
    }

    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        super.onCharacteristicWrite(gatt, characteristic, status);

        if (status == BluetoothGatt.GATT_SUCCESS) {
            // Log.d(TAG,"Characteristic write successful");
        }
        writeInProgress = false;
    }

    @Override
    public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
        // Stop if the device doesn't have the UART service.
        if (!parseUUIDs(scanRecord).contains(UART_UUID)) {
            //main.writeLine("Parse UUID failed...");
            //main.messages.append("Parse UUID failed...");
            return;

        }


        // Connect to first found device if required.
        if (connectFirst) {
            // Stop scanning for devices.
            stopScan();
            // Prevent connections to future found devices.
            connectFirst = false;
            // Connect to device.
            gatt = device.connectGatt(context, true, this);
        }
    }

    // Private functions to simplify the notification of all callbacks of a certain event.
    private void notifyOnConnected(BluetoothLeUart uart) {
        for (Callback cb : callbacks.keySet()) {
            if (cb != null) {
                cb.onConnected(uart);
            }
        }
    }

    private List<UUID> parseUUIDs(final byte[] advertisedData) {
        List<UUID> uuids = new ArrayList<UUID>();

        int offset = 0;
        while (offset < (advertisedData.length - 2)) {
            int len = advertisedData[offset++];
            if (len == 0)
                break;

           // main.writeLine(advertisedData.toString());
            int type = advertisedData[offset++];
            switch (type) {
                case 0x02: // Partial list of 16-bit UUIDs
                case 0x03: // Complete list of 16-bit UUIDs
                    //main.writeLine("case 02,03...");
                   // main.messages.append("case 02,03...");
                    while (len > 1) {
                        int uuid16 = advertisedData[offset++];
                        uuid16 += (advertisedData[offset++] << 8);
                        len -= 2;
                        uuids.add(UUID.fromString(String.format("%08x-0000-1000-8000-00805f9b34fb", uuid16)));
                    }
                    break;

                case 0x06:// Partial list of 128-bit UUIDs
                case 0x07:// Complete list of 128-bit UUIDs
                    // Loop through the advertised 128-bit UUID's.
                    //main.writeLine("case 06,07...");
                   // main.messages.append("case 06,07...");
                    while (len >= 16) {
                        try {
                            // Wrap the advertised bits and order them.
                            ByteBuffer buffer = ByteBuffer.wrap(advertisedData, offset++, 16).order(ByteOrder.LITTLE_ENDIAN);
                            long mostSignificantBit = buffer.getLong();
                            long leastSignificantBit = buffer.getLong();
                            uuids.add(new UUID(leastSignificantBit,
                                    mostSignificantBit));
                        } catch (IndexOutOfBoundsException e) {
                            // Defensive programming.
                            //Log.e(LOG_TAG, e.toString());
                            continue;
                        } finally {
                            // Move the offset to read the next uuid.
                            offset += 15;
                            len -= 16;
                        }
                    }
                    break;
                default:
                    //main.writeLine("case default...");
                  //  main.messages.append("case default");
                    offset += (len - 1);
                    break;
            }
        }
        return uuids;
    }
}

Here is my class from where I send the data

public class SwitchClass extends Activity {
public TextView messages;
public Switch Switch1;
public byte[] switchData = {'U','1','1','1','0','0','2','Z'};
private BluetoothLeUart uart;   

public void writeLine(final CharSequence text) {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            messages.append(text);
            messages.append("\n");
            //messages.setText("anirudh");
        }
    });
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.switchlayout);

    Switch1 = (Switch) findViewById(R.id.switch1);
     uart = new BluetoothLeUart(getApplicationContext());

     Switch1.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView,
                                     boolean isChecked) {
            if (isChecked) {
               switchString = new String(switchData);
            writeLine(switchString);
            // send this array 8 bytes to BLE
            sendData(switchString);
            }
        }
    });

    public void sendData(String sendVal) {
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append(sendVal.toCharArray(), 0, 8);
    uart.send(stringBuilder.toString());
}
Jaiprakash Soni
  • 4,100
  • 5
  • 36
  • 67
Arun
  • 140
  • 3
  • 15

4 Answers4

0

You are creating a service from this activity and you are destroying it onStop so your service is now been disconnected , so automatically you cant get bluetoothgatt and adapter for it

I suggest you to have child fragment inside you activity so your service will stay alive and you can have as many fragment you want to display the thing !!

Hopes this will help you !!

Punit Sharma
  • 2,951
  • 1
  • 20
  • 35
  • can you please give me an example code for better understanding – Arun Jan 19 '16 at 13:32
  • check this answer and repo for more clarity http://stackoverflow.com/a/34760339/4919210 – Punit Sharma Jan 19 '16 at 15:59
  • I don't think that example solves my problem. My problem is my BluetoothGatt gatt; and BluetoothGattCharacteristic tx becomes null and I am not able to send the data. I deleted the two lines in onStop method and just printed a toast message in that place so that disconnection doesn't happen. But unfortunately even that didn't work. – Arun Jan 20 '16 at 05:47
0

you can visit https://github.com/captain-miao/bleYan, It's a simple BLE library and example.

qinmiao
  • 5,559
  • 5
  • 36
  • 39
  • in the link nothing much is there about sending the data to connected device. it explains much about scanning and connecting which i have already done successfully . my problem is my BluetoothGatt becomes null when i change the activity which i have to overcome so that i can use BluetoothGatt and BluetoothGattCharacteristic from new activity to send the data. – Arun Jan 20 '16 at 09:56
  • if want to send the data, the ble must connected.//you maybe check it. – qinmiao Jan 21 '16 at 03:37
  • yes I agree but my BluetoothGatt and BluetoothwriteCharacteristic becomes null on changing activity from MainActivity to SwitchClass which is why I can't send the data. can you give me a solution to this. What i want is connect the device from MainActivity and receive call back after connection is successful to MainActivity. After connection is successful change the activity to SwitchClass and send data to connected device from SwitchClass. – Arun Jan 21 '16 at 08:31
  • I can't explain it in English. best-practices: 1. create a service: manger ble connect(),reconnect(),gatt etc; 2. any activity can use ble by service. – qinmiao Jan 21 '16 at 10:33
  • can you please give some code so that i can understand the proper way to overcome this issue or make changes in the above code so that i can understand. – Arun Jan 21 '16 at 12:12
0

I also got exactly the same error like yours. In my case I comment out or deleted the following line under onServiceDiscover method. It works suddenly.It may not be the answer, Hope it will give you some clue to solve.

    disManuf = gatt.getService(DIS_UUID).getCharacteristic(DIS_MANUF_UUID);
    disModel = gatt.getService(DIS_UUID).getCharacteristic(DIS_MODEL_UUID);
    disHWRev = gatt.getService(DIS_UUID).getCharacteristic(DIS_HWREV_UUID);
    disSWRev = gatt.getService(DIS_UUID).getCharacteristic(DIS_SWREV_UUID);

    readQueue.offer(disManuf);
    readQueue.offer(disModel);
    readQueue.offer(disHWRev);
    readQueue.offer(disSWRev);
Kaung San
  • 79
  • 1
  • 14
0

You must set to synchronized your BluetoothGatt Object like

public void setBluetoothGatt(BluetoothGatt gatt) {
    synchronized (this) {
        this.bluetoothGatt = gatt;
    }
}

because BluetoothGatt throw DeadObject Exception when you change the Activity

TsigumEnes
  • 131
  • 1
  • 14