Pebble: Accelerometer Case Study

  • Tutorial
The accelerometer used in Pebble is calibrated to measure acceleration within ± 4G, through the API it is possible to obtain acceleration in three axes x, y and z in thousandths of G. Thus, the range of possible values ​​for each axis is from -4000 to 4000.
It is possible to set accelerometer data refresh rate at: 10, 25 (default), 50 and 100 Hz.

Below is how to get data from Pebble’s built-in accelerometer and the extreme application of acquired knowledge.

The developers offer three separate ways to use the accelerometer [1] :
  • Tap Event Service - the reaction of the application to the sharp movement of the clock. [2]
  • Data Event Service - receiving all accelerometer data. [3]
  • Service Peek - interactive data acquisition. [4]


Tap event service


Used when the application needs to track the fact that the user shook the clock or knocked on it.
An event handler will generally look like:

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

AccelAxisType axis - the axis along which the “poke” occurred, the possible values ​​are ACCEL_AXIS_X , ACCEL_AXIS_Y , ACCEL_AXIS_Z .
int32_t direction - the direction of the "poke" (-1 or +1). For the X axis, the positive direction to the right of the watch, for Y - movement to the top, for Z - vertically up.

Subscribing to events, the tap_handler handler is called on every tap:

accel_tap_service_subscribe(tap_handler);


Data Event Service


It is used to accumulate and analyze accelerometer data, to determine a specific gesture or movement of the user.

An event handler will generally look like:

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

AccelData * data - a data set for all axes, plus a time stamp and a sign whether the vibration motor worked when receiving the set.
uint32_t num_samples - the number of sets in the buffer, from 0 to 25.

Subscribing to events, the data_handler handler is called every time a data set is received.

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

And an example from the data processing documentation:

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,Z\n0 %d,%d,%d\n1 %d,%d,%d\n2 %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


Getting the last saved values ​​of accelerometer indicators.

NB Use is not possible when subscribing to the Data Event Service .

At any time, calling accel_service_peek allows you to read the latest data saved by the accelerometer.

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


Determining the height of the tossing hours


And, as a result, an example of an application that will determine how high the user is not sorry to toss the clock.

The idea is not new at all, for the first time I came across a n900Fly toy similar to Nokia n900 [5] . Then came SMTH (Send Me To Heaven) .

Application Functionality:
  • determining the flight status of the watch;
  • determination of flight time and altitude calculation;
  • display last toss result

In this case, we do not use either the definition of tapas or the analysis of data sets.
Without inventing a new way to determine the moments of throwing and landing from n900Fly:
  • the average acceleration vector is calculated as sqrt (x ^ 2 + y ^ 2 + z ^ 2) ;
  • if the vector becomes less than 400 - the moment of separation;
  • if the vector becomes greater than 500 - the moment of landing.

We initialize the accelerometer, set the update frequency to 25Hz and subscribe to the timer with an update interval of 20 milliseconds:
#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);
}


Once every 20 milliseconds, the accelerometer is polled, the average acceleration value is calculated and the state of the clock is determined:
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);
}

If we fix the landing, then redraw the layer and delay the next access to the accelerometer data for 1 s, otherwise even a small rebound will be counted as a new throw.

The math functions in the Pebble SDK are sparse. Therefore, to calculate the square root, an integer algorithm for calculating the inverse square root [6] , found on the Internet, is used:
#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);
}


For the accuracy of calculating the flight time, it is necessary to take into account the milliseconds:
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 );
}


And we consider the height at which the clock flies up according to the formula G * t ^ 2/2, where g = 9.8 m / s ^ 2:
// Высота полета в мм
static uint32_t flight_height(uint32_t flightTime) {
  return (uint32_t)( 9.8 * ((flightTime/2)*(flightTime/2))/2 / 100 );
}


FlightTime and flightHeight are calculated, you can show them in any way convenient for the user:


Who is higher?

Full bitbucket / icanfly Pebble App Store app code

: I can fly

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. Wikipedia - Fast reverse square root

Also popular now: