1

I wrote an Android Phone app in Android Studio that connects to a UbloX NINA B1 to send and receive data on the SPS Service.

I'm having issues with the incoming data from the NINA B1 (Peripheral). I'm not sure if I connected correctly and turned the notifications on correctly.

Below is the code where I do the connection to the SPS Service. (Hardcoded to select Group 3 and child 0)

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
// Clicked on a Caracteristic (eg. FIFO or Credits)
public boolean enableNotification() {

    // Hardcode for SPS service on NINA B112, can only use this service, other services will not be used
    // This groupPosition and childPosition was determined from A_05 code, The selectable list gave these values when selecting SPS service
    int groupPosition = 3;
    int childPosition = 0;

    BluetoothGattCharacteristic characteristic;
    characteristic = characteristics_HashMapList.get(services_ArrayList.get(groupPosition).getUuid().toString()).get(childPosition);
    // Todo find a better way to do below two lines. This is a temp way to pass characteristic & mBTLE_Service to static variables to use in HomeFragment for sending data
    //Copy characteristics of SPS service to static act_characteristic for use by HomeFragment
    this.act_characteristic = characteristic;
    //Copy mBTLE_Service of SPS service to static act_service for use by HomeFragment
    this.act_service = mBTLE_Service;

    if (Utils.hasWriteProperty(characteristic.getProperties()) != 0) {
        String uuid = characteristic.getUuid().toString();

        //act_characteristic.setTitle(uuid);
        //act_characteristic.setService(mBTLE_Service);
        //act_characteristic.setCharacteristic(characteristic);

        //dialog_btle_characteristic.show(getFragmentManager(), "Dialog_BTLE_Characteristic");
    }  if (Utils.hasReadProperty(characteristic.getProperties()) != 0) {
        if (mBTLE_Service != null) {
            mBTLE_Service.readCharacteristic(characteristic);
        }
    }  if (Utils.hasNotifyProperty(characteristic.getProperties()) != 0) {
        if (mBTLE_Service != null) {
            mBTLE_Service.setCharacteristicNotification(characteristic, true);
        }
    }

    return false;
}

Here is where I set the notifications ON, this part I'm not so confident about.

 @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {

    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.w(TAG, "BluetoothAdapter not initialized");
        return;
    }

    mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);

    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(getString(R.string.CLIENT_CHARACTERISTIC_CONFIG)));
    // Todo: if the indications and notifications is to be used, then mode the indications part back into setCharacteristicNotification() and add a check for Indications, then change below code to set both indication and notify
    if (enabled) {
        // Enable INDICATION & NOTIFICATION
        final byte[] ENABLE_INDICATION_NOTIFICATION = {0x03, 0x00};
        descriptor.setValue(ENABLE_INDICATION_NOTIFICATION);
    }
    else {
        descriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
    }

    //ToDo Shorten the delay time so that the Notifications Enable still works

    // Delay in milli seconds
    int m_sec = 200;
    Delay.delay(m_sec, new Delay.DelayCallback() {
        @Override
        public void afterDelay() {
            // Enable Notifications after 200 m_sec Delay
            mBluetoothGatt.writeDescriptor(descriptor);
        }
    });

}

This is where I handle the incoming Bytes, I send a Modbus message from the Phone to NINA B1, then NINA B1 replies with a Modbus message. The Incoming byte count is always < 20 bytes. And I fill the RxBuff until the correct amount of bytes are received AND the Slave Address is correct, then I decode the received message.

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) {

    final Intent intent = new Intent(action);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
        intent.putExtra(EXTRA_UUID, characteristic.getUuid().toString());
    }
    // For all other profiles, writes the data formatted in HEX.
    // Only Pack 20 bytes at a time coming from BLE server into data[]
    final byte[] rx_Data;
    int tempSlavAdr;
    rx_Data = characteristic.getValue();

    if (rx_Data != null && rx_Data.length > 0) {
        // Only handle message if request came from ReadInput_Thread
        if (this.request_flag) {
            // ToDO: See if other non Modbus data arrives between packets
            // Populate RxBuff
            for (int i = 0; i < rx_Data.length; i++) {
                this.RxBuff[this.WrPtr++] = rx_Data[i];
            }
            Log.d(TAG,"QTY Bytes in rx_Data = " + (Integer)rx_Data.length);
            // Start Decode message only after all expected bytes are received && first message is Slave Address

            // Prevent Negative numbers in Slave Address: tempSlavAdr is used in the the next if statement
            tempSlavAdr = (byte) this.RxBuff[0];
                if (tempSlavAdr < 0) tempSlavAdr += 256;

            if ((this.WrPtr == FragmentHome.ExpectedCount_sent) && (tempSlavAdr == slaveAddr)) {
                // Copy RxBuff over to Dec_RxBuff
                int Dec_RxBuff[] = new int[255];
                for (int i = 0; i < this.WrPtr; i++) {
                    Dec_RxBuff[i] = (this.RxBuff[i]);
                    Dec_RxBuff[i] += 256;
                    Dec_RxBuff[i] = Dec_RxBuff[i] & 0xFF;
                }
                // Clear flag indicating that Thread Send a Request
                this.request_flag = false;
                message_rx = false;
                // Decode ModbusMessage and message Good
                if (modbusHandler.DecodeModbusMessage(Dec_RxBuff)){
                    message_rx = true;
                }
                // Decode ModbusMessage and message Bad
                else{
                    message_rx = false;
                }
                // Clear buffers
                WrPtr = 0;
                Arrays.fill(rx_Data, (byte) 0);
                Arrays.fill(RxBuff, (byte) 0);
                // Todo What if WrPtr stop short of Expected counter? Do some protection
                // WrPtr > ExpectedCount Clear Buffers
            } else if (WrPtr > FragmentHome.ExpectedCount_sent) {
                // Clear buffers
                // HomeFragment.set_Actual(false);
                Log.d(TAG,"RX Count to big expected " + FragmentHome.ExpectedCount_sent + " got "  + WrPtr);
                WrPtr = 0;
                Arrays.fill(rx_Data, (byte) 0);
                Arrays.fill(RxBuff, (byte) 0);
            }

            // Todo Use this intent to pass data to a service to Decode Modbus Data (Service not written yet)
            // intent.putExtra(EXTRA_DATA, new String(data) + "\n" + Utils.hexToString(data));
        }
    }
    else {
        //intent.putExtra(EXTRA_DATA, "0");
    }

    //sendBroadcast(intent);
}

I still get some data loss, and not sure how to handle this. Phone and NINA B1 is next to one another so distance is not a problem.

Can I get some help on how to set the indications correctly? And why I might lose bytes.

Marinus

0 Answers0