martedì 8 aprile 2014

Proximity Sensor con Bluetooth 2 in Android

Non avendo a disposizione un telefono con Bluetooth 4.0 (od LE che si dica) ho provato a ricreare un sistema di prossimita' legato a Bluetooth che faccia un po' il verso a IBeacon e simili

Per simulare un IBeacon ed un ricevitore bisogna prevedere un trasmettitore BT sempre visibile ed un ricevitore che sai in grado di misurare la qualita' del segnale di ricezione senza la necessita' di un precedente accoppiamento con il trasmettitore


Esempio di scoperta Bluetoorh

Android permette di calcolare la qualita' del segnale del ricevitore (RSSI) anche senza la necessita' di accoppiamento per tutte le versioni di Bluetooth mentre MacOSX e Linux permetto di essere impostati per avere bluetooth sempre visibile (senza timeout). Quindi c'e' tutto per fare la prova

Il programma per Android (prendendo come base il seguente link) e' il seguente

AndroidManifest.xml
----------------------------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.luca.innocenti.bttester"
    android:versionCode="1"
    android:versionName="1.0" >
    
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="19" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.luca.innocenti.bttester.MainActivity"
            android:label="@string/app_name" 
            android:theme="@style/Theme.AppCompat">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            
        </activity>
    </application>

</manifest>
----------------------------------------------------------

fragment_main.xml
----------------------------------------------------------
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.luca.innocenti.bttester.MainActivity$PlaceholderFragment" >

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="16dp"
        android:text="Ricerca" />

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/button1"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="90dp"
        android:text=" " />

</RelativeLayout>
----------------------------------------------------------

MainActivity.java
----------------------------------------------------------
package com.luca.innocenti.bttester;

import android.support.v7.app.ActionBarActivity;
import android.support.v4.app.Fragment;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;


public class MainActivity extends ActionBarActivity {
private static TextView rssi_msg;

    private static BluetoothAdapter BTAdapter = BluetoothAdapter.getDefaultAdapter();


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}
        registerReceiver(receiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
         
   }
private final BroadcastReceiver receiver = new BroadcastReceiver(){
        
@Override
public void onReceive(Context context, Intent intent) {
 String action = intent.getAction();
           if(BluetoothDevice.ACTION_FOUND.equals(action)) {
               int rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI,Short.MIN_VALUE);
               String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME);
               
              
               rssi_msg.setText(rssi_msg.getText() + name + " => " + rssi + "dBm\n");
           }
}
    };

@Override
public boolean onCreateOptionsMenu(Menu menu) {

// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {

int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}

/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {


public PlaceholderFragment() {
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container,
false);
rssi_msg = (TextView) rootView.findViewById(R.id.textView1);
Button cerca= (Button) rootView.findViewById(R.id.button1);
cerca.setOnClickListener(new View.OnClickListener() {
          public void onClick(View view) {
               BTAdapter.startDiscovery();
          }
       });
return rootView;
}
}

}
----------------------------------------------------------

A questo punto per completare l'opera e' sufficiente di richiamare la funzione startDiscovery all'interno di un timer e gestire un evento in funzione della qualita' del segnale e del nome dell'emettitore