Акселерометр, барометр, компас и другие датчики в Android

Современные устройства на базе Android оснащаются самыми различными датчиками, начиная от акселерометра и, заканчивая датчиками контроля качества воздуха. При этом, различные устройства могут содержать различное количество датчиков. В этой части мы рассмотрим работу с основными датчиками устройства на базе Android.

Общие принципы работы с датчиками

Все доступные для работы интерфейсы датчиков располагаются в пространстве имен Microsoft.Maui.Devices.Sensors.

Для того, чтобы получить доступ к данным с датчиков, приложение должно подучить разрешение HIGH_SAMPLING_RATE_SENSORS.

Для всех датчиков необходимо установить скорость обновления данных. В .NET MAUI для этого используется перечисление SensorSpeed, которое содержит следующие значения:

Имя Значение Описание Интервал обновления
Default 0 Скорость датчика по умолчанию устройства. 200 мс
UI 1 Скорость, подходящая для общего пользовательского интерфейса. 60 мс.
Game 2 Подходит для игр. 20 мс
Fastest 3 Как можно быстрее получить данные датчика. 5 мс

Используя конкретные реализации интерфейсов датчиков работа с ними строится по общему алгоритму:

  1. Проверяется поддержка того или иного датчика на устройстве. Для этого используется свойство IsSupported
  2. Проверяется отслеживаются ли в данный момент показания датчика. Для этого используется свойство IsMonitoring
  3. Если показания не отслеживаются, то приложение подписывается на событие датчика ReadingChanged (или аналогичное) и вызывается метод Start() с параметром скорости обновления (одним из значений SensorSpeed)
  4. Если приложение заканчивает работу, то приложение отписывается от события и вызывается метод Stop().

Конечно, каждый датчик возвращает свои значения, например, акселерометр возвращает вектор ускорения, барометр — давление и т.д. Но принцип чтения данных один и тот же. Теперь перейдем к реализации приложения.

Проверка датчиков на устройстве

Создадим новое приложение Blazor Hybrid и добавим в файл Platforms/Android/MainApplication.cs необходимое разрешение:

using Android.App;
using Android.Runtime;

[assembly: UsesPermission(Android.Manifest.Permission.HighSamplingRateSensors)] //добавляем разрешение

namespace BlazorSensors
{
    [Application]
    public class MainApplication : MauiApplication
    {
        public MainApplication(IntPtr handle, JniHandleOwnership ownership)
            : base(handle, ownership)
        {
        }

        protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
    }
}

Теперь изменим код компонента Home следующим образом:

@page "/"

@using Microsoft.Maui.Devices.Sensors

<h1>Датчики</h1>
<table class="table-bordered">
    <thead>
        <tr>
            <th>Датчик</th>
            <th>Состояние</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var sensor in allSensors)
        {
            <tr>
                <td>@(sensor.GetType().Name)</td>
                <td>
                    @{
                        if (Check(sensor))
                        {
                            <b><font color="green">Доступен</font></b>
                        }
                        else
                        {
                            <b><font color="red">Нет доступа</font></b>
                        }
                    }
                </td>
            </tr>
        }

        
    </tbody>
</table>

@code{

    List<object> allSensors = [Accelerometer.Default, 
                               Compass.Default, 
                               Magnetometer.Default,      
                               Barometer.Default, 
                               Gyroscope.Default, 
                               OrientationSensor.Default];

    private bool Check(object sensor)
    {
       return (bool)(sensor.GetType().GetProperty("IsSupported").GetValue(sensor));
    }
}

Здесь мы создаем список сенсоров:

List<object> allSensors = [Accelerometer.Default, 
                           Compass.Default, 
                           Magnetometer.Default,      
                           Barometer.Default, 
                           Gyroscope.Default, 
                           OrientationSensor.Default];

а затем в разметке компонента запускаем цикл, в котором проверяем доступность датчика на устройстве, выводя в качестве имени сенсора имя класса, реализующего интерфейс по умолчанию и состояние датчика:

@foreach (var sensor in allSensors)
{
    <tr>
        <td>@(sensor.GetType().Name)</td>
        <td>
            @{
                if (Check(sensor))
                {
                    <b><font color="green">Доступен</font></b>
                }
                else
                {
                    <b><font color="red">Нет доступа</font></b>
                }
            }
        </td>
    </tr>
}

В методе Check() мы, используя рефлексию, запрашиваем значение свойства IsSupported:

private bool Check(object sensor)
{
   return (bool)(sensor.GetType().GetProperty("IsSupported").GetValue(sensor));
}

Запустим приложение и проверим результат. На моем устройстве Android я получил следующую таблицу:

Теперь рассмотрим работу с конкретным датчиком. Как было сказано выше — принцип работы с датчиками одинаковый, поэтому в качестве примера, рассмотрим работу с акселерометром.

Чтение данных акселерометра

Добавим в код компонента Home следующие методы:

public void ToggleAccelerometer()
{
    if (Accelerometer.Default.IsSupported)
    {
        if (!Accelerometer.Default.IsMonitoring)
        {
            Accelerometer.Default.ReadingChanged += ReadingChanged;
            Accelerometer.Default.Start(SensorSpeed.UI);
        }
        else
        {
            Accelerometer.Default.Stop();
            Accelerometer.Default.ReadingChanged -= ReadingChanged;
        }
    }
}

private void ReadingChanged(object? sender, AccelerometerChangedEventArgs e)
{
    MainThread.BeginInvokeOnMainThread(()=> {
        accel = $"Показания акселерометра: {e.Reading.ToString()}";
        StateHasChanged();
    });
}

Метод ToggleAccelerometer() начинает или останавливает прослушивание датчика. Если датчик доступен и не производится «прослушивание», то мы подписываемся на событие ReadingChanged и запускаем прослушивание со скоростью UI.

if (!Accelerometer.Default.IsMonitoring)
{
    Accelerometer.Default.ReadingChanged += ReadingChanged;
    Accelerometer.Default.Start(SensorSpeed.UI);
}

иначе — отписываемся от получения события и останавливаем работу с датчиком:

Accelerometer.Default.Stop();
Accelerometer.Default.ReadingChanged -= ReadingChanged;

Метод ReadingChanged() — обработчик события. Здесь мы обновляем интерфейс приложения — выводим данные полученные с датчика:

private void ReadingChanged(object? sender, AccelerometerChangedEventArgs e)
{
    accel = $"Показания акселерометра: \r\n {e.Reading.ToString()}";
    StateHasChanged();
}

переменная accel — это обычная строка:

string accel = "";

Теперь добавим новые элементы управления в разметку компонента:

<button class="btn-primary" @onclick="ToggleAccelerometer">Акселерометр</button>
<p>@accel</p>

Теперь, если запустить приложение и нажать кнопку «Акселерометр», то можно увидеть показания датчика — ускорение по осям.

Следует особо отметить, что, прежде чем применять данные с датчиков для решения каких-либо задач, необходимо датчики откалибровать. Например, на рисунке выше показаны данные с акселерометра при неподвижном устройстве и эти данные постоянно изменяются так как датчик не откалиброван. Вопросы калибровки датчиков и расчёта различных величин по данным датчика выходит за рамки этой статьи 

Получение сообщения о встряхивании устройства

У датчика Accelerometer определено также событие

event EventHandler? ShakeDetected;

которое срабатывает при встряхивании устройства. Чтобы им воспользоваться, напишем ещё один обработчик:

string snake = "";
private void ShakeDetected(object? sender, EventArgs e)
{
    MainThread.BeginInvokeOnMainThread(() =>
    {
        snake = "А-А-А-А-А-!!!!!";
        StateHasChanged();
    });
}

и подпишемся на событие ShakeDetected в методе ToggleAccelerometer():

public void ToggleAccelerometer()
{
    if (Accelerometer.Default.IsSupported)
    {
        if (!Accelerometer.Default.IsMonitoring)
        {
            Accelerometer.Default.ReadingChanged += ReadingChanged;
            Accelerometer.Default.ShakeDetected += ShakeDetected; //подписываемся на событие встряхивания
            Accelerometer.Default.Start(SensorSpeed.UI);
        }
        else
        {
            Accelerometer.Default.Stop();
            Accelerometer.Default.ShakeDetected += ShakeDetected; //отписываемся
            Accelerometer.Default.ReadingChanged -= ReadingChanged;
        }
    }
}

Теперь запустите приложение, включите датчик и потрясите устройство — на экране вы увидите:

Соответственно, чем дольше вы будете трясти устройство, тем длиннее будет строка:

Итого

Все доступные для работы интерфейсы датчиков располагаются в пространстве имен Microsoft.Maui.Devices.Sensors. Работа с датчиками строится по одному принципу — мы должны проверить доступность датчика, вызвать метод Start(), указав скорость чтения данных с датчика и подписаться на событие обновления данных датчика.

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии