Bluetooth Low Energy: RFDuino is not detected

时间:2016-04-15 14:50:53

标签: android bluetooth-lowenergy rfduino

I'm trying to program an App to establish a basic communication over BLE between an Android Phone and an RFDuino. The RFDuino is just advertising and can be detected by the "BLE Scanner" App. Below is the draft code for the activity, that is supposed to detect BLE devices.

The problem is, that no devices are detected. The Debug Log shows, that the method "onLeScan" is not even called once...

I'm running out of Ideas and would be thankful for every hint :-)

package de.tuhh.et5.serialcommunicator;

import (...)

public class BLEActivity extends AppCompatActivity {
    private static final long SCAN_PERIOD = 10000;
    private int REQUEST_ENABLE_BT = 1;
    public final static String TAG = "BLE_Activity"; // Tag for the LOG

    private BluetoothAdapter ble_adapter;
    private boolean scanning;
    private Handler ble_handler;
    private BLE_DeviceListAdapter BLE_DeviceListAdapter;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        d("on Create");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ble);          

        TabHost tabHost=(TabHost)findViewById(R.id.tabHost);
        tabHost.setup();

        TabHost.TabSpec t1=tabHost.newTabSpec("Tab1");
        t1.setContent(R.id.tab1_layout);
        t1.setIndicator("Devices");
        tabHost.addTab(t1);

        BLE_DeviceListAdapter = new BLE_DeviceListAdapter();

       ListView ble_list = (ListView)findViewById(R.id.BLE_DeviceListView);
        ble_list.setAdapter(BLE_DeviceListAdapter);

        ble_handler = new Handler();
        // get a bluetooth adapter from the bluetooth manager
        final BluetoothManager bluetoothManager =
                (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        ble_adapter = bluetoothManager.getAdapter();

        // Check if BLE is supported by the Android device
        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            Toast.makeText(this, "BLE Not Supported",
                    Toast.LENGTH_SHORT).show();
            finish();
        }

        // Enable bluetooth
        if (ble_adapter == null || !ble_adapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        }

    }


    // menu stuff
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        d("menu inflates");
        // Inflate the menu; this adds items to the action bar if it is present.
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.ble_menu, menu);

        if (scanning) {
            menu.findItem(R.id.scanning_start).setVisible(false);
        } else {
            menu.findItem(R.id.scanning_start).setVisible(true);
        }
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.scanning_start:
                d("scan pressed");
                BLE_DeviceListAdapter.clear();
                ble_scan(true);
                break;
        }
        return true;
    }

    private void ble_scan(final boolean en){
        d("Scan start");
        // stopLeScan and startLeScan are deprecated since Android 5.0, but still work.
        // to offer a compatibility down to Android 4.3 the old methods are used
        if (en){
            ble_handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    d("scan timer finished");
                    scanning = false;
                    ble_adapter.stopLeScan(ble_callback);
                    invalidateOptionsMenu();
                }
            },SCAN_PERIOD);
            scanning = true;
            ble_adapter.startLeScan(ble_callback);
        }else{
            scanning = false;
            ble_adapter.stopLeScan(ble_callback);
        }
        invalidateOptionsMenu();
    }

    private BluetoothAdapter.LeScanCallback ble_callback = new BluetoothAdapter.LeScanCallback(){

        @Override
        public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord){
            d("onLeScan");
            runOnUiThread(new Runnable(){
                @Override
                public void run(){

                    BLE_DeviceListAdapter.addDevice(device);
                    BLE_DeviceListAdapter.notifyDataSetChanged();

                }
            });
        }
    };


    // This class lists the elements in an ble device entry as defined in "ble_device_entry.cml"
    // It is needed in the BLE_DeviceListAdapter class
    private class ViewHolder {
        TextView name;
        TextView device_id;
        TextView device_adress;
    }

    // #####################################################
    // private Class BLE_DeviceListAdapter
    //
    // Adapter to handle found BLE Devices
    // #####################################################

    private class BLE_DeviceListAdapter extends BaseAdapter{

        private ArrayList<BluetoothDevice> ble_devices; // holds found devices
        private LayoutInflater ble_inflater;            // layout inflater

        public BLE_DeviceListAdapter(){
            super();
            // Init
            ble_devices = new ArrayList<>();
            ble_inflater = BLEActivity.this.getLayoutInflater();
        }

        // method to add a device
        public void addDevice(BluetoothDevice ble_device){
            // device is added, if it is not already in the list
            if (!ble_devices.contains(ble_device)){
                ble_devices.add(ble_device);
            }
        }

        // Mandatory, returns device at position ...
        @Override
        public BluetoothDevice getItem(int position){
            return ble_devices.get(position);
        }

        // clear list..
        public void clear(){
            ble_devices.clear();
        }

        // Mandatory: Not needed, therefore just a dummy
        @Override
        public long getItemId(int i) {
            return i;
        }

        // Mandatory: returns size of list
        @Override
        public int getCount() {
            return ble_devices.size();
        }

        // Mandatory: creates and returns view with a device entry
        @Override
        public View getView(int i, View view, ViewGroup viewGroup ){

            ViewHolder viewHolder; // create view holder

            // if the view does not already exist:
            if (view == null){

                view = ble_inflater.inflate(R.layout.ble_device_entry, null);// inflate view
                viewHolder = new ViewHolder(); // create a ViewHolder
                // Connenct the ViewHolder elements to layout elements
                viewHolder.name = (TextView)findViewById(R.id.BLE_sName);
                viewHolder.device_adress = (TextView)findViewById(R.id.BLE_sAdress);
                viewHolder.device_id = (TextView)findViewById(R.id.BLE_sID);
                view.setTag(viewHolder); // Add the ViewHolder as a Tag to the view
            }else{
                // if the view already exists
                viewHolder = (ViewHolder) view.getTag(); // get Tag from view to populate ViewHolder
            }
            // get BLE Device
            Log.d("test", "hier so....");
            BluetoothDevice device = ble_devices.get(i);
            // get device name
            final String deviceName = device.getName();
            // assign device name to ViewHolder Object
            if (deviceName != null && deviceName.length() > 0){
                viewHolder.device_id.setText(deviceName);
            }else viewHolder.device_id.setText(R.string.unknown_ble_device);
            // assign more values
            viewHolder.device_adress.setText(device.getAddress());
            viewHolder.name.setText(R.string.unknown_ble_device);
            d("before return view");
            return view;
        }

    }
    // #####################################################
    // End Adapter
    // #####################################################

    public void d(Object msg) {
        Log.d(TAG, ">==< " + msg.toString() + " >==<");
    }

    public void e(Object msg) {
        Log.e(TAG, ">==< " + msg.toString() + " >==<");
    }

}

2 个答案:

答案 0 :(得分:1)

好吧,似乎我搞砸了权限。我没有该位置的运行时权限。我认为基于清单的许可是足够的,如果一个人没有排除目标Android 6.xx。现在我对视图持有者和东西有问题,但那是另一个故事: - )。

无论如何,感谢解决方案@MarkusKauppinen!

答案 1 :(得分:0)

我认为扫描代码看起来不错。

要检查的一些事项:

您是否调试/登录以检查选项菜单是否有效并且ble_scan()被调用?

您是否调试/登录以检查startLeScan()是否返回true

如果未找到任何设备,则不会调用onLeScan()回调。

由于您正在扫描10秒钟,因此RFDuino应该经常宣传自己/足够长时间,以至于在10秒窗口内始终至少发生一个广告周期。

所有Android版本都需要这些权限:

android.permission.BLUETOOTH

android.permission.BLUETOOTH_ADMIN

另外Android 6.x(Marhsmallow)以及后来还需要:

android.permission.ACCESS_COARSE_LOCATION

如果您正在编写仅针对6.x及更高版本的代码,则需要使用新的运行时权限模型,而不是基于旧的清单文件。

在某些运行Android 6.x的设备上,系统范围内&#34;位置&#34;设置必须为ON。请参阅:onLeScan from BluetoothAdapter.LeScanCallback not called in Android Marshmallow

然后当然some of the older discussions可能会有一些有用的提示。