Pebble: акселерометр, пример использования

в 15:00, , рубрики: Pebble, pebble watch, tutorial, Программирование, метки:

Pebble: акселерометр, пример использования - 1Акселорометр, который используется в Pebble откалиброван для измерения ускорения в пределах ± 4G, через API возможно получение ускорения по трем осям x, y и z в тысячных долях G. Таким образом диапазон возможных значений для каждой оси от -4000 до 4000.
Есть возможность установить частоту обновления данных акселерометром в: 10, 25 (по умолчанию), 50 и 100 Hz.

Ниже о том, как получать данные встроенного в Pebble акселерометра и экстремальное применение приобретенных знаний.

Разработчики предлагают три обособленных способа использования акселерометра [1]:

  • Tap Event Service — реакция приложения на резкое движение часов. [2]
  • Data Event Service — получение всех данных акселерометра. [3]
  • Service Peek — интерактивное получение данных.[4]

Tap Event Service

Используетя когда приложению нужно отследить факт того, что пользователь потряс часами или постучал по ним.
Обработчик события в общем случае будет выглядеть, как:

static void tap_handler(AccelAxisType axis, int32_t direction) {
}

AccelAxisType axis — ось по которой произошел «тычек», возможные значения ACCEL_AXIS_X, ACCEL_AXIS_Y, ACCEL_AXIS_Z.
int32_t direction — направление «тычка» (-1 или +1). Для оси X положительное направление к правой части часов, для Y — движение к верхней части, для Z — вертикально вверх.

Подписка на события, обработчик tap_handler вызывается при каждом tap'е:

accel_tap_service_subscribe(tap_handler);

Data Event Service

Используется для накопления и анализа данных акселерометра, определение конкретного жеста или движения пользователя.

Обработчик события в общем случае будет выглядеть, как:

static void data_handler(AccelData *data, uint32_t num_samples) {

}

AccelData *data — набор данных для всех осей, плюс таймштамп и признак работал ли вибродвигатель при получении набора.
uint32_t num_samples — количество наборов в буфере, от 0 до 25.

Подписка на события, обработчик data_handler вызывается каждый раз при получении набора данных.

uint32_t num_samples = 3;
accel_data_service_subscribe(num_samples, data_handler);

И пример из документации обработки данных:

static void data_handler(AccelData *data, uint32_t num_samples) {
  // Long lived buffer
  static char s_buffer[128];

  // Compose string of all data for 3 samples
  snprintf(s_buffer, sizeof(s_buffer),
    "N X,Y,Zn0 %d,%d,%dn1 %d,%d,%dn2 %d,%d,%d",
    data[0].x, data[0].y, data[0].z,
    data[1].x, data[1].y, data[1].z,
    data[2].x, data[2].y, data[2].z
  );

  //Show the data
  text_layer_set_text(s_output_layer, s_buffer);
}

Service Peek

Получение последних сохраненных значений показателей акселерометра.

N.B. Использование невозможно при подписке на Data Event Service.

В любой момент времени вызов accel_service_peek позволяет прочитать последние данные сохраненные акселерометром.

Типовое использование:

AccelData accel = (AccelData) { .x = 0, .y = 0, .z = 0 };
accel_service_peek(&accel);

Определение высоты подбрасывания часов

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

Идея совершенно не новая, в первый раз я столкнулся с подобным на Nokia n900 [5] — игрушка n900Fly. Потом появилось S.M.T.H (Send Me To Heaven).

Функциональность приложения:

  • определение состояние полета часов;
  • определение времени полета и расчет высоты;
  • отображение последнего результата подкидывания

В данном случае ни определение тапа, ни анализ наборов данных не используем.
Не выдумывая нового, спооб определения моментов бросания и приземления из n900Fly:

  • расчитывается средний вектор ускорения, как sqrt(x^2+y^2+z^2);
  • если вектор становится меньше 400 — момент отрыва;
  • если вектор становится больше 500 — момент приземления.

Инициализируем акселерометр, устанавливаем частоту обновления в 25Hz и подписываемся на таймер с интервалом обновления 20 милисекунд:

#define ACCEL_STEP_MS 20

static void init(void) {
  /* ... */

  accel_service_set_sampling_rate(ACCEL_SAMPLING_25HZ);
  accel_data_service_subscribe(0, NULL);
  timer = app_timer_register(ACCEL_STEP_MS, accel_callback, NULL);
}

Раз в 20 милисекунд проходит опрос акселерометра, расчитывается среднее значение ускорения и определяется состояние часов:

static void accel_callback(void *data) {
  AccelData accel = (AccelData) { .x = 0, .y = 0, .z = 0 };
  accel_service_peek(&accel);

  // Полетели
  if (( accel_abs(accel) < 400 ) && ( !iFlying)) {
    time_ms(&start.sec, &start.ms);
    iFlying = true;
  };

  // Приземлилиись
  if ( (accel_abs(accel) > 500) && (iFlying) ) {
    time_ms(&end.sec, &end.ms);
    flightTime = flight_time(start, end);
    flightHeight = flight_height(flightTime);
    iFlying = false;
    layer_mark_dirty(s_layer);
    timer = app_timer_register(1000, accel_callback, NULL);
    return;
  }

  timer = app_timer_register(ACCEL_STEP_MS, accel_callback, NULL);
}

Если фиксируем приземление, то перерисовываем слой и задерживаем следующие обращение к данным акселерометра на 1 с, иначе даже маленький отскок посчитается как новый бросок.

Математические функции в Pebble SDK представлены скудно. Поэтому для вычисления квадратного корня используется целочисленный алгоритм вычисления обратного квадратного корня [6], найденный на просторах Интернета:

#define SQRT_MAGIC_F 0x5f3759df

float my_sqrt(const float x) {
    const float xhalf = 0.5f*x;

     union {
        float x;
        int i;
    } u;
    u.x = x;
    u.i = SQRT_MAGIC_F - (u.i >> 1);
    return x*u.x*(1.5f - xhalf*u.x*u.x);
}

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

typedef struct time_with_ms {
  time_t sec;
  uint16_t ms;
} time_with_ms;

static time_with_ms start;
static time_with_ms end;

/* ... */

time_ms(&end.sec, &end.ms);

/* ... */

time_ms(&start.sec, &start.ms);

/* ... */

// Время полета в мс
static uint32_t flight_time(time_with_ms start, time_with_ms end) {
  return ( (end.sec - start.sec)*1000 + end.ms - start.ms );
}

А высоту на которую подлетают часы считаем по формуле G*t^2/2, где g=9.8 м/с^2:

// Высота полета в мм
static uint32_t flight_height(uint32_t flightTime) {
  return (uint32_t)( 9.8 * ((flightTime/2)*(flightTime/2))/2 / 100 );
}

Посчитаны flightTime и flightHeight, можно их показать любым удобным для пользователя способом:
Pebble: акселерометр, пример использования - 2

Кто выше?

Полный код приложения bitbucket/icanfly

1. Pebble Developers // Detection Acceleration
2. Pebble Developers // Tap Event Service
3. Pebble Developers // Data Event Service
4. Pebble Developers // AccelerometerService
5. maemo.org — package overview for n900Fly
6. Википедия — Быстрый обратный квадратный корень

Автор: tmnhy

Источник

Поделиться

* - обязательные к заполнению поля