Streaming Pebble Accelerometer Data

Updates
– 30/3/14 – Added links to source code

It’s been a long term aim of mine to try and speed up AppMessage as fast as I can, in order to transfer more than mere signal messages between the phone and the watch. An example of this is the long time it takes to send responses to the watch in Wristponder (although now that only applies when a change takes place, thanks to the Persistent Storage API).

An ideal use case for this is some sort of accelerometer data stream, so I set to it. I realised that the key to the fastest possible AppMessage speed is to send the next message as soon as possible, when the last one has been received on the other side. If a waiting period is not observed, there will be problems, such as APP_MSG_BUSY or APP_BSG_BUFFER_OVERFLOW. The solution I used uses the app_message_outbox_sent() callback to send the next message. This function is called as soon as the other side ACKnowledges the last message, signalling that it is ready for the next.

Gathering the accelerometer data asynchronously into a global storage array:

static void accel_new_data(AccelData *data, uint32_t num_samples)
{
	for(uint32_t i = 0; i < num_samples; i++)
	{
		latest_data[(i * 3) + 0] = (int)(0 + data[i].x);	//0, 3, 6
		latest_data[(i * 3) + 1] = (int)(0 + data[i].y);	//1, 4, 7
		latest_data[(i * 3) + 2] = (int)(0 + data[i].z);	//2, 5, 8
	}
}

And sending it when the previous message has been ACKnowledged:

static void send_next_data()
{
	DictionaryIterator *iter;
	app_message_outbox_begin(&iter);

	for(int i = 0; i < NUM_SAMPLES; i++)
	{
		for(int j = 0; j < 3; j++)
		{
			int value = 0 + latest_data[(3 * i) + j];
			Tuplet t = TupletInteger((3 * i) + j, value);
			dict_write_tuplet(iter, &t);
		}
	}

	app_message_outbox_send();
}

static void out_sent_handler(DictionaryIterator *iter, void *context)
{
	//CAUTION - INFINITE LOOP
	send_next_data();

	//Show on watch
	static char buffs[3][32];
	snprintf(buffs[0], sizeof("X: XXXXX"), "X: %d", latest_data[0]);
	snprintf(buffs[1], sizeof("Y: YYYYY"), "Y: %d", latest_data[1]);
	snprintf(buffs[2], sizeof("Z: ZZZZZ"), "Z: %d", latest_data[2]);
	text_layer_set_text(x_layer, buffs[0]);
	text_layer_set_text(y_layer, buffs[1]);
	text_layer_set_text(z_layer, buffs[2]);
}

An additional measure that helps speed things up is temporarily reducing the ‘sniff interval’ of the Bluetooth module to ‘SNIFF_INTERVAL_REDUCED‘:

app_comm_set_sniff_interval(SNIFF_INTERVAL_REDUCED);

And collecting accelerometer data at a faster rate than it is consumed, to avoid sending duplicate frames. This appears to be about 15 AppMessages per second, each packed with 30 ints representing 10 time-spliced samples from the accelerometer, with a total throughput of approximately 1.6 KBps.

The end result looks like this (using the excellent Android GraphView library):
Screenshot_2014-03-26-19-21-09The next step may be to implement some sort of gesture recognition to enable movements to control some external application. We shall see!

Source code
Android
Pebble

 

Advertisements
14 comments
  1. Dan said:

    Is this on Github so I can download the android and watch app?

  2. Dan said:

    Thanks!!!

  3. Robin said:

    Interesting.
    I developed something very similar: see https://github.com/foldedtoad/PebblePointer.
    I had no idea your code was available, and wrote it from scratch.
    It is interesting to see the similarities and differences in these implementations and designs.
    Robin

    • bonsitm said:

      Very nice, neat code! Will try it out soon.

  4. Hi Chris, thanks for doing all these posts and tutorials, they’ve been a huge help learning to Pebble! I tried running your code on my Pebble and Android, and the Pebble is stuck at “Waiting for Android…”, and the Android says “NO DATA” even after I try pushing “START” a few times. Any idea what might be wrong?
    Loren

    • bonsitm said:

      Hi, I’m not sure what is causing this. Can you use logcat on Android or CloudPebble to get Pebblel logs that may indicate any errors?

      • On the phone side: There doesn’t look like anything bad in LogCat (I’m new to Android/LogCat, so I might be missing something glaring in the logs). I see PblAndroid say “touching up App Message with transaction id -1to have id 38″ and then “28 93 b0 c4 2b ca 4c 83 a3 3a 0e f6 ba 6c 8b 17″ (the UUID in your Landing.java), then BluetoothSocket.cpp says “writeNative” (I assume that’s the phone’s Bluetooth program sending the message?).

        On the watch side: the watch app_log doesn’t say anything except for “I C:0 I/O Buffer: 126/656″, which happens in your C code during init(). I added an app_log(“AWWW YEAHHH”) in the “in_received_handler” method, and that never prints, so the watch is never receiving any messages.

        Any other ideas where to look?

      • bonsitm said:

        How weird. Just de-downloaded and tested both programs and they still work fine, so it isn’t anything broken by SDK or firmware updates. Will have a poke around.

  5. On the phone side: There doesn’t look like anything bad in LogCat (I’m new to Android/LogCat, so I might be missing something glaring in the logs). I see PblAndroid say “touching up App Message with transaction id -1to have id 38” and then “28 93 b0 c4 2b ca 4c 83 a3 3a 0e f6 ba 6c 8b 17” (the UUID in your Landing.java), then BluetoothSocket.cpp says “writeNative” (I assume that’s the phone’s Bluetooth program sending the message?).

    On the watch side: the watch app_log doesn’t say anything except for “I C:0 I/O Buffer: 126/656”, which happens in your C code during init(). I added an app_log(“AWWW YEAHHH”) in the “in_received_handler” method, and that never prints, so the watch is never receiving any messages.

    Any other ideas where to look?

  6. Tomer Levi said:

    In the Pebble app side you set: accel_service_set_sampling_rate(ACCEL_SAMPLING_100HZ);
    which means 100 samples per second.
    On the Android side you get 14 samples per second.

    Are you still dropping messages due to APP_MSG_BUSY or APP_BSG_BUFFER_OVERFLOW?

    • bonsitm said:

      It is possible not all samples make it across, but the sensible use of ACK callbacks should prevent dropped messages. I should revisit this experiment at some point.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: