Как я могу увидеть сервисы и характеристики модуля, к которому я подключен? - PullRequest
2 голосов
/ 17 июня 2020

Я делаю приложение Android, которое должно подключаться к BLE-модулю и уметь читать его услуги и характеристики. Мое приложение основано на коде приложения Accent Systems "iBKS Hello World" .

Я не использую iBeacons, а использую другой тип BLE-модуля. Но когда я подключаюсь к нему, приложение не показывает список услуг и характеристик, как указывает код, что должно произойти.

Может ли кто-нибудь помочь мне решить эту проблему? Пожалуйста, дайте мне знать!

Мой код:

public class ScanActivity extends AppCompatActivity {

//DEFINE VARS
String TAG = "ScanActivity";

BluetoothAdapter mBluetoothAdapter;
BluetoothGatt mBluetoothGatt;
BluetoothLeScanner scanner;
ScanSettings scanSettings;


private List<String> scannedDeivcesList;
private ArrayAdapter<String> adapter;

//DEFINE LAYOUT
ListView devicesList;

//THIS METHOD RUNS ON APP LAUNCH
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_scan);

    //Define listview in layout
    devicesList = (ListView) findViewById(R.id.devicesList);
    //Setup list on device click listener
    setupListClickListener();

    //Initialize de devices list
    scannedDeivcesList = new ArrayList<>();

    //Initialize the list adapter for the listview with params: Context / Layout file / TextView ID in layout file / Devices list
    adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, android.R.id.text1, scannedDeivcesList);

    //Set the adapter to the listview
    devicesList.setAdapter(adapter);


    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    assert getSupportActionBar() != null;
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

    SpannableString s = new SpannableString("Scan for modules");

    s.setSpan(new com.accent_systems.ibkshelloworld.TypefaceSpan(this, "Khand-Bold.ttf"), 0, s.length(),
            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    s.setSpan(new ForegroundColorSpan(Color.parseColor("#3a3c3e")), 0, s.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
    setTitle(s);

    getSupportActionBar().setDisplayUseLogoEnabled(true);
    getSupportActionBar().setDisplayShowHomeEnabled(true);

    //init Bluetooth adapter
    initBT();
    //Start scan of bluetooth devices
    startLeScan(true);

}

@Override
protected void onStop() {
    super.onStop();
    startLeScan(false);
}

private void initBT(){
    final BluetoothManager bluetoothManager =  (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    mBluetoothAdapter = bluetoothManager.getAdapter();

    //Create the scan settings
    ScanSettings.Builder scanSettingsBuilder = new ScanSettings.Builder();
    //Set scan latency mode. Lower latency, faster device detection/more battery and resources consumption
    scanSettingsBuilder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
    //Wrap settings together and save on a settings var (declared globally).
    scanSettings = scanSettingsBuilder.build();
    //Get the BLE scanner from the BT adapter (var declared globally)
    scanner = mBluetoothAdapter.getBluetoothLeScanner();
}

private void startLeScan(boolean endis) {
    if (endis) {
        //********************
        //START THE BLE SCAN
        //********************
        //Scanning parameters FILTER / SETTINGS / RESULT CALLBACK. Filter are used to define a particular
        //device to scan for. The Callback is defined above as a method.
        scanner.startScan(null, scanSettings, mScanCallback);
    }else{
        //Stop scan
        scanner.stopScan(mScanCallback);
    }
}


private ScanCallback mScanCallback = new ScanCallback() {
    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        super.onScanResult(callbackType, result);

        //Here all the detected BLE devices will be received . "result" contains the device
        //address and name as a BLEPeripheral, the advertising content as a ScanRecord, the Rx RSSI
        //and the timestamp when received. Type result.get... to see all the available methods you can call.

        //Convert advertising bytes to string for a easier parsing. GetBytes may return a NullPointerException. Treat it right(try/catch).
        String advertisingString = byteArrayToHex(result.getScanRecord().getBytes());
        //Print the advertising String in the LOG with other device info (ADDRESS - RSSI - ADVERTISING - NAME)
        Log.i(TAG, result.getDevice().getAddress()+" - RSSI: "+result.getRssi()+"\t - "+advertisingString+" - "+result.getDevice().getName());

        //Check if scanned device is already in the list by mac address
        boolean contains = false;
        for(int i=0; i<scannedDeivcesList.size(); i++){
            if(scannedDeivcesList.get(i).contains(result.getDevice().getAddress())){
                //Device already added
                contains = true;
                //Replace the device with updated values in that position
                scannedDeivcesList.set(i, result.getRssi()+"  "+result.getDevice().getName()+ "\n       ("+result.getDevice().getAddress()+")");
                break;
            }
        }

        if(!contains){
            //Scanned device not found in the list. NEW => add to list
            scannedDeivcesList.add(result.getRssi()+"  "+result.getDevice().getName()+ "\n       ("+result.getDevice().getAddress()+")");
        }

        //After modify the list, notify the adapter that changes have been made so it updates the UI.
        //UI changes must be done in the main thread
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                adapter.notifyDataSetChanged();
            }
        });

    }
};

//Method to convert a byte array to a HEX. string.
private String byteArrayToHex(byte[] a) {
    StringBuilder sb = new StringBuilder(a.length * 2);
    for(byte b: a)
        sb.append(String.format("%02x", b & 0xff));
    return sb.toString();
}

void setupListClickListener(){
    devicesList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            //Stop the scan
            Log.i(TAG, "SCAN STOPPED");
            scanner.stopScan(mScanCallback);

            //Get the string from the item clicked
            String fullString = scannedDeivcesList.get(position);
            //Get only the address from the previous string. Substring from '(' to ')'
            String address = fullString.substring(fullString.indexOf("(")+1, fullString.indexOf(")"));
            //Get BLE device with address
            BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
            //******************************
            //START CONNECTION WITH DEVICE AND DECLARE GATT
            //******************************
            Log.i(TAG,"*************************************************");
            Log.i(TAG, "CONNECTION STARTED TO DEVICE "+address);
            Log.i(TAG,"*************************************************");

            //ConnectGatt parameters are CONTEXT / AUTOCONNECT to connect the next time it is scanned / GATT CALLBACK to receive GATT notifications and data
            // Note: On Samsung devices, the connection must be done on main thread
            mBluetoothGatt = device.connectGatt(ScanActivity.this, false, mGattCallback);

            /*
            There is also another simplest way to connect to a device. If you already stored
            the device in a list (List<BluetoothDevice>) you can retrieve it directly and
            connect to it:
            mBluetoothGatt = mList.get(position).connectGatt(MainActivity.this, false, mGattCallback);
             */
        }
    });
}

//Connection callback
BluetoothGattCallback mGattCallback =
        new BluetoothGattCallback() {
            @Override
            public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    //Device connected, start discovering services
                    Log.i(TAG, "DEVICE CONNECTED. DISCOVERING SERVICES...");
                    mBluetoothGatt.discoverServices();
                } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                    //Device disconnected
                    Log.i(TAG, "DEVICE DISCONNECTED");
                }
            }

            // On discover services method
            @Override
            public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    //Services discovered successfully. Start parsing services and characteristics
                    Log.i(TAG, "SERVICES DISCOVERED. PARSING...");
                    displayGattServices(gatt.getServices());
                } else {
                    //Failed to discover services
                    Log.i(TAG, "FAILED TO DISCOVER SERVICES");
                }
            }

            //When reading a characteristic, here you receive the task result and the value
            @Override
            public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    //READ WAS SUCCESSFUL
                    Log.i(TAG, "ON CHARACTERISTIC READ SUCCESSFUL");
                    //Read characteristic value like:
                    //characteristic.getValue();
                    //Which it returns a byte array. Convert it to HEX. string.
                } else {
                    Log.i(TAG, "ERROR READING CHARACTERISTIC");
                }
            }

            //When writing, here you can check whether the task was completed successfully or not
            @Override
            public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
                super.onCharacteristicWrite(gatt, characteristic, status);
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    Log.i(TAG, "ON CHARACTERISTIC WRITE SUCCESSFUL");
                } else {
                    Log.i(TAG, "ERROR WRITING CHARACTERISTIC");
                }
            }

            //In this method you can read the new values from a received notification
            @Override
            public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
                Log.i(TAG, "NEW NOTIFICATION RECEIVED");
                //New notification received. Check the characteristic it comes from and parse to string
                /*if(characteristic.getUuid().toString().contains("0000fff3")){
                    characteristic.getValue();
                }*/
            }

            //RSSI values from the connection with the remote device are received here
            @Override
            public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
                Log.i(TAG, "NEW RSSI VALUE RECEIVED");
                //Read remote RSSI like: mBluetoothGatt.readRemoteRssi();
                //Here you get the gatt table where the rssi comes from, the rssi value and the
                //status of the task.
            }
        };

//Method which parses all services and characteristics from the GATT table.
private void displayGattServices(List<BluetoothGattService> gattServices) {
    //Check if there is any gatt services. If not, return.
    if (gattServices == null) return;

    // Loop through available GATT Services.
    for (BluetoothGattService gattService : gattServices) {
        Log.i(TAG, "SERVICE FOUND: "+gattService.getUuid().toString());
        //Loop through available characteristics for each service
        for (BluetoothGattCharacteristic gattCharacteristic : gattService.getCharacteristics()) {
            Log.i(TAG, "  CHAR. FOUND: "+gattCharacteristic.getUuid().toString());
        }
    }

    //****************************************
    // CONNECTION PROCESS FINISHED!
    //****************************************
    Log.i(TAG, "*************************************");
    Log.i(TAG, "CONNECTION COMPLETED SUCCESFULLY");
    Log.i(TAG, "*************************************");

}

}

1 Ответ

0 голосов
/ 17 июня 2020

Полное описание можно найти здесь . И здесь короткий пример. Итак, вы приносите список услуг после подключения BLE и запроса услуг. Позже вы можете запросить характеристики для каждой услуги. Основная идея, когда вы предоставляете услуги один за другим, у вас есть UUID и ссылка. Если у вас есть какие-то услуги, которыми вы хотели бы пользоваться, вы должны знать их, прежде чем приносить все услуги. И, собрав их, сохраните где-нибудь ссылку на сервис, тестируемый по каждому известному UUID. Позже используйте эту ссылку для описания характеристик и связи с устройством.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...