Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Acquisire dati dai sensori

Link copiato negli appunti

La maggior parte dei PC e la quasi totalità dei tablet sono dotati di numerosi sensori, come accelerometri, giroscopi, bussole, ricevitori GPS, sensori di prossimità, ecc. In passato l'intarazione con questi sensori era tutt'altro che semplice, poiché occorreva gestire i vari driver da codice o tramite librerie esterne, magari recuperate dai vari produttori hardware, nonché gestirne le varie versioni e compatibilità con gli ambienti di sviluppo.

Il Windows Runtime mette a disposizione degli sviluppatori due comodi set di API (corrispondenti a due diversi namespace) che semplificano notevolmente l'interazione con i sensori. Questo grazie al fatto che tutte le API che vedremo in questo articolo poggiano sulla Windows Sensor and Location Platform. La piattaforma espone due namespace per le applicazioni Windows Store:

  • il namespace Windows.Devices.Sensors, che supporta i sensori di movimento (come l'accelerometro), di orientamento del device nello spazio (come la bussola e l'inclinometro), sensori di luminosità, ecc.;
  • il namespace Windows.Devices.Geolocation, che consente di recuperare la posizione del device tramite un cosiddetto "location provider" (per maggiori dettagli sulla piattaforma, si rinvia alla documentazione su MSDN).

In questo articolo ci occuperemo di esplorare le API del namespace Windows.Devices.Sensors, mentre le API che consentono di determinare la posizione dell'utente saranno oggetto di uno specifico articolo.

Il namespace Windows.Devices.Sensors

Il namespace Windows.Devices.Sensors mette a disposizione classi, metodi e tipi per accedere ai vari sensori eventualmente integrati nei device con Windows 8. Ciascun sensore può essere utilizzato tramite semplici API che seguono grossomodo lo stesso identico pattern, con alcune variazioni.

Per prima cosa, occorre ottenere una reference alla classe che incapsula lo specifico sensore da usare (come le classi Accelerometer, Gyrometer, ecc.) tramite il metodo statico GetDefault, esposto dalla classe stessa. A questo punto puoi scegliere tra due strategie: usare il polling per interrogare il device a intervalli regolari, oppure abbonarti all'evento ReadingChanged per essere notificato di ogni cambiamento nella lettura dei sensori. Vedremo entrambe queste strategie in azione nelle prossime pagine.

Reagire ai movimenti del device con l'accelerometro

Il sensore di accelerazione, o accelerometro, misura l'accelerazione trasmessa a un dispositivo lungo i suoi tre assi (X, Y, and Z), vale a dire la variazione della velocità nell'unità di tempo. La prossima immagine mostra l'orientamento dei tre assi in un tablet e in un notebook.



(Fonte: http://msdn.microsoft.com/en-us/library/windows/apps/jj155767.aspx)

Per utilizzare l'accelerometro da una Windows Store app in XAML/C#, la prima cosa da fare è, come si è accennato, recuperare una reference ad un oggetto di tipo Accelerometer, ossia la classe che incapsula la logica del relativo sensore. Per fare questo, è sufficiente invocare il metodo statico GetDefault della classe Accelerometer, come mostrato nel prossimo snippet:

Accelerometer accelerometer = Accelerometer.GetDefault();
if (accelerometer == null)
{
	// Sensore non trovato
}

Il metodo GetDefault restituisce un valore solo se il sensore viene trovato, altrimenti restituisce null. È pertanto importante controllare il valore restituito prima di qualunque operazione sul sensore.

Per testare i sensore, potete usare il seguente codice XAML come riferimento per la vostra pagina MainPage.xaml.

<Page x:Class="Demo.Html.it.SensorSample.CS.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	  xmlns:local="using:Demo.Html.it.SensorSample.CS"
	  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
	  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
	  mc:Ignorable="d">
	<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
		<TextBlock x:Name="SensorResult" Width="Auto" Height="Auto" Margin="20" FontSize="22" />
	</Grid>
</Page>

Il seguente listato mostra come inizializzare il sensore.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Devices.Sensors;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace Demo.Html.it.SensorSample.CS
{
	/// <summary>
	/// An empty page that can be used on its own or navigated to within a Frame.
	/// </summary>
	public sealed partial class MainPage : Page
	{
		public MainPage()
		{
			this.InitializeComponent();
			this.InitializeAccelerometer();
		}
		private Accelerometer _accelerometer;
		private void InitializeAccelerometer()
		{
			this._accelerometer = Accelerometer.GetDefault();
			if (_accelerometer == null)
			{
				SensorResult.Text = "Sensore non trovato.";
				return;
			}
			uint minReportInterval = _accelerometer.MinimumReportInterval;
			var desiredReportInterval = minReportInterval > 16 ? minReportInterval : 16;
			this._accelerometer.ReportInterval = desiredReportInterval;
		}
	}
}

La proprietà ReportInterval indica l'intervallo di tempo (in millisecondi) tra due successive letture del sensore. La proprietà dovrebbe essere impostata a un valore diverso dallo zero prima di registrare l'handler dell'evento ReadingChanged o chiamare il metodo GetCurrentReading, in modo da permettere al sensore (tramite i relativi driver) di allocare le risorse necessarie per poter soddisfare le richieste.

Per migliorare l'efficienza e diminuire così il consumo della batteria, il valore questa proprietà dovrebbe comunque essere impostato nuovamente a zero quando non è più necessario utilizzare il sensore. Il prossimo snippet illustra questo punto (lo stesso principio si applica anche agli altri sensori discussi in questo articolo).

protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
	if (this._accelerometer != null)
	this._accelerometer.ReportInterval = 0;
	base.OnNavigatingFrom(e);
}

Prima di impostare l'intervallo, è bene tuttavia accertarsi che il sensore sia in grado di soddisfare la richiesta di intervallo tramite la proprietà MinimumReportInterval, altrimenti potremmo incorrere in un'eccezione o in risultati inaspettati.

Il codice visto sopra si accerta che il valore impostato non sia mai inferiore ai 16 millisecondi.

In ogni caso, teniamo presente che, anche se impostiamo l'intervallo tra due letture a un valore valido, il sensore è comunque libero di utilizzare un valore diverso, sulla base alla propria logica di funzionamento interno. Inoltre, maggiore è l'intervallo di tempo tra due successive letture, e maggiore sarà la forza necessaria a scatenare (trigger) una nuova lettura da parte del sensore (si rinvia alla documentazione MSDN per i dettagli).

Ora che il sensore è stato inizializzato, esistono tre modi per leggere i dati dall'accelerometro: sottoscrivere l'evento ReadingChanged e attendere fino a che una nuova lettura diviene disponibile, interrogare il sensore a intervalli regolari, oppure attendere che il device venga "scosso" (shaken).

I primi due pattern (quello event-based e quello basato sul polling) sono gli stessi per tutti i sensori discussi in questo articolo (con lievi variazioni), mentre l'ultimo, basato su un evento specifico della classe Accelerometer (l'evento di Shaken, appunto), è supportato unicamente dal sensore di accelerazione.

Il prossimo snippet mostra un esempio di come sfruttare l'evento ReadingChanged per attendere la disponibilità di una nuova lettura.

private Accelerometer _accelerometer;
private void InitializeAccelerometer()
{
	this._accelerometer = Accelerometer.GetDefault();
	if (_accelerometer == null)
	{
		SensorResult.Text = "Sensore non trovato.";
		return;
	}
	uint minReportInterval = _accelerometer.MinimumReportInterval;
	var desiredReportInterval = minReportInterval > 16 ? minReportInterval : 16;
	this._accelerometer.ReportInterval = desiredReportInterval;
	this._accelerometer.ReadingChanged += Accelerometer_ReadingChanged;
}
privateasyncvoid Accelerometer_ReadingChanged(Accelerometer sender, AccelerometerReadingChangedEventArgs args)
{
	if (args.Reading != null)
	{
		await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
		{
			SensorResult.Text = String.Format("X: {0,5:0.00} - Y: {1,5:0.00} - Z: {2,5:0.00} - 	Timestamp: {3}",
				args.Reading.AccelerationX,
				args.Reading.AccelerationY,
				args.Reading.AccelerationZ,
				args.Reading.Timestamp);
		});
	}
}

Nell'handler dell'evento ReadingChanged è possibile ispezionare la proprietà Reading (di tipo AccelerationReading) esposta dall'istanza di tipo AccelerometerReadingChangedEventArgs ricevuta come parametro dall'handler per recuperare i valori che rappresentano l'accelerazione lungo i tre assi. Questi valori sono incapsulati nelle proprietà AccelerationX, AccelerationY e AccelerationZ, mentre la proprietà Timestamp, come il nome suggerisce, indica il momento in cui il sensore ha registrato quei valori.

L'altro modo di recuperare i dati provenienti dal sensore è quello di interrogare (poll) direttamente il sensore stesso. Per far questo, è necessario valorizzare la proprietà ReportInterval, e invocare il metodo GetCurrentReading a intervalli regolari (l'esempio utilizza a tal fine la classe DispatcherTimer), come mostrato nel seguente listato.

private Accelerometer _accelerometer;
private DispatcherTimer _timer;
private void InitializeAccelerometer()
{
	this._accelerometer = Accelerometer.GetDefault();
	if (_accelerometer == null)
	{
		SensorResult.Text = "Sensore non trovato.";
		return;
	}
	uint minReportInterval = _accelerometer.MinimumReportInterval;
	var desiredReportInterval = minReportInterval > 16 ? minReportInterval : 16;
	this._accelerometer.ReportInterval = desiredReportInterval;
	this._timer = newDispatcherTimer();
	this._timer.Tick += PollAccelerometerSensorReadings;
	this._timer.Interval = new TimeSpan(0, 0, 0, 0, (Int32)desiredReportInterval);
	this._timer.Start();
}
privateasyncvoid PollAccelerometerSensorReadings(object sender, object e)
{
	if (this._accelerometer != null)
	{
		var readings = this._accelerometer.GetCurrentReading();
		await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
		{
			SensorResult.Text = String.Format("X: {0,5:0.00} - Y: {1,5:0.00} - Z: {2,5:0.00} - Timestamp: {3}",
				readings.AccelerationX,
				readings.AccelerationY,
				readings.AccelerationZ,
				readings.Timestamp);
		});
	}
}

Come abbiamo già accennato, la classe Accelerometer espone anche una terza, e specifica opzione per determinare i movimenti dell'utente. Questa classe espone infatti un evento Shaken che viene sollevato ogni volta che l'utente "scuote" il device. Si tratta di un evento che non offre alcuna lettura dell'accelerazione lungo i tre assi, in quanto i calcoli per determinare l'entità dello scuotimento del device sono affidati per intero a WinRT (tramite i driver del sensore). È da notare come, in questo caso, non è necessario impostare la proprietà ReportInterval.

Il prossimo listato mostra un esempio

private void InitializeAccelerometer()
{
	this._accelerometer = Accelerometer.GetDefault();
	if (_accelerometer == null)
	{
		SensorResult.Text = "Sensore non trovato.";
		return;
	}
	this._accelerometer.Shaken += Accelerometer_Shaken;
}
private Int32 _numberOfShakens = 0;
privateasyncvoid Accelerometer_Shaken(Accelerometer sender, AccelerometerShakenEventArgs args)
{
	await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
	{
		this._numberOfShakens++;
		SensorResult.Text = String.Format("Numero di shake: {0}", this._numberOfShakens);
	});
}

Misurare la velocità angolare con il girometro

Il girometro misura la velocità angolare lungo i tre assi. Il suo impiego è molto simile al sensore di accelerazione: per prima cosa, occorre recuperare una reference a un oggetto di tipo Gyrometer tramite il metodo GetDefault della classe stessa. Quindi puoi abbonarti all'evento ReadingChanged per essere notificato di ogni cambiamento nella lettura dei dati, oppure effettuare il polling sul sensore tramite il metodo GetCurrentReading.

Per testare questo sensore possiamo usare il codice XAML presentato in precedenza.

Il seguente listato mostra come recuperare i dati letti dal sensore tramite l'evento ReadingChanged:

public MainPage()
{
	this.InitializeComponent();
	this.InitializeGyrometer();
}
private Gyrometer _gyro;
private void InitializeGyrometer()
{
	this._gyro = Gyrometer.GetDefault();
	if (this._gyro == null)
	{
		SensorResult.Text = "Sensore non trovato.";
		return;
	}
	uint minReportInterval = _gyro.MinimumReportInterval;
	var desiredReportInterval = minReportInterval > 16 ? minReportInterval : 16;
	this._gyro.ReportInterval = desiredReportInterval;
	this._gyro.ReadingChanged += Gyro_ReadingChanged;
}
private async void Gyro_ReadingChanged(Gyrometer sender, GyrometerReadingChangedEventArgs args)
{
	if (args.Reading != null)
	{
		await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
		{
			SensorResult.Text = String.Format("X: {0,5:0.00} - Y: {1,5:0.00} - Z: {2,5:0.00} - Timestamp: {3}",
				args.Reading.AngularVelocityX,
				args.Reading.AngularVelocityY,
				args.Reading.AngularVelocityZ,
				args.Reading.Timestamp);
		});
	}
}
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
	if (this._gyro != null)
	this._gyro.ReportInterval = 0;
	base.OnNavigatingFrom(e);
}

In alternativa, possiamo interrogare il sensore a intervalli regolari, come mostrato nel prossimo snippet. Anche in questo caso, il pattern seguito è praticamente identico a quello usato in precedenza con l'accelerometro.

private Gyrometer _gyro;
privateDispatcherTimer _timer;
private void InitializeGyrometer()
{
	this._gyro = Gyrometer.GetDefault();
	if (this._gyro == null)
	{
		SensorResult.Text = "Sensore non trovato.";
		return;
	}
	uint minReportInterval = _gyro.MinimumReportInterval;
	var desiredReportInterval = minReportInterval > 16 ? minReportInterval : 16;
	this._gyro.ReportInterval = desiredReportInterval;
	this._timer = newDispatcherTimer();
	this._timer.Tick += PollGyrometerSensorReadings;
	this._timer.Interval = new TimeSpan(0, 0, 0, 0, (Int32)desiredReportInterval);
	this._timer.Start();
}
privateasyncvoid PollGyrometerSensorReadings(object sender, object e)
{
	if (this._gyro != null)
	{
		var readings = this._gyro.GetCurrentReading();
		await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
		{
			SensorResult.Text = String.Format("X: {0,5:0.00} - Y: {1,5:0.00} - Z: {2,5:0.00} - Timestamp: {3}",
				readings.AngularVelocityX,
				readings.AngularVelocityY,
				readings.AngularVelocityZ,
				readings.Timestamp);
		});
}
}

Utilizzare il sensore bussola per determinare l'orientamento del device

Il sensore bussola (compass) indica l'orientamento espresso in gradi rispetto al nord magnetico e, se l'implementazione lo prevede, anche rispetto al nord geografico (detto anche "true north").

Possiamo usare il seguente codice XAML per testare il codice proposto in questo paragrafo:

<Page x:Class="Demo.Html.it.SensorSample.CS.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Demo.Html.it.SensorSample.CS" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
	<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
		<StackPanel>
			<TextBlock x:Name="MagneticNorthTextBlock" Width="Auto" Height="Auto" Margin="20" FontSize="20" />
			<TextBlock x:Name="TrueNorthTextBlock" Width="Auto" Height="Auto" Margin="20" FontSize="20" />
		</StackPanel>
	</Grid>
</Page>

Come per ogni altro sensore, anche in questo caso il primo passo consiste nel recuperare la reference alla classe che incapsula la logica interna del sensore, tramite il metodo statico GetDefault della classe Compass. Quindi occorre impostare la proprietà ReportInterval per permettere al sistema di allocare le risorse necessarie.

Dopo aver inizializzato il sensore, per recuperare i dati possiamo scegliere tra le due ormai note strategie: l'uso dell'evento ReadingChanged o il polling del sensore.

Il prossimo esempio mostra come usare la prima strategia per recuperare il valore (in gradi) della proprietà HeadingMagneticNorth e, dove disponibile, della proprietà HeadingTrueNorth, esposte dall'oggetto di tipo CompassReading ricevuto dall'handler dell'evento ReadingChanged.

public MainPage()
{
	this.InitializeComponent();
	this.InitializeCompass();
}
private Compass _compass;
private void InitializeCompass()
{
	this._compass = Compass.GetDefault();
	if (this._compass == null)
	{
		MagneticNorthTextBlock.Text = "Sensore non trovato.";
		return;
	}
	uint minReportInterval = this._compass.MinimumReportInterval;
	var desiredReportInterval = minReportInterval > 16 ? minReportInterval : 16;
	this._compass.ReportInterval = desiredReportInterval;
	this._compass.ReadingChanged += Compass_ReadingChanged;
}
private async void Compass_ReadingChanged(Compass sender,
CompassReadingChangedEventArgs args)
{
	if (args.Reading != null)
	{
		await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
		{
			MagneticNorthTextBlock.Text = String.Format("Nord magnetico: {0,5:0.00} gradi.", args.Reading.HeadingMagneticNorth);
			if (args.Reading.HeadingTrueNorth != null)
				TrueNorthTextBlock.Text = String.Format("Nord geografico (true north): {0,5:0.00} gradi.", args.Reading.HeadingTrueNorth);
			else
				TrueNorthTextBlock.Text = String.Format("Nord geografico (true north): nessun dato disponibile");
		});
	}
}

Dal momento che non tutti i sensori implementano il nord geografico, è sempre opportuno controllare che la relativa proprietà sia stata valorizzata correttamente, prima di usarla.

Combinare dati differenti tramite il sensore di orientamento

Il sensore di orientamento ("orientation sensor") combina i dati provenienti da tre diversi sensori (accelerometro, girometro e bussola) allo scopo di determinare l'orientamento nello spazio del device. Questo sensore espone due diverse API, rappresentate dalle classi SimpleOrientationSensor e OrientationSensor.

La classe SimpleOrientationSensor permette di determinare in modo semplice e intuitivo l'orientamento attuale del device nello spazio, senza la necessità di effettuare complessi calcoli sui dati provenienti dai vari sensori. L'orientamento del device può infatti assumere uno dei seguenti valori, espressi tramite l'enum SimpleOrientation:

Valore Descrizione
NotRotated Il device non è ruotato. Corrisponde alla posizione "portrait-up".
Rotated90DegreesCounterclockwise Il device è stato ruotato di 90 in senso antiorario. Corrisponde alla posizione "landscape-left".
Rotated180DegreesCounterclockwise Il device è stato ruotato di 180 gradi. Corrisponde alla posizione "portrait-down".
Rotated270DegreesCounterclockwise Il device è stato ruotato di 270 gradi in senso antiorario. Corrisponde alla posizione "landscape-right".
Faceup Il device è posizionato "a faccia in su" (face-up); il display è visibile all'utente.
Facedown Il device è posizionato "a faccia in giù" (face-down); il display non è visibile da parte dell'utente.

La classe SimpleOrientationSensor segue gli stessi pattern visti in precedenza, con alcune leggere differenze.

Puoi utilizzare il seguente codice XAML come riferimento per testare la classe SimpleOrientationSensor.

<Page x:Class="Demo.Html.it.SensorSample.CS.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Demo.Html.it.SensorSample.CS"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
	<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
		<StackPanel>
			<TextBlock x:Name="OrientationSensorResult" Width="Auto" Height="Auto" Margin="20" FontSize="22" />
		</StackPanel>
	</Grid>
</Page>

Il codice che segue sfrutta la classe SimpleOrientationSensor per mostrare a video l'orientamento corrente del device.

public MainPage()
{
	this.InitializeComponent();
	this.InitializeSimpleOrientationSensor();
}
private SimpleOrientationSensor _simpleOrientationSensor;
private void InitializeSimpleOrientationSensor()
{
	this._simpleOrientationSensor = SimpleOrientationSensor.GetDefault();
	if (this._simpleOrientationSensor == null)
	{
		OrientationSensorResult.Text = "Sensore non trovato.";
		return;
	}
	this._simpleOrientationSensor.OrientationChanged += SimpleOrientationSensor_OrientationChanged;
}
private async void SimpleOrientationSensor_OrientationChanged(SimpleOrientationSensor sender, SimpleOrientationSensorOrientationChangedEventArgs args)
{
	await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
	{
		switch (args.Orientation)
		{
			case SimpleOrientation.Facedown:
				OrientationSensorResult.Text = "Orientamento corrente: Face-Down";
				break;
			case SimpleOrientation.Faceup:
				OrientationSensorResult.Text = "Orientamento corrente: Face-Up";
				break;
			case SimpleOrientation.NotRotated:
				OrientationSensorResult.Text = "Orientamento corrente: Portrait-Up";
				break;
			case SimpleOrientation.Rotated180DegreesCounterclockwise:
				OrientationSensorResult.Text = "Orientamento corrente: Portrait-Down";
				break;
			case SimpleOrientation.Rotated270DegreesCounterclockwise:
				OrientationSensorResult.Text = "Orientamento corrente: Landscape-Right";
				break;
			case SimpleOrientation.Rotated90DegreesCounterclockwise:
				OrientationSensorResult.Text = "Orientamento corrente: Landscape-Left";
				break;
		}
	});
}

L'API nasconde i meccanismi interni utilizzati per determinare l'orientamento del dispositivo, ottenuto combinando i dati provenienti da tre sensori differenti. Rispetto agli altri sensori discussi in questo articolo, la principale differenza sta nel fatto che, in questo caso, non è necessario impostare la proprietà ReportInterval prima di sottoscrivere l'evento OrientationChanged. L'evento OrientationChanged prende il posto dell'evento ReadingChanged, normalmente usato dagli altri sensori, e viene sollevato dal sistema ogni volta che l'utente ruota il device in una posizione differente. In alternativa, è sempre possibile effettuare il polling sul sensore.

La seconda API a nostra disposizione è rappresentata dalla classe OrientationSensor, la quale permette di accedere direttamente ai dati provenienti dal sensore di orientamento. Questa classe viene normalmente destinata ad applicazioni complesse, come giochi o altre app che hanno bisogno di conoscere con precisione l'orientamento per decidere la vista da mostrare all'utente (come nel caso della cosiddetta "augmented reality"). Il prossimo listato mostra come recuperare i dati dal sensore di orientamento tramite il classico evento ReadingChanged. Da notare come sia necessario, in questo caso, impostare adeguatamente la proprietà ReportInterval prima di abbonarsi all'evento stesso.

private OrientationSensor _orientationSensor;
private void InitializeOrientationSensor()
{
	this._orientationSensor = OrientationSensor.GetDefault();
	if (this._orientationSensor == null)
	{
		// Sensore non trovato
	}
	uint minReportInterval = this._orientationSensor.MinimumReportInterval;
	var desiredReportInterval = minReportInterval > 16 ? minReportInterval : 16;
	this._orientationSensor.ReportInterval = desiredReportInterval;
	this._orientationSensor.ReadingChanged += OrientationSensor_OrientationChanged;
}
private void OrientationSensor_OrientationChanged(OrientationSensor sender, OrientationSensorReadingChangedEventArgs args)
{
	SensorRotationMatrix rotationMatrix = args.Reading.RotationMatrix;
	SensorQuaternion quaternion = args.Reading.Quaternion;
	// aggiusta la vista mostrata a video in base all'orientamento del display
}

Il codice qui illustrato è identico a quello usato per gli altri sensori discussi finora, incluso il nome dell'evento sollevato quando una nuova lettura è disponibile. La proprietà Reading (di tipo OrientationSensorReading) esposta dall'oggetto di tipo OrientationSensorReadingChangedEventArgs ricevuto come parametro dall'event handler incapsula due proprietà: RotationMatrix e Quaternion. Matrici e quaternioni descrivono in termini matematici la rotazione del dispositivo nello spazio. Questi oggetti matematici esulano tuttavia dall'obiettivo di questa guida.

Recuperare i dati provenienti dall'inclinometro

L'inclinometro determina l'angolo di rotazione del dispositivo lungo i tre assi. La rotazione attorno all'asse verticale (Y) è detta anche "roll" (o "gamma"), mentre il termine "pitch" (o "beta") indica la rotazione attorno all'asse laterale (X). Infine, il termine "yaw" (o "alpha") indica la rotazione attorno all'asse longitudinale (Z). La prossima figura mostra l'orientamento dei tre assi in un device con Windows 8.



(Fonte: http://msdn.microsoft.com/en-us/library/windows/apps/jj155767.aspx)

Per testare l'inclinometro, possiamo sfruttare il codice XAML usato in precedenza per provare l'accelerometro.

Il seguente listato mostra un esempio di utilizzo dell'inclinometro che sfrutta il polling per recuperare i dati dal sensore a intervalli regolari e mostrare il valore (in gradi) delle corrispondenti proprietà PitchDegrees, YawDegrees, e RollDegrees properties.

public MainPage()
{
	this.InitializeComponent();
	this.InitializeInclinometer();
}
private Inclinometer _inclinometer;
private DispatcherTimer _timer;
private void InitializeInclinometer()
{
	this._inclinometer = Inclinometer.GetDefault();
	if (this._inclinometer == null)
	{
		SensorResult.Text = "Sensore non trovato.";
		return;
	}
	uint minReportInterval = this._inclinometer.MinimumReportInterval;
	var desiredReportInterval = minReportInterval > 16 ? minReportInterval : 16;
	this._timer = new DispatcherTimer();
	this._timer.Tick += PollInclinometerSensorReadings;
	this._timer.Interval = new TimeSpan(0, 0, 0, 0, (Int32)desiredReportInterval);
	this._timer.Start();
}
private async void PollInclinometerSensorReadings(object sender, object e)
{
	if (this._inclinometer != null)
	{
		var readings = this._inclinometer.GetCurrentReading();
		await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
		{
			SensorResult.Text = String.Format("Pitch (X) rotation: {0,5:0.00} - Yaw (Y) rotation: {1,5:0.00} - Roll (Z) rotation: {2,5:0.00}",
				readings.PitchDegrees,
				readings.YawDegrees,
				readings.RollDegrees);
		});
	}
}

Usare il sensore di luminosità

Un'applicazione Windows Store può utilizzare il sensore di luminosità per individuare e rispondere a cambiamenti nella luminosità ambientale. Questo può essere utile, ad esempio, per aggiustare il contrasto tra il background e il font utilizzato per il testo sulla base della luminosità rilevata, migliorando così la leggibilità del testo stesso.

Anche in questo caso, per testare il sensore puoi utilizzare la pagina XAML già usata con l'accelerometro.

La misura della luminosità ambientale è espressa in lux e può essere recuperata tramite la proprietà IlluminanceLux della classe LightSensorReading class, come mostrato nel prossimo listato.

public MainPage()
{
	this.InitializeComponent();
	this.InitializeLightSensor();
}
private LightSensor _lightSensor;
private void InitializeLightSensor()
{
	this._lightSensor = LightSensor.GetDefault();
	if (this._lightSensor == null)
	{
		SensorResult.Text = "Sensore non trovato.";
		return;
	}
	uint minReportInterval = this._lightSensor.MinimumReportInterval;
	var desiredReportInterval = minReportInterval > 16 ? minReportInterval : 16;
	this._lightSensor.ReportInterval = desiredReportInterval;
	this._lightSensor.ReadingChanged += LightSensor_ReadingChanged;
}
private async void LightSensor_ReadingChanged(LightSensor sender, LightSensorReadingChangedEventArgs args)
{
	await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
	{
		SensorResult.Text = String.Format("Illuminazione in lux : {0}", args.Reading.IlluminanceInLux);
	});
}

In questo articolo abbiamo visto i sensori ricompresi nel namespace Windows.Devices.Sensors e che includono sensori come accelerometro, girometro, inclinometro, ecc. Nel prossimo articolo approfondiremo invece le API esposte dal namespace Windows.Devices.Geolocation per determinare la posizione geografica dell'utente.

Ti consigliamo anche