martedì 15 aprile 2014

Accelerometro su Kinect

Kinect e' fornita di un accelerometro a 3 assi che secondo quando riportato da IFixIt dovrebbe essere un modello KXSD9 di Kionix



Per leggere i dati dell'accelorometro in Python si puo' usare il seguente script (ripreso da qui)
Attenzione: per funzionare e' necessario utilizzare PyUsb alla versione unstable 1.0 e non quella stable 0.4 che normalmente e' pacchettizzata per Linux
Prima e' quindi necessario scaricare il pacchetto unstable da GitHub
(installarea con il classico python setup.py install)
Lo script e' un demo anche per muovere il motore di tilt del Kinect
------------------------------------------------------
import usb.core
import usb.util
import sys
import time


# find our device
dev = usb.core.find(idVendor=0x045e, idProduct=0x02B0)
# was it found?
if dev is None:
    raise ValueError('Device not found')
for cfg in dev:
    sys.stdout.write("Configuration #"+str(cfg.bConfigurationValue) + '\n')
    for intf in cfg:
        sys.stdout.write('\tInterface #' + \
                         str(intf.bInterfaceNumber) + \
                         '\t, Alternate setting ' + \
                         str(intf.bAlternateSetting) + \
                         '\n')
        sys.stdout.write("\tEndpoints:\n")
        for ep in intf:
            sys.stdout.write('\t\t' + \
                             str(ep.bEndpointAddress) + \
                             '\n')


# set the active configuration. With no arguments, the first
# configuration will be the active one
dev.set_configuration()

# (bmRequestType, bmRequestType, bmRequest, wValue, wIndex, nBytes)
ret = dev.ctrl_transfer(0xC0, 0x10, 0x0, 0x0, 1)

print hex(ret[0])   # should return 0x22 but dont know why ?
# ???
ret = dev.ctrl_transfer(0x40, 0x6, 0x1, 0x0, [])

time.sleep(1.5)
# head up!
ret = dev.ctrl_transfer(0x40, 0x31, 0xfff0, 0x0, [])
time.sleep(1.5)
# bring head down
ret = dev.ctrl_transfer(0x40, 0x31, 0xffd0, 0x0, [])
time.sleep(1.5)
# up!
ret = dev.ctrl_transfer(0x40, 0x31, 0xfff0, 0x0, [])
time.sleep(1.5)
# down!
ret = dev.ctrl_transfer(0x40, 0x31, 0xffd0, 0x0, [])
time.sleep(1.5)
# up!
ret = dev.ctrl_transfer(0x40, 0x31, 0xfff0, 0x0, [])


while True:
    # Get accel data
    ret = dev.ctrl_transfer(0xC0, 0x32, 0x0, 0x0, 10)
    #print map(hex, ret)
    # bytes 0 & 1 are always zero
    x = (ret[2] << 8) | ret[3]
    x = (x + 2**15) % 2**16 - 2**15     # convert to signed 16b
    y = (ret[4] << 8) | ret[5]
    y = (y + 2**15) % 2**16 - 2**15     # convert to signed 16b
    z = (ret[6] << 8) | ret[7]
    z = (z + 2**15) % 2**16 - 2**15     # convert to signed 16b
    print x, "\t", y, "\t", z
    time.sleep(0.5)
------------------------------------------------------
(attenzione l'asse x dello script in  Python coincide con l'asse Z della codifica Microsoft mentre l'asse z dello script in Python corrisponde con quello X della codifica Microsoft)

Leggendo i dati si ha che non e' presente un sensore di azimuth per cui non e' possibile avere una orientazione nello spazio kinect. In pratica non e' possibile distinguere la rotazione sull'asse Y

Sito Microsoft


L'asse Z Microsoft mostra la rotazione destra/sinistra (mettendo di fronte al kinect appoggiato sul tavolo) e ruotando di 90° verso sinistra mostra valori positivi di 858 unita' mentre ruotando a destra di -797 unita'

L'asse X mostra la rotazione con valori di +779 unita' per posizione a 90° con i sensori che guardano lo zenith mentre di -865 unita' con i sensori che guardano nadir

Se si continua la rotazione oltre i 90° i valori tornano a decrescere verso il valore 0. Per capire se il sensore sta guardando avanti od indietro (ovvero se e' appoggiato alla sua basetta od e' appeso alla sua basetta) vengono usati i valori dell'asse Y Microsoft (y Python) che sono positivi se il Kinect guarda avanti e negativi se guarda indietro

i valori riportati sono espressi come accelerazioni in funzione dell'accelerazione di gravita'
Per calcolare l'accelerazione reale misurata la regola sembra essere
(misura/819)*9.18 m/sec*sec

Sembra quindi che i valori letti debbano essere confinati tra 0 ed 819 mentre le mie letture eccedono questo valore. Se si calcola il valore medio tra la misura maggiore e minore si ha che
asse Z (858+797)/2 = 827
asse X (779+865)/2=822
quindi considerando l'incertezza (vedi sotto) c'e' solo da calcolare un piccolo offset dello zero ed i valori coincidono con quelli di fabbrica

Mantenendo fermo il kinect ho effettuato oltre 14200 misure ed ho ottenuto una deviazione standard dei dati nei vari assi come segue
x = +/- 6.9 unita'
y = +/- 7.45 unita'
z = +/- 21.7 unita'

Per riportare i valori di inclinazione in gradi si puo' usare il seguente calcolo
-------------------------------------------------------------------------
pitch = math.atan2(y,x)*(180/3.1415926)
roll = math.atan2(y,math.sqrt((x*x)+(z*z)))*(180/3.1415926)
if (z <0):
roll = 90+(90-roll)
-------------------------------------------------------------------------
Di seguito una immagine con l'orientazione ed il valore degli angoli in relazione a diverse orientazioni


RGB con PyOpenNi Kinect

Non si tratta di una funzione molto bene documentata (anche perche' non presente nella cartella degli esempi) ma con PyOpenNi e' possibile acquisire immagini RBG da Kinect





il codice di riferimento (sostanzialmente autoesplicativo) e' il seguente
----------------------------------------------

from openni import *
from PIL import Image

ctx = Context()
ctx.init()

depth = DepthGenerator()
rgb = ImageGenerator()
depth.create(ctx)
rgb.create(ctx)

depth.set_resolution_preset(RES_VGA)
depth.fps = 30

rgb.set_resolution_preset(RES_VGA)
rgb.fps = 30

ctx.start_generating_all()

ctx.wait_one_update_all(rgb)
im = Image.fromstring('RGB',(640,480),rgb.get_raw_image_map())
im.save("rgb.jpg")

ctx.wait_one_update_all(depth)
de = Image.fromstring('L',(640,480),depth.get_raw_depth_map_8())
de.save("depth.jpg")

Kinect con SimpleCV


Un metodo alternatico a PyOpenNi per interagire con Kinect da Python e' utilizzare SimpleCV


Per l'installazione si puo' procedere come segue

sudo apt-get install libopencv-*
sudo apt-get isntall python-opencv
sudo apt-get install python-numpy
sudo apt-get install python-pygame
sudo apt-get install python-setuptools


si scarica la libreria Simple CV di GitHub al seguente link e si installa com
python setup.py install


se tutto e' andato a buon fine sono sufficiente le poche righe sottostanti per avere l'acquisizione dell'immagine RGB e di profondita' da Kinect

------------------------------------------------------
from SimpleCV import *

cam=Kinect()


depth = cam.getDepth()
depth.save('depth.jpg')
rgb = cam.getImage()
rgb.save('rgb.jpg')

Allineamento RGB/IR su Kinect

Sovrapponendo le immagini RGB e di profondita' di Kinect si vede chiaramente, indipendentemente se l'oggetto e' in primo piano o sullo sfondo


Leggendo questo link, si osserva che i parametri di calibrazioni delle camere sono codificate (e diversi) all'interno di ogni Kinect in funzione di come sono state montate e calibrate in fabbrica
Usando la versione completa di OpenNi (o l'SDK Microsoft) sono presenti specifiche funzioni che effettuano la fusione delle immagini dai due sensori come depth.GetAlternativeViewPointCap().SetViewPoint(image);

lunedì 14 aprile 2014

Controllo remoto su Linux con TeamViewer


Di solito quando installo software di controllo remoto su Windows uso LogMeIn ma in questo caso dovevo utilizzare un client Linux e la procedura di uso di Hamachi per LogMeIn non e' proprio lineare, cosi' ho provato ad utilizzare TeamViewer

Il software si scarica direttamente in formato .deb dall'indirizzo
http://www.teamviewer.com/it/download/linux.aspx

e si installa con il solito dpkg -i nome.deb

A questo punto da linea di comando si lancia il comando
./teamview

viene richiesto un account (che corrisponde ad una e-mail ed una password)


in seguito si imposta la password specifica per il computer client. Sono previsti codici che consentire il controllo remote anche ad altri utenti


La cosa carina e' che al successivo riavvio Teamview viene caricato automaticamente senza modificare nessun script di boot

Per controllare la macchina Linux ho scelto di usare un Mac. La procedura di installazione e' semplicissima e l'interfaccia grafica e' identica a quella per Linux
Una volta loggati vengono mostrate le macchine client accese e facendo doppio clic ci si collega


A differenza di LogMeIn non si utilizza il browser (almeno non in modo esplicito) e non e' richiesta l'installazione di nessun plugin dato che il lavoro viene fatto tutto dall'applicazione scaricata  all'inizio 

E' anche disponibile un client Android anche se la sua usabilita' non e' molto buona




venerdì 11 aprile 2014

3D Scanner con Kinect

Per completare i testi con il Kinect ho provato a creare uno scanner 3D.
L'oggetto e' stata la caffettiera sotto riportata il cui centro di rotazione e' stato posto a 65 cm dal Kinect ed e' stato ripreso da 4 fotogrammi ruotando ogni volta  l'oggetto di 90°
Ovviamente e' stata una prova casalinga e ci sono forti errori di allineamento tra il sensore Kinect e l'asse di rotazione.

L'oggetto reale


La sua scansione (da notare che era posto sopra una scatola)


Per l'acquisizione e' stato usato il seguente script
-------------------------------------------------
from openni import *
import time
import pickle
import numpy as np
import Image
import scipy


ctx = Context()
ctx.init()

# Create a depth generator
depth = DepthGenerator()
depth.create(ctx)

# Set it to VGA maps at 30 FPS
depth.set_resolution_preset(RES_VGA)
depth.fps = 30

# Start generating
ctx.start_generating_all()


# Update to next frame
nRetVal = ctx.wait_one_update_all(depth)

depthMap = depth.map
depthMap2 = np.array(depthMap)
f = open("ca_2700.txt","w+b")
pickle.dump(depthMap2,f)
f.close()
-------------------------------------------------

i vari file salvati con pickle sono stati poi trattati con i seguenti programmi (uno ogni 90°, di fatto l'immagine viene centrata e vengono filtrati solo i dati > 0 ed inferiori a 70 cm)
--------------------------------
#!/usr/bin/python
import pickle
import numpy as np
import Image

f = open("ca_000.txt")
data = pickle.load(f)
f.close()

t = 0

for y in range(0,480):
for x in range (0,640):
if ((data[t] < 700) and (data[t]>0)):
print str(x-320)+","+str(y-240)+","+str(660-data[t])
t = t + 1
--------------------------------
#!/usr/bin/python
import pickle
import numpy as np
import Image

f = open("ca_180.txt")
data = pickle.load(f)
f.close()

t = 0

for y in range(0,480):
for x in range (0,640):
if ((data[t] < 700) and (data[t]>0)):
print str(x-320)+","+str(y-240)+","+str(-(660-data[t]))
t = t + 1
--------------------------------
#!/usr/bin/python
import pickle
import numpy as np
import Image

f = open("ca_090.txt")
data = pickle.load(f)
f.close()

t = 0

for y in range(0,480):
for x in range (0,640):
if ((data[t] < 700) and (data[t]>0)):
print str(660-data[t]) + "," + str(y-240) + "," + str(x-320) #non funziona 
t = t + 1
--------------------------------
#!/usr/bin/python
import pickle
import numpy as np
import Image

f = open("ca_090.txt")
data = pickle.load(f)
f.close()

t = 0

for y in range(0,480):
for x in range (0,640):
if ((data[t] < 700) and (data[t]>0)):
print str(-(660-data[t])) + "," + str(y-240) + "," + str(x-320)  
t = t + 1
--------------------------------

L'output e' stato dirottato su file testo che poi sono importati in Meshlab.

giovedì 10 aprile 2014

ISS Spotting - 10 aprile 2014 20:36

A distanza di quasi un anno, viste le condizioni favorevoli, ho provato di nuovo a fotografare ISS mentre passa sopra Caldine
Nel tentativo precedente i risultati erano stati scarsini piu' che altro per l'uso di una fotocamera compatta mentre questa volta ho impiegato una reflex digitale (con un cavalletto che lascia un po' a desiderare in rapporto al peso della fotocamera)

La magnitudo prevista di -3.3 era perfetta per l'osservazione, di contro l'ora del passaggio era subito dopo il tramonto (si vede chiaramente che ancora non e' arrivata la notte sull'orizzonte)

Scia di ISS (tempo di scatto 4 sec)

Situazione virtuale da Stellarium


ISS che passa in prossimita' di Beltegeuse

ISS che scompare sull'orizzonte Nord

Orione da casa



Sensore di Prossimita' con WiFi

Avevo sentito parlare del fatto che in alcuni centri commerciale veniva effettuatao il tracking dei clienti leggendo in modo passivo il MAC Address dei telefoni anche quando questi non sono associati ad un access point. Francamente non pensavo che si potesse fare o meglio che non si potesse fare in modo semplice

In realta' usando Airmon ed una antenna WiFi adeguata la cosa e' piuttosto semplice

Kali Linux


Per la prova ho lanciato in macchina virtuale Kali Linux collegando una scheda WiFi USB Alpha (basata su RTL8187)

in seguito e' sufficiente inserire i due seguenti comandi
airmon-ng start wlan0
airodump-ng mon0

per visulalizzare i Mac Address di tutti i dispositivi presenti nel raggio d'azione dell'antenna (nella parte superiore gli access point, in quella inferiore i client). Due dei dispositivi tracciati sono i miei due telefoni di prova
Semplice e banale e c'e' anche la possibilita' di effettuare un log su file di testo

L'unica cosa che mi rimane un po' dubbia e' il perche' il dispositivo mobile venga agganciato praticamente in tempo reale (nel momento in cui viene accesa l'antenna) mentre compare per lungo tempo in lista anche quando l'antenna viene spenta

Questo tipo di sistema puo' essere utilizzato come sistema di prossimita' basato sul WiFi

mercoledì 9 aprile 2014

Proximity Sensor con Bluetooth 2 in Android (2)

Questo e' il seguito del precedente post
Qui e' stato inserito un timer che ogni 10 secondi effettua un discovery dei dispositivi bluetooth vicini e se il dispositivo ha un certo nome e se il suo segnale' sufficientemente forte mostra una determinata pagina html su una WebView

Link al codice sorgente


MainActivity.java (evidenziate in giallo le linee di codice piu' interessanti)
----------------------------------------------------
package com.luca.innocenti.btproximity;

import java.util.Timer;
import java.util.TimerTask;

import android.support.v7.app.ActionBarActivity;
import android.support.v4.app.Fragment;
import android.annotation.SuppressLint;
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.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebChromeClient;
import android.webkit.WebView;


@SuppressLint("SetJavaScriptEnabled")
public class MainActivity extends ActionBarActivity {
    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));


Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
     public void run() {
     runOnUiThread(new Runnable() {
      @Override
      public void run() {
      BTAdapter.startDiscovery();
     
      }
    
      });
      }
      }, 0, 10000);
    }


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);
                if (name.equals("GT") && (rssi >= -50))
                {
                html5.loadUrl("file:///android_asset/www/1.html");
                }
                
                if (name.equals("GT2") && (rssi >= -50))
                {
                html5.loadUrl("file:///android_asset/www/2.html");
                }

            }
           
  
  
 }
   };
public static WebView html5;

@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) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
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);

html5 = (WebView) rootView.findViewById(R.id.webView1);
html5.getSettings().setJavaScriptEnabled(true);
html5.setWebChromeClient(new WebChromeClient());
html5.loadUrl("file:///android_asset/www/index.html");
return rootView;
}
}

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

Manifest
----------------------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.luca.innocenti.btproximity"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="19" />
    
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.luca.innocenti.btproximity.MainActivity"
            android:label="@string/app_name" >
            <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.btproximity.MainActivity$PlaceholderFragment" >

    <WebView
        android:id="@+id/webView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true" />

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

martedì 8 aprile 2014

Accuratezza del sensore di distanza di Kinect (2)




Per cercare di studiare un po' di piu' la precisione del Kinect ho effettuato due serie di misure a distanza fissa e una serie di misure a distanze progressive (misurate con stecca centimetrata)

Ad una distanza di  circa 60 cm (la distanza del kinect dal bersaglio e' approssimata in quanto non e' semplice misurare la reale distanza tra il sensore ed il bersaglio a meno di non smontare il dispositivo) e' emerso che da 436 misure la distanza media misurata e' stata di 602.1 mm con una standard deviation di +- 0.56 mm

Ad una distanza di  circa 73 cm  e' emerso che da 325 misure la distanza media misurata e' stata di 728.6 mm con una standard deviation di +- 0.52 mm

Le cose cambiano in modo sensibile se si punta un bersaglio fisso distante (maggiore di 2 m)
In questo caso, come si osserva nel grafico sottostante, su oltre 1100 misure, i dati si sono distribuiti in due grandi classi (2252 mm e 2206 mm). L'incertezza quindi sale a 5 cm






In seguito il bersaglio e' stato spostato su una stecca centimetrata da circa 60 a circa 73 cm di distanza (per evitare errori sono state considerate solo le distanze relative partendo dal punto di partenza)

i risultati sono buoni ma non eccezionali e comunque il linea  con quanto dichiarato a questo link

Range: ~ 50 cm to 5 m. Can get closer (~ 40 cm) in parts, but can't have the full view be < 50 cm.
Horizontal Resolution: 640 x 480 and 45 degrees vertical FOV and 58 degrees horizontal FOV. Simple geometry shows is about ~ 0.75 mm per pixel x by y at 50 cm, and ~ 3 mm per pixel x by y at 2 m.
Depth resolution: ~ 1.5 mm at 50 cm. About 5 cm at 5 m.

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


NullPointerException su OnCreate di Android

Per molto tempo sono stato abituato a dichiarare i widget di Android direttamente nella funzione OnCreate con una sintassi di questo tipo


-------------------------------------------
public void onCreate(Bundle savedInstanceState) { 
super.onCreate(savedInstanceState); 
 setContentView(R.layout.activity_main); 

Button cerca = (Button) findViewById(R.id.button1); 
cerca.setOnClickListener(new OnClickListener(){ 
                      public void onClick(View v) { 
                             //fai qualcosa
                        } 
                    });
 }
-------------------------------------------

con le nuove modifiche questa sintassi crea un NullPointerException (senza molte altre spiegazioni)
Adesso invece si le dichiarazioni devono essere spostati in OnCreateView()
--------------------------------------------
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container,false);
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) {
               //fai qualcosa;
          }
       });
return rootView;
}
--------------------------------------------
Per rendere visibile a tutto il programma la TextView si puo' dichiarare come static nella MainActivity

--------------------------------------------
public class MainActivity extends ActionBarActivity {
private static TextView rssi_msg;
--------------------------------------------

Un altro aspetto che e' cambiato nel modo di programmare Android e' la presenza di due file di layout (ovvero il classico activity_main.xml e fragment_main.xml). I componenti visivi devono essere posti su fragment_main.xml

Per cambiare il tema dell'applicazione si puo' modificare il file AndroidManifest.xml come
--------------------------------------------
        <activity
            android:name="com.luca.innocenti.bttester.MainActivity"
            android:label="@string/app_name" 
            android:theme="@style/Theme.AppCompat">

MTP per Android

Un piccolo riassunto per salvare e copiare file dai dispositivi Android che usano il protocollo MTP per la connessione via USB



Windows
per collegarsi via MTP e' necessario avere l'apposito driver Media Transfer Protocol Porting Kit che si scarica da questo link

Mac OS X
Per Mac si usa il programma Android File Transfer che si scarica da questo link


Linux (Debian/Ubuntu)
Per Linux ovviamente le cose si complicano
Sotto Ubuntu 12.04 quando si connette un dispositivo Android MTP (come Nexus 7 2012) questo compare nel file manager, si riesce a sfogliare il contenuto ma tutti i tentativi di copiare file sono inutili

Alcuni hanno trovato soluzione installando gmtp ma per me e' stato un insuccesso
La soluzione invece e' derivata dall'impiego di go-mtpfs (github)
Per Ubuntu c'e' una soluzione piu' semplice

sudo add-apt-repository ppa:webupd8team/unstable
sudo apt-get update
sudo apt-get install go-mtpfs
sudo groupadd fusesudo gpasswd -a luca fuse

a questo punto si riavvia e si puo' accedere all'unita' MTP montandola come segue
go-mtpfs /home/luca/nexus7

per la cronaca il sistema funziona perfettamente da linea di comando (per esempio da Midnight Commander) ma ha problemi a copiare trascinando le icone dal file manager di Gnome

LLama3 Anita

A seguito di questo post ho provato a vedere ho provato a vedere cosa accadeva ad utilizzare un modello specifico per la lingua italiana in...