Bluetooth iBeacon Part 1: Search for the device + Wireless Debugging!

Bluetooth iBeacon Part 1: Search for the device + Wireless Debugging!

Here -> Part 2

Recently, I received my $5 iBeacon, which was looked like a white button on eBay. I ordered three of them to check the function like this kind. The online description about this ibeacon is equiped NRF51882 distance chip, 3.3V. Bluetooth beacon is distance device can tell you how close to it in its broadcast range. Here are the pictures I taken.


It is a tool to help phone display the merchandise in the mall, finding the book in the library, or even tagging your children.



Let's try to search to the device in this part. Our task is:

  • Check the Bluetooth status: if it's OFF then turn it ON.
  • Search all nearby Bluetooth device and put them on a ListView.

Simple, aren't they? In this project, I need your Android phone stands by instead of emulator. Let's do it.

Open a new project with empty activity, called "Beacon Test". We've nothing change in build.gradle. That's a good beginning.


In activity_main.xml, we need to two buttons: one for turning Bluetooth OFF and one for scanning. Also, we need a ListView to display what the scanner found.



The UI is easy. After you finished it, we need to grab some permissions in AndroidManifest.xml.

<?xml version="1.0" encoding="utf-8"?>  
<manifest ...>  
  
    <uses-permission android:name="android.permission.BLUETOOTH" />  
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />  
    <uses-permission android:name="android.permission.INTERNET" />  
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />  
  
    <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>  
  
    <application ...  

Have you noticed the ACCESS_FINE_LOCATION? Yeah, Bluetooth beacon is a distance device which is required permission for location. After that, please paste the request permission from your other program in MainActivity.java.

    static final int REQUEST_ID_MULTIPLE_PERMISSIONS = Your Favor #;
 
    public void requestPermissions() {  
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {  
            if (checkSelfPermission(Manifest.permission.INTERNET)   
                                  == PackageManager.PERMISSION_GRANTED &&  
                    ...BLUETOOTH... &&  
                    ...BLUETOOTH_ADMIN... &&  
                    ...ACCESS_FINE_LOCATION...) {  
                ltag("Permission is granted");  
            } else {  
                ltag("Permission is revoked");  
                ActivityCompat.requestPermissions(this, new String[]{  
                                Manifest.permission.INTERNET,  
                                ...BLUETOOTH,  
                                ...BLUETOOTH_ADMIN,  
                                ...ACCESS_FINE_LOCATION  
                        },  
                        REQUEST_ID_MULTIPLE_PERMISSIONS);  
            }  
        }  
    }  
  
    @Override  
    public void onRequestPermissionsResult(...) {  
        ... 
  
        switch (requestCode) {  
            case REQUEST_ID_MULTIPLE_PERMISSIONS:  
                ...
                break;  
        }  
    }  

And paste your shortcut for toast and log. Here is mine.

    /* Toast shortcut */  
    public static void msg(Context context, String message) {  
        Toast.makeText(context, message, Toast.LENGTH_LONG).show();  
    }  
    /* Log tag and shortcut */  
    final static String TAG = "MYLOG BLE";  
    public static void ltag(String message) { Log.i(TAG, message); }  

Let's put in some variables.

    /*  
        Variables  
     */  
    private BluetoothAdapter mBluetoothAdapter;  
    Button bluetoothButton;  
    boolean bleEnable = false;    
  
    //search list and adapter  
    private boolean mScanning;  
    private Handler mHandler;  
    private List<BluetoothDevice> mBles;  
	//ListView  
    ListView deviceListView;  
    private BleDeviceListAdapter bleListAdapter;  
    /*  
        End Variables  
     */

BluetoothAdapter is your hardware and don't mix up with your ListViewAdapter. Others are for the scanning function. Let's initialize all the variables in onCreate().

    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
  
        requestPermissions();  
  
        // Determine whether BLE is supported on the device.  
        // you can selectively disable BLE-related features.  
        if (!getPackageManager().hasSystemFeature(  
            PackageManager.FEATURE_BLUETOOTH_LE)) {  
            msg(this, "Bluetooth Low Energy Device Not Supported!");  
            finish();  //Bye-bye   
        }  
  
        // Initializes Bluetooth adapter.  
        final BluetoothManager bluetoothManager =  
            (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);  
        mBluetoothAdapter = bluetoothManager.getAdapter();  
  
        bluetoothButton = (Button) findViewById(R.id.bluetoothButton);  
        checkBluetooth();  

        //ListView of your scanning function  
        mHandler = new Handler(); //initial timer  
        mBles = new ArrayList<>();  //hold search result      
        deviceListView = (ListView) findViewById(R.id.deviceListView);  
  
    }   

Everything is following our plan: check device and scan. Let's check bluetooth.

    /*  
        Check bluetooth adapter  
     */  
    private void checkBluetooth() {  
        if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {  
            //ask to turn on bluetooth Hardware  
            Intent i = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);  
            startActivity(i);  
  
            if (mBluetoothAdapter.isEnabled()) {  
                msg(this, "Bluetooth turned ON.");  
                ltag("Bluetooth turned ON.");  
                bluetoothButton.setText("Bluetooth(ON)\nTurned OFF?");  
                bleEnable = true;  
            }  
        } else {  
            msg(this, "Bluetooth turned ON!");  
            bluetoothButton.setText("Bluetooth(ON)\nTurned OFF?");  
            bleEnable = true;  
        }  
    }

    @Override  
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {    
        // User chose not to enable Bluetooth.  
        if (requestCode == REQUEST_ENABLE_BT &&    
            resultCode == RESULT_CANCELED) {  
            finish();  //bye-bye    
            return;  
        }  
        super.onActivityResult(requestCode, resultCode, data);  
    }  
  
    private static final int REQUEST_ENABLE_BT = Your Another Favor;  
    @Override  
    protected void onResume() {  
        super.onResume();  
        // Ensures Bluetooth is enabled on the device.  
        if (!mBluetoothAdapter.isEnabled()) {  
            if (!mBluetoothAdapter.isEnabled()) {  
                Intent enableBtIntent = new 
                    Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);  
                startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);  
            }  
        }  
        //update   
        checkBluetooth();  
        // Initializes list view adapter.    
        bleListAdapter = new BleDeviceListAdapter(MainActivity.this, mBles);  
        deviceListView.setAdapter(bleListAdapter);  
        scanLeDevice(false);  
    }  
    /*  
        End check bluetooth adapter  
     */

There're two places required to be checked, beginning and resume. And we have a button to turn off the Bluetooth function.

    /*  
        Turn off bluetooth hardware  
     */  
    public void bluetoothOff(View v) {  
        if (bleEnable) {  
            mBluetoothAdapter.disable();  
            if (mBluetoothAdapter.isEnabled()) {  
                msg(this, "Error to disable Bluetooth!");  
            } else {  
                msg(this, "Bluetooth turned OFF!");  
                bluetoothButton.setText("Bluetooth(OFF)\nTurned ON?");  
                bleEnable = false;  
            }  
        } else {  
            checkBluetooth();  
        }  
    }

Let's code our scanner. The scanner needs a callback, called BluetoothAdapter.LeScanCallback. Long name and hard to remember, isn't it? It updates your ListView and device list when it founds something.

    private BluetoothAdapter.LeScanCallback mLeScanCallback =  
            new BluetoothAdapter.LeScanCallback() {  
                @Override  
                public void onLeScan(final BluetoothDevice device, int rssi,                                        byte[] scanRecord) {  
                    runOnUiThread(new Runnable() {  
                        @Override  
                        public void run() {  
                            bleListAdapter.addDevice(device);  
                            mBles.add(device);  
                            bleListAdapter.notifyDataSetChanged();  
                        }  
                    });  
                }  
            };  

Here is your scanner:

    // Stops scanning after 10 seconds.  
    private static final long SECOND = 1000;  
    private static final long SCAN_PERIOD = 10 * SECOND;  
    private void scanLeDevice(final boolean enable) {  
        if (enable) {  
            // Stops scanning after a pre-defined scan period.  
            mHandler.postDelayed(new Runnable() {  
                @Override  
                public void run() {  
                    mScanning = false;  
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);  
                }  
            }, SCAN_PERIOD);  
  
            mScanning = true;  
            mBluetoothAdapter.startLeScan(mLeScanCallback);  
        } else {  
            mScanning = false;  
            mBluetoothAdapter.stopLeScan(mLeScanCallback);  
        }  
    }

    @Override  
    protected void onPause() {  
        super.onPause();  
        scanLeDevice(false);  
        bleListAdapter.clear();  
    }

    /*  
        Find Beacon button
     */  
    public void findBeacon(View v) {  
        scanLeDevice(true);  
        deviceListView.setAdapter(bleListAdapter);  
    }

Finally, we need the ListViewAdapter, called BleDeviceListAdapter.java. And its custom layout is list_item_device.xml. There're two TextViews in the layout, device name and device address.

Here is the code of adapter.

/**  
 * Created by Homan on 3/9/2018.  
 */  
  
public class BleDeviceListAdapter extends BaseAdapter{  
    private List<BluetoothDevice> mLeDevices;   
    private Context context;  
  
    public BleDeviceListAdapter(Context context, List<BluetoothDevice> devices) {  
        super();  
        this.context = context;  
        this.mLeDevices = devices;  
    }  
  
    public void addDevice(BluetoothDevice device) {  
        if(!mLeDevices.contains(device)) {  
            mLeDevices.add(device);  
        }  
    }  
  
    public BluetoothDevice getDevice(int position) {  
        return mLeDevices.get(position);  
    }  
    public void clear() {  
        mLeDevices.clear();  
    }  
  
    @Override  
    public int getCount() {  
        return mLeDevices.size();  
    }  
  
    @Override  
    public Object getItem(int position) {  
        return mLeDevices.get(position);  
    }  
  
    @Override  
    public long getItemId(int position) {  
        return position;  
    }  
  
    @Override  
    public View getView(int position, View convertView, ViewGroup parent) {  
        // General ListView optimization code.  
        if (convertView == null) {  
            convertView = LayoutInflater.from(context)  
                                        .inflate(R.layout.list_item_device,   
                          parent, false);  
        }  
  
        TextView deviceNameTV = (TextView)   
            convertView.findViewById(R.id.deviceNameTV);  
        TextView deviceAddressTV = (TextView)  
            convertView.findViewById(R.id.deviceAddressTV);  
  
        BluetoothDevice device = mLeDevices.get(position);  
        final String deviceName = device.getName();  
        if (deviceName != null && deviceName.length() > 0)  
            deviceNameTV.setText("Device Name: "+deviceName);  
        else  
            deviceNameTV.setText("Device Name: UnKnown");  
        deviceAddressTV.setText("Address: "+device.getAddress());  
        return convertView;  
    }  
}  

A regular ListViewAdapter, it's not a big deal for us. Here is the method to connect your phone on WiFi without your USB cable.

Let's connect to your phone to your private WiFi network. And you choose Setting->About phone->Status->IP address. For example, I found mine, https://www.dhirubhai.net/redir/invalid-link-page?url=172%2e20%2e10%2e2 .

Now, run the project. Let's check the ON/OFF function.

Working fine. How's about the scanner?

Wow, what a messy! And the duplicate addresses are showed in list. There're too many Bluetooth device around. Let's identify our device and fix the bug in next part.















要查看或添加评论,请登录

Homan Huang的更多文章

社区洞察

其他会员也浏览了