4

I am using the AltBEacon Android library for developing an iBeacon app for Android devices. I am scanning for beacons, however, only two out of four beacons are found (sometimes 1/4).

I increasemBeaconManager.setForegroundScanPeriod(5000l); to 5 seconds, but still same result. I am not sure if the CustomAdapter I use for binding details to view is wrong or the issue is do with the mobile device (I am using Galaxy Note II - Android 4.4.2 (KitKat))? Can anyone locate my mistake?

Another issue is that the distance calculation from device to the beacon is returning not correct (when I am approximately 0.5 m away it returns betweem 3-6 m)

What am I doing wrong?

Note: I have checked UUID, Major, Minors of Beacon for any mistake. I have increased the scanning time. None of these worked.

Here is the Fragment that starts monitoring and ranging:

public class FragmentScanBeacons extends Fragment implements BeaconConsumer{

    public static Region mRegion = new Region("Server", Identifier.parse("Here is my UUID"), null, null);
    private BeaconManager mBeaconManager;

    private BeaconBaseAdapter beaconBaseAdapter;

    private ListView beaconsListLv;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBeaconManager = BeaconManager.getInstanceForApplication(getActivity());

        //BEACON PARSER
        mBeaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));
        //mBeaconManager.debug = true;
        beaconBaseAdapter = new BeaconBaseAdapter(getActivity());
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        View view = inflater.inflate(R.layout.fragment_beacons, container, false);

        //UI
        beaconsListLv = (ListView) view.findViewById(R.id.beaconsListView);

        //Set Adapter
        beaconsListLv.setAdapter(beaconBaseAdapter);

        //Check for bluetooth and Scan for Beacon
        verifyBluetooth();

        //Start Monitoring and Ranging
        mBeaconManager.bind(this);

        return view;
    }

    @Override
    public void onResume() {
        super.onResume();
        if(mBeaconManager.isBound(this)){
            mBeaconManager.setBackgroundMode(false);
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        if(mBeaconManager.isBound(this)){
            mBeaconManager.setBackgroundMode(true);
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mBeaconManager.unbind(this);
    }

    @Override
    public void onBeaconServiceConnect() {
        try {

            //Scan lasts for SCAN_PERIOD time
            mBeaconManager.setForegroundScanPeriod(1000l);
            //mBeaconManager.setBackgroundScanPeriod(0l);

            //Wait every SCAN_PERIOD_INBETWEEN time
            mBeaconManager.setForegroundBetweenScanPeriod(0l);

            //Update default time with the new one
            mBeaconManager.updateScanPeriods();
        }
        catch (RemoteException e){
            e.printStackTrace();
        }

        //Set Monitoring
        mBeaconManager.setMonitorNotifier(new MonitorNotifier() {
            @Override
            public void didEnterRegion(Region region) {
                Log.d("TEST", "ENTERED beacon region");

                //Start Raning as soon as you detect a beacon
                try {
                    mBeaconManager.startRangingBeaconsInRegion(mRegion);
                }
                catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void didExitRegion(Region region) {
                Log.d("TEST", "EXITED beacon region");
            }

            @Override
            public void didDetermineStateForRegion(int state, Region region) {
                Log.d("TEST", "SWITCHED from seeing/not seeing beacon to state " + state);
            }
        });

        //Set Ranging
        mBeaconManager.setRangeNotifier(new RangeNotifier() {
            @Override
            public void didRangeBeaconsInRegion(final Collection<Beacon> beacons, Region region) {
                if (beacons != null && beacons.size() > 0) {
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            beaconBaseAdapter.initAll(beacons);
                        }
                    });
                }
            }
        });

        try {
            //Start Monitoring
            mBeaconManager.startMonitoringBeaconsInRegion(mRegion);
        }
        catch (RemoteException e) {
            e.printStackTrace();
        }

    }

    @Override
    public Context getApplicationContext() {
        return getActivity().getApplicationContext();
    }

    @Override
    public void unbindService(ServiceConnection serviceConnection) {
        getActivity().unbindService(serviceConnection);
    }

    @Override
    public boolean bindService(Intent intent, ServiceConnection serviceConnection, int mode) {
        return getActivity().bindService(intent, serviceConnection, mode);
    }
}

And here is the custom adapter:

public class BeaconBaseAdapter extends BaseAdapter {

    private Context myContext;

    private LayoutInflater inflater;

    public static ArrayList<Beacon> beacons;

    public BeaconBaseAdapter(Context context) {
        this.myContext = context;
        this.inflater = LayoutInflater.from(context);
        this.beacons = new ArrayList<Beacon>();
    }

    public void initAll(Collection<Beacon> newBeacons) {
        this.beacons.clear();
        this.beacons.addAll(newBeacons);
        notifyDataSetChanged();
    }

    @Override
    public int getCount() {
        return beacons.size();
    }

    @Override
    public Beacon getItem(int position) {
        return beacons.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        if (convertView == null) {
            convertView = inflater.inflate(R.layout.beacon_list_row, null);
            convertView.setTag(new ViewHolder(convertView));
        }

        bind(getItem(position), position, convertView);
        return convertView;
    }


    private void bind(Beacon beacon, int position, View view) {
        ViewHolder holder = (ViewHolder) view.getTag();

        holder.manufacturerTextView.setText("Manufacturer: " + beacon.getManufacturer());
        holder.idOneTextView.setText("UUID: " + beacon.getId1());
        holder.idTwoTextView.setText("Major: " + beacon.getId2());
        holder.idThreeTextView.setText("Minor: " + beacon.getId3());
        holder.txPowerTextView.setText("TX-Power: " + beacon.getTxPower());
        holder.rssiTextView.setText("RSSI: " + beacon.getRssi());
        holder.distanceTextView.setText(String.format("DISTANCE: (%.2f m)", beacon.getDistance()));
        holder.nameTextView.setText("Bluetooth Name: " + beacon.getBluetoothName());
        holder.addressTextView.setText("Bluetooth Adrs: " + beacon.getBluetoothAddress());
    }

    static class ViewHolder {
        final TextView nameTextView;
        final TextView manufacturerTextView;
        final TextView idOneTextView;
        final TextView idTwoTextView;
        final TextView idThreeTextView;
        final TextView txPowerTextView;
        final TextView rssiTextView;
        final TextView distanceTextView;
        final TextView addressTextView;

        ViewHolder(View view) {
            nameTextView = (TextView) view.findViewWithTag("name");
            manufacturerTextView = (TextView) view.findViewWithTag("manufacturer");
            idOneTextView = (TextView) view.findViewWithTag("id_one");
            idTwoTextView = (TextView) view.findViewWithTag("id_two");
            idThreeTextView = (TextView) view.findViewWithTag("id_three");
            txPowerTextView = (TextView) view.findViewWithTag("tx_power");
            rssiTextView = (TextView) view.findViewWithTag("rssi");
            distanceTextView = (TextView) view.findViewWithTag("distance");
            addressTextView = (TextView) view.findViewWithTag("address");
        }
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
hrskrs
  • 4,447
  • 5
  • 38
  • 52
  • Are you detecting those beacons using another application? Half my beacons went out of batteries while I was developing... – shkschneider Jan 15 '15 at 13:42
  • Yeap, i checked them with IOS device and all are found. Actually android device it also finds all but not on a single catch. Sometimes it catches 2, sometimes 3. on the `logcat` there is an `onScanResult()` that soemtimes it logs 3 device but on my `ListView` only 2 are binded – hrskrs Jan 15 '15 at 13:42
  • Are all beacons from same manufacturer? I had an issue with different manufacturers, and had to figure out different layouts to add to beacon parsers list – Álvaro Pérez Soria Jan 15 '15 at 13:52
  • @Álvaro Pérez Soria yeah all of them are from the same manufacturer. – hrskrs Jan 15 '15 at 13:53

2 Answers2

6

A few thoughts:

  1. Try using an off-the-shelf beacon detection program like Locate, which uses the same Android Beacon Library under the hood. If the program detects your beacons reliably, then it is probably an issue in the custom code you mention.

  2. Make sure your beacons are transmitting frequently enough. The baseline transmission frequency is 10 Hz, but the library with default settings will pick up beacons reliably that are transmitting at least 1 Hz. Increasing the mBeaconManager.setForegroundScanPeriod(); as you describe should help this, but if the beacons are only transmitting at 0.1 Hz, then you would still have problems like you describe.

  3. Some Android devices have a combined Wi-Fi/Bluetooth chip that prevents reliable simultaneous operation of both these radios. Try disabling Wi-Fi and see if you get the same result.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
davidgyoung
  • 63,876
  • 14
  • 121
  • 204
  • thnx for reply. The detection program you suggested, it finds 3/4, but the distance proximation is good enough. Why i cannot get that same accuracy. Is this app an open source so i can refer to the app code because i am using same libary but distance proximation is not good?? – hrskrs Jan 15 '15 at 15:14
  • 1
    That exact program is not open source, but there is a version of it with simpler UI that is: https://github.com/AltBeacon/android-beacon-library-reference. This version only shows the first beacon detected, but it is trivial to modify it to show every beacon detected. – davidgyoung Jan 15 '15 at 15:21
  • as i am using same library, and getting distance from the `RangeMonitoring` without modifying the any function, hows possible that i dont get an accurate distance. The LocateBeacon app calculates good enough while the library i am using its not reliable? ps: it is also based on the example given on the [website](http://altbeacon.github.io/android-beacon-library/samples.html) – hrskrs Jan 15 '15 at 15:49
  • 1
    What version of the Android Beacon Library are you using with the reference application? The Locate app you downloaded was built last week with the 2.1-beta1 version of the Android Beacon Library. If you are using this version of the library, then the distance estimates should be the same. – davidgyoung Jan 15 '15 at 17:20
  • I was using `android-beacon-library-2.0.aar` from the official website. Now i tried with `android-beacon-library-2.1-beta1.aar`.Now it calculates right. It was because of the device. In `Galaxy Note II` it was calculating it not correct, while in `HTC M8` it calculates it ok. The only thing left is that from the code above my app cannot find all beacons at once. Whenever `notifyOnChange()` is called to refresh the `Listview`, 1 out of 3, sometimes 2/3 are shown. They also change row places. Can you locate my problem why is this happening and guide me? – hrskrs Jan 16 '15 at 07:37
  • i also noticed that the `Tx-Power` changes according to the beacon i use. Have you used any algorithm to try calculating best` tx-power` (calibrated--power)? You used some coefficients from `json` however the coefficients are different than the link [here](http://stackoverflow.com/questions/20416218/understanding-ibeacon-distancing) – hrskrs Jan 19 '15 at 09:16
  • The txPower ahould be different for each beacon type. It is a value transmitted by the beacon to tell you how powerful its transmitter is for distance estimation purposes. – davidgyoung Jan 19 '15 at 14:05
  • 1
    If you want a consistent list of visible beacons in a consistent sort order, you need to maintain your own list, adding beacons to it as they are discovered and removing them from the list when they have not been seen for a few seconds. You can then sort the list by the desired criteria (e.g. by identifiers) before displaying the list. You must do this in custom code, as the library does not do so for you. – davidgyoung Jan 19 '15 at 14:08
  • thnx for helping. I really appriciate it. Last question is that what if we want to use `multiple beacons` from different `manufacturers` in the same application. What is the best way to `parse` them? Should we create `multiple instances` of `BeaconManager`? – hrskrs Jan 19 '15 at 15:17
  • i also noticed that `Locate` beacon detection program is not finding `all beacons`. I have `4 beacons from the same manufacturer` but only `3` are found, while `iPhone lists all` of them? – hrskrs Jan 19 '15 at 15:39
  • What is the manufacturer and model? Do you know the transmit frequency? Is the beacon that is not detected on Android consistently not detected, or only intermittently? Anything different about it? – davidgyoung Jan 19 '15 at 17:52
  • its an `April Beacon`. There are `4(x,y,z,l)` beacons but it detects only 3. Sometimes `x,y,z` sometimes `x,y,l` but never all of them? What can be the reason? Also what is the best way to parse different manufacturers beacons? – hrskrs Jan 20 '15 at 06:54
  • I suspect that the reason you don't see every beacon in every callback is because the April Beacon is not transmitting frequently enough. The library by default does a scan cycle every 1.1 seconds, and if a beacon is not detected in this interval it will not show up. You can adjust this as you describe above with a line like `mBeaconManager.setForegroundScanPeriod(5000l);` – davidgyoung Jan 20 '15 at 21:58
  • 1
    You only need to add a new BeaconParser with a custom beacon layout if you are using a beacon from a manufacturer that uses a different layout. All beacons that comply with the iBeacon layout can use the same parser. – davidgyoung Jan 20 '15 at 22:00
  • and what about `lollipop` support? With lollipop `android` says that they made a massive improvement. Does this library include that? – hrskrs Jan 21 '15 at 07:55
  • Library version 2.1+ includes Android 5.0 support for beacon transmission and fast detection in the background. – davidgyoung Jan 21 '15 at 12:41
  • again i came up with a new question. I didnt want to open a new section instead i am asking here: Is it possible that with this library i can modify `beacon properties`(changing UUID, Major, Transmit Frequency, etc)? – hrskrs Jan 23 '15 at 07:48
  • 1
    Unfortunately, no, because there is no way to set beacon identifiers that is standard. See here: http://stackoverflow.com/a/20766229/1461050 – davidgyoung Jan 23 '15 at 12:40
  • i asked that because most beacons own APPs allow that by writing a default password. At least they allow to change `UUID, Major and Minor` – hrskrs Jan 23 '15 at 15:15
0

OK, I finally solved it. I ordered according to the beacon MAC address. So instead of:

public void initAll(Collection<Beacon> newBeacons) {
    this.beacons.clear();
    this.beacons.addAll(newBeacons);
}

I did:

public void initAll(Collection<Beacon> newBeacons) {

    this.beacons.clear();
    this.beacons.addAll(newBeacons);

    //Sort
    Collections.sort(this.beacons, new Comparator<Beacon>() {
        @Override
        public int compare(Beacon b1, Beacon b2) {
            String mac1 = b1.getBluetoothAddress();
            String mac2 = b2.getBluetoothAddress();

            return mac1.compareTo(mac2);
        }
    });
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
hrskrs
  • 4,447
  • 5
  • 38
  • 52