Sensors in .NET MAUI #MAUIUIJuly
In this post, we will focus on how our phone's sensors work. We will explore six key sensors available in .NET MAUI, complete with real-time visualizations and code samples.

In this post, we will focus on how our phone's sensors work. We will explore how to use them for cool UI effects in a future post.
For today, we will explore six key sensors available in .NET MAUI, complete with real-time visualizations and practical UI patterns that will inspire your next app.
The Sensor Landscape in .NET MAUI
.NET MAUI provides built-in access to several sensors through the `Microsoft.Maui.Essentials` package:
Sensor | Purpose | UI Applications |
---|---|---|
Accelerometer | Linear acceleration in 3D | Shake gestures, tilt controls, step detection |
Gyroscope | Angular velocity/rotation | Smooth camera controls, VR interfaces |
Magnetometer | Magnetic field detection | Compass displays, metal detection games |
Barometer | Atmospheric pressure | Weather widgets, altitude indicators |
Compass | Magnetic north heading | Navigation UIs, AR overlays |
Orientation | Device position/rotation | Auto-rotation, 3D scene controls |
Setting Up Sensor Integration
Let's configure our project for sensor usage.
Project Setup
First, ensure your project includes the necessary permissions:
Android (Platforms/Android/AndroidManifest.xml
):
<uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS" />
<uses-feature android:name="android.hardware.sensor.accelerometer" android:required="false" />
<uses-feature android:name="android.hardware.sensor.gyroscope" android:required="false" />
<uses-feature android:name="android.hardware.sensor.compass" android:required="false" />
iOS (Platforms/iOS/Info.plist
):
<key>NSMotionUsageDescription</key>
<string>This app uses CMAltimeter.</string>
The Service Architecture
We'll use a clean service pattern that standardizes sensor logic from sensor-specific values:
public abstract class BaseBindableSensor_Service : BaseBindableAppCapability_Service
{
public SensorSpeed SensorSpeed { get; set; } = SensorSpeed.UI;
public bool IsMonitoring { get; set; }
public string Status { get; protected set; }
// Abstract methods for each sensor implementation
protected abstract void StartSensor();
protected abstract void StopSensor();
protected abstract bool IsSensorMonitoring();
}
Give it a 👍 if you'd like it too !
Sensor Implementations
Accelerometer: Motion-Responsive UI
The accelerometer measures acceleration forces, perfect for gesture-based controls.
public class Accelerometer_Service : BaseBindableSensor_Service
{
public float XinG { get; set; }
public float YinG { get; set; }
public float ZinG { get; set; } // 1.0G when flat on table
private void OnReadingChanged(object? sender, AccelerometerChangedEventArgs e)
{
MainThread.BeginInvokeOnMainThread(() =>
{
XinG = e.Reading.Acceleration.X;
YinG = e.Reading.Acceleration.Y;
ZinG = e.Reading.Acceleration.Z;
});
}
}
Units
- Properties: X, Y, Z
- Unit: g-force (g) — gravitational acceleration
- Typical Range: ~-1.0 to +1.0 per axis (on a stationary device)
- Meaning: Acceleration including gravity, in g units:
- X: side-to-side
- Y: top-to-bottom
- Z: front-to-back (screen facing you = negative Z)
Here is me shaking my phone around :
Gyroscope: Smooth Rotation Controls
Perfect for camera controls and VR-style interfaces:
public class Gyroscope_Service : BaseBindableSensor_Service
{
public float XRadians { get; set; } // Pitch
public float YRadians { get; set; } // Yaw
public float ZRadians { get; set; } // Roll
public double XDegrees { get; set; }
public double YDegrees { get; set; }
public double ZDegrees { get; set; }
private void OnReadingChanged(object? sender, GyroscopeChangedEventArgs e)
{
MainThread.BeginInvokeOnMainThread(() => {
XRadians = e.Reading.AngularVelocity.X;
XDegrees = RadianToDegree(e.Reading.AngularVelocity.X);
YRadians = e.Reading.AngularVelocity.Y;
YDegrees = RadianToDegree(e.Reading.AngularVelocity.Y);
ZRadians = e.Reading.AngularVelocity.Z;
ZDegrees = RadianToDegree(e.Reading.AngularVelocity.Z);
});
}
}
Units
- Properties: X, Y, Z
- Unit: radians per second (rad/s)
- Typical Range: ~-10 to +10 rad/s (varies by device)
- Meaning: Angular velocity around each axis:
- X: pitch (tilting forward/backward)
- Y: yaw (turning left/right)
- Z: roll (tilting side-to-side)
Here is me shaking my phone around :
Compass: Navigation & Orientation UI
Essential for location-based apps and AR experiences:
public class Compass_Service : BaseBindableSensor_Service
{
public float HeadingInDegrees { get; set; } // 0-359°, 0=North
private void OnReadingChanged(object? sender, CompassChangedEventArgs e)
{
MainThread.BeginInvokeOnMainThread(() => {
HeadingInDegrees = e.Reading.HeadingMagneticNorth;
});
}
}
<Image Source="compass"
WidthRequest="200"
HeightRequest="200"
HorizontalOptions="Center"
VerticalOptions="Center"
Rotation="{Binding HeadingInDegrees, Converter={StaticResource MathExpressionConverter}, ConverterParameter='-x'}" />
Units
- Property: HeadingInDegrees
- Unit: degrees (°)
- Range: 0 to 359° (0° = North)
- Meaning: Direction the device is facing relative to magnetic north.
Pointing my phone at different corners of the room.
Barometer: Atmospheric Air Pressure
Great for weather apps and altitude-sensitive UIs:
public class Barometer_Service : BaseBindableSensor_Service
{
public double PressureInHectopascals { get; set; } // 950-1050 hPa typical range
private void OnReadingChanged(object? sender, BarometerChangedEventArgs e)
{
MainThread.BeginInvokeOnMainThread(() => {
PressureInHectopascals = e.Reading.PressureInHectopascals;
});
}
}
Units
- Property: PressureInHectopascals
- Unit: hectopascals (hPa)
- Typical Range: ~950 to 1050 hPa (varies with altitude and weather)
- Meaning: Atmospheric pressure, used for altitude estimation and weather prediction.
- Higher pressure = lower altitude and/or clear weather
- Low pressure = higher altitude and/or stormy weather
This one’s more observational — just turn it on and see the values fluctuate.
Magnetometer: Magnetic Field Detection
The magnetometer detects magnetic fields, essential for compass functionality and metal detection:
public class Magnetometer_Service : BaseBindableSensor_Service
{
public float XInMicroTesla { get; set; }
public float YInMicroTesla { get; set; }
public float ZInMicroTesla { get; set; }
// Total magnetic field strength
public double TotalFieldStrength { get; set; }
private void OnReadingChanged(object? sender, MagnetometerChangedEventArgs e)
{
MainThread.BeginInvokeOnMainThread(() =>
{
XInMicroTesla = e.Reading.MagneticField.X;
YInMicroTesla = e.Reading.MagneticField.Y;
ZInMicroTesla = e.Reading.MagneticField.Z;
TotalFieldStrength = Math.Sqrt(
XInMicroTesla * XInMicroTesla +
YInMicroTesla * YInMicroTesla +
ZInMicroTesla * ZInMicroTesla);
});
}
}
Units
- Properties: X, Y, Z
- Unit: microteslas (μT)
- Typical Range: ~-50 to +50 μT (Earth's magnetic field)
- Meaning: Magnetic field strength:
- X: horizontal component (side-to-side)
- Y: vertical component (up/down)
- Z: depth component (front-to-back)
Bringing my phone next to a magnet
Orientation: Device Positioning (Maths ahead!)
The orientation sensor provides quaternion data that can be converted to human-readable angles:

// Raw quaternion components (W, X, Y, Z)
public float W { get; set; } // Scalar component (-1.0 to +1.0)
public float X { get; set; } // Pitch rotation (-1.0 to +1.0)
public float Y { get; set; } // Yaw rotation (-1.0 to +1.0)
public float Z { get; set; } // Roll rotation (-1.0 to +1.0)
// Human-readable Euler angles
public double PitchDegrees { get; private set; } // Forward/backward tilt
public double YawDegrees { get; private set; } // Left/right rotation
public double RollDegrees { get; private set; } // Left/right tilt
private void OnReadingChanged(object? sender, OrientationSensorChangedEventArgs e)
{
MainThread.BeginInvokeOnMainThread(() =>
{
// Update raw quaternion values
W = e.Reading.Orientation.W;
X = e.Reading.Orientation.X;
Y = e.Reading.Orientation.Y;
Z = e.Reading.Orientation.Z;
// Convert to human-readable values
UpdateEulerAngles();
});
}
#region Quaternion to Euler Conversion Methods
/// <summary>
/// Converts quaternion (W,X,Y,Z) to Euler angles (Pitch, Yaw, Roll) in degrees
/// </summary>
private void UpdateEulerAngles()
{
// Convert quaternion to Euler angles using standard formulas
var (pitch, yaw, roll) = QuaternionToEulerAngles(W, X, Y, Z);
PitchInDegrees = RadiansToDegrees(pitch);
YawInDegrees = RadiansToDegrees(yaw);
RollInDegrees = RadiansToDegrees(roll);
}
/// <summary>
/// Converts quaternion to Euler angles (in radians)
/// Returns: (pitch, yaw, roll) in radians
/// </summary>
private static (double pitch, double yaw, double roll) QuaternionToEulerAngles(float w, float x, float y, float z)
{
// Pitch (X-axis rotation) - forward/backward tilt
var sinPitch = 2.0 * (w * x + y * z);
var cosPitch = 1.0 - 2.0 * (x * x + y * y);
var pitch = Math.Atan2(sinPitch, cosPitch);
// Yaw (Y-axis rotation) - left/right turn
var sinYaw = 2.0 * (w * y - z * x);
var yaw = Math.Abs(sinYaw) >= 1
? Math.CopySign(Math.PI / 2, sinYaw) // Use 90 degrees if out of range
: Math.Asin(sinYaw);
// Roll (Z-axis rotation) - left/right tilt
var sinRoll = 2.0 * (w * z + x * y);
var cosRoll = 1.0 - 2.0 * (y * y + z * z);
var roll = Math.Atan2(sinRoll, cosRoll);
return (pitch, yaw, roll);
}
/// <summary>
/// Converts radians to degrees
/// </summary>
private static double RadiansToDegrees(double radians)
{
return radians * (180.0 / Math.PI);
}
#endregion
<!-- Always pointing north and parallel to earth -->
<Image Source="compass"
WidthRequest="200"
HeightRequest="200"
HorizontalOptions="Center"
VerticalOptions="Center"
RotationX="{Binding PitchInDegrees}"
RotationY="{Binding YawInDegrees}"
Rotation="{Binding RollInDegrees}" />
Units
- Properties: X, Y, Z, W (quaternion)
- Unit: None (quaternion representation)
- Meaning: Orientation in 3D space:
- X, Y, Z: vector components
- W: scalar component
- Note: Used for 3D orientation, not directly human-readable.
By mapping them onto this compass, we have a compass that not only points north but also tries to stay aligned with the Earth’s surface:
Check out my MAUI playground for a full sample: