After finishing my engineering dissertation, today is a day to get some coding done before starting on the next assignment sprint tomorrow.

The result is version 1.6.0 of Wristponder!

Screenshot_2014-04-15-14-52-59

New features include:

  • The maximum length of a response has been raised from 40 to 100 characters.
  • Option to auto-start the watchapp when an SMS or phone call is received.
  • Option to receive a notification when the SMS send is successful or fails. This is good for when signal is weak and takes longer than the alert dialog timeout.
  • Sorting the list of responses now triggers a sync of responses where it incorrectly did not previously. Full manual sorting is coming soon, when I figure out the UI processes to use.
  • The watchapp is now built with SDK 2.0.2. Sorry this transition from BETA6 took so long.

As a result, both the Android and watchapp require updating. The good news is that the new watchapp is bundled with the Android app with an improved side-loading mechanism that looks more robust, I hope.

A set of selectable Favourite contacts is also coming soon, but requires significant UI modification, more than a day’s work. Stay tuned for that.

Enjoy!

Download
Get it on Google Play

Building on the small amount of JavaScript code developed by controlling the Spark Core pins from my Pebble, I decided to write a basic webpage to allow me to perform the same control functions but from a browser. Sure, it’s been done before, but as I have no real expertise or experience of JavaScript beyond that used for PebbleKit JS apps, and virtually none at all for HTML, it seemed like a good learning opportunity.

And it turned out to be easier than expected! After a couple of hours, the basic code was in place. The webpage looks like this:

jQUery CoreNothing too pretty to look at, but does the job well.

During the aforementioned Pebble project, I sought help with a problem in getting the same pin number back as I sent to the Spark Cloud. The solution to this turned out to be specifying the Spark.function() parameter string as a key-value dictionary, making the core (aha) code segment in this webpage as so:

//Send the request to the Core
var sendRequest = function(pin, on, device_id, access_token) {
	var url;
	if(on) {
		url = "https://api.spark.io/v1/devices/" + device_id + "/on?access_token=" + access_token;
	} else {
		url = "https://api.spark.io/v1/devices/" + device_id + "/off?access_token=" + access_token;
	}

	console.log("jQuery AJAX: Requesting pin " + pin + " " + (on == true ? "on" : "off") + "...");

	//Send request using jQuert AJAX
	$.ajax({
	  type: "POST",
	  url: url,
	  data: {"args":pin},
	  success: success,
	  dataType: "json"
	});
};

The entire project (three files!) can be found on GitHub here. I may expand it to include more functions in the future, but at the moment it provides a good platform to play around with, and I’ve learned a small amount about HTML forms and using jQuery. Time well spent!

 

Updates:

- Appears the return_code from the Core is not always that which is requested. Works well for Pin D0 though.

- Source code repo updated. All Dx pins now work. Uses jQuery. Still READY-TIMEOUTs though…

I’ve revisited my control of the Spark Core Dx pins, with some polishing up and JS driven UI updating so I know the response has been received from the Spark Cloud.

pebble-screenshot_2014-04-05_17-55-09

I’m loving working with it again, and can’t wait to build some real remote controlled device, but that will have to wait for another day. I plan to release this app eventually, possibly under the name of PebbleTinker, derived from the stock Tinker app for Spark Core, and all looks to be working fine, but I’m being haunted by the READY-TIMEOUT, where PebbleKit JS is killed after about ten seconds after install. Once this is fixed, I look forward to releasing!

If you want to give it a go early, the source code is partly available on GitHub.

Required Reading

Pebble SDK 2.0 Tutorial #1: Your First Watchapp

Pebble SDK 2.0 Tutorial #2: Telling the Time

Pebble SDK 2.0 Tutorial #3: Images and Fonts

Pebble SDK 2.0 Tutorial #4: Animations and Timers

Pebble SDK 2.0 Tutorial #5: Buttons and Vibrations

Pebble SDK 2.0 Tutorial #6: AppMessage for PebbleKit JS

Pebble SDK 2.0 Tutorial #7: MenuLayers

Introduction

NOTE: This section requires knowledge on how to set up a new Android project in an IDE such as Eclipse! I will assume you are using Eclipse

After a few requests and comments, it’s time to revisit the Android app communication from the old 1.X tutorial series and produce an example app for the new 2.0 SDK.

For the purposes of simplicity, we will be extending the Pebble SDK new-project example, which starts us off with a nice button setup. To do this, create a new empty CloudPebble project, and add this code to the main .c file:

#include <pebble.h>

static Window *window;
static TextLayer *text_layer;

static void select_click_handler(ClickRecognizerRef recognizer, void *context) {
  text_layer_set_text(text_layer, "Select");
}

static void up_click_handler(ClickRecognizerRef recognizer, void *context) {
  text_layer_set_text(text_layer, "Up");
}

static void down_click_handler(ClickRecognizerRef recognizer, void *context) {
  text_layer_set_text(text_layer, "Down");
}

static void click_config_provider(void *context) {
  window_single_click_subscribe(BUTTON_ID_SELECT, select_click_handler);
  window_single_click_subscribe(BUTTON_ID_UP, up_click_handler);
  window_single_click_subscribe(BUTTON_ID_DOWN, down_click_handler);
}

static void window_load(Window *window) {
  Layer *window_layer = window_get_root_layer(window);
  GRect bounds = layer_get_bounds(window_layer);

  text_layer = text_layer_create((GRect) { .origin = { 0, 72 }, .size = { bounds.size.w, 20 } });
  text_layer_set_text(text_layer, "Press a button");
  text_layer_set_text_alignment(text_layer, GTextAlignmentCenter);
  layer_add_child(window_layer, text_layer_get_layer(text_layer));
}

static void window_unload(Window *window) {
  text_layer_destroy(text_layer);
}

static void init(void) {
  window = window_create();
  window_set_click_config_provider(window, click_config_provider);
  window_set_window_handlers(window, (WindowHandlers) {
    .load = window_load,
    .unload = window_unload,
  });
  const bool animated = true;
  window_stack_push(window, animated);
}

static void deinit(void) {
  window_destroy(window);
}

int main(void) {
  init();

  APP_LOG(APP_LOG_LEVEL_DEBUG, "Done initializing, pushed window: %p", window);

  app_event_loop();
  deinit();
}

With that in place, test compilation to make sure all works as it should regarding button operation.

To extend this to interact with an Android app, we must first add in the AppMessage components from the AppMessage for PebbleKit JS section. First, define the in_received_handler() where received AppMessages will be interpreted as before:

static void in_received_handler(DictionaryIterator *iter, void *context) 
{
   
}

After this, register the handler and open AppMessage inside init(), before pushing the Window:

//Register AppMessage events
app_message_register_inbox_received(in_received_handler);           
app_message_open(512, 512);    //Large input and output buffer sizes

Define globally the protocol we will use for communication using enumerations or by #defineing constants. I prefer enums, but both will do the job. We will define a key representing a button event occurring, and further values to distinguish between the buttons themselves:

enum {
	KEY_BUTTON_EVENT = 0,
	BUTTON_EVENT_UP = 1,
	BUTTON_EVENT_DOWN = 2,
	BUTTON_EVENT_SELECT = 3
};

The next step is to create a function to send these keys and values, which will be exactly the same as that shown in ‘AppMessage for PebbleKit JS’, above the click handlers:

void send_int(uint8_t key, uint8_t cmd)
{
    DictionaryIterator *iter;
    app_message_outbox_begin(&iter);
     
    Tuplet value = TupletInteger(key, cmd);
    dict_write_tuplet(iter, &value);
     
    app_message_outbox_send();
}

Finally, add calls to send_int() to each of the three button click handlers to send a signal corresponding to which button was pressed. This should look like the code shown below:

static void select_click_handler(ClickRecognizerRef recognizer, void *context) {
	text_layer_set_text(text_layer, "Select");
	send_int(KEY_BUTTON_EVENT, BUTTON_EVENT_SELECT);
}

static void up_click_handler(ClickRecognizerRef recognizer, void *context) {
	text_layer_set_text(text_layer, "Up");
	send_int(KEY_BUTTON_EVENT, BUTTON_EVENT_UP);
}

static void down_click_handler(ClickRecognizerRef recognizer, void *context) {
	text_layer_set_text(text_layer, "Down");
	send_int(KEY_BUTTON_EVENT, BUTTON_EVENT_DOWN);
}

After setting up the Android side, we will come back to the Pebble side to implement the reverse process; sending data to the watch from Android.

Android App Integration

Set up a new Android project and make sure it runs correctly as just a blank Activity. Following the Android SDK plugin for Eclipse without modifying any of the settings except project location and name is a good starting point, which I will be using. After completing this process and removing the superfluous onCreateOptionsMenu(), my main Activity file looks like this:

package com.wordpress.ninedof.pebblesdk2part8;

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}

}

In order to communicate with Pebble, you will need to import the PebbleKit project into Eclipse. Once this is done, add it as a Library by right clicking the Tutorial project and choosing ‘Properties’, then clicking ‘Add’ under the ‘Android’ section. Choose ‘PEBBLE_KIT’ and click OK, then OK again to close the ‘Properties’ dialogue.

So, let’s make the two talk! As the messages will begin coming from the watch we must register a BroadcastReceiver to intercept the Pebble’s AppMessages. This is done as shown below:

private PebbleDataReceiver mReceiver;

... onCreate() here ...

@Override
protected void onResume() {
	super.onResume();

	mReceiver = new PebbleDataReceiver(UUID.fromString("2fc99a5d-ee35-4057-aa9b-0d4dd8e35ef5")) {

		@Override
		public void receiveData(Context context, int transactionId, PebbleDictionary data) {
			
		}

	};

	PebbleKit.registerReceivedDataHandler(this, mReceiver);
}

@Override
protected void onPause() {
	super.onPause();
	
	unregisterReceiver(mReceiver);
}

Be careful to note that the UUID specified in the constructor is the SAME UUID as specified in your corresponding watchapp’s appinfo.json, or in Settings on CloudPebble. The two must match for correct communication!

Next, define the exact same set of keys and values as on the Pebble side, as these are used to communicate:

private static final int
	KEY_BUTTON_EVENT = 0,
	BUTTON_EVENT_UP = 1,
	BUTTON_EVENT_DOWN = 2,
	BUTTON_EVENT_SELECT = 3;

Now this is done we add logic to the overridden receiveData() method to determine which button press was encoded in the received message:

@Override
public void receiveData(Context context, int transactionId, PebbleDictionary data) {
	//ACK the message
	PebbleKit.sendAckToPebble(context, transactionId);

	//Check the key exists
	if(data.getUnsignedInteger(KEY_BUTTON_EVENT) != null) {
		int button = data.getUnsignedInteger(KEY_BUTTON_EVENT).intValue();

		switch(button) {
		case BUTTON_EVENT_UP:
			//The UP button was pressed
			break;
		case BUTTON_EVENT_DOWN:
			//The DOWN button was pressed
			break;
		case BUTTON_EVENT_SELECT:
			//The SELECT button was pressed
			break;
		}
	}
}

The last step that completes this leg of the journey is to actually see which button was pressed on the Android display, akin to how it is on the Pebble. To do this, simply set the main View to a TextView in onCreate:

private TextView buttonView;

...

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	
	mButtonView = new TextView(this);
	mButtonView.setText("No button yet!");
	
	setContentView(mButtonView);
}

Finally, add calls to TextView.setText() in the switch statement within the receiveData method to show on the Android display which button was pressed:

switch(button) {
case BUTTON_EVENT_UP:
	//The UP button was pressed
	mButtonView.setText("UP button pressed!");
	break;
case BUTTON_EVENT_DOWN:
	//The DOWN button was pressed
	mButtonView.setText("DOWN button pressed!");
	break;
case BUTTON_EVENT_SELECT:
	//The SELECT button was pressed
	mButtonView.setText("SELECT button pressed!");
	break;
}

Time to try it out! Compile and install the watchapp, run the Android project in Eclipse to install and launch on your phone, open the watchapp and press a button. You should see something like this:

pebble-screenshot_2014-04-04_00-05-49

Screenshot_2014-04-04-00-04-54

Going The Other Way

To send data back to Pebble, we will define a new key on both sides to trigger a vibration. Name this key KEY_VIBRATION and give it a value of 4. With this done, modify the receiveData() method to send this message using a PebbleDictionary object after the switch statement like so:

//Make the watch vibrate
PebbleDictionary dict = new PebbleDictionary();
dict.addInt32(KEY_VIBRATION, 0);
PebbleKit.sendDataToPebble(context, UUID.fromString("2fc99a5d-ee35-4057-aa9b-0d4dd8e35ef5"), dict);

Finally, return to CloudPebble and add the new key to the main .c file. Finally, add a call to vibes_short_pulse() in in_received_handler():

enum {
	KEY_BUTTON_EVENT = 0,
	BUTTON_EVENT_UP = 1,
	BUTTON_EVENT_DOWN = 2,
	BUTTON_EVENT_SELECT = 3,
	KEY_VIBRATION = 4
};

...

static void in_received_handler(DictionaryIterator *iter, void *context) 
{
	Tuple *t = dict_read_first(iter);
	if(t)
	{
		vibes_short_pulse();
	}
}

Recompile, install and launch BOTH the Pebble and Android apps, press a button and feel the communications flowing through to your wrist!

Conclusions

Another long post! For more information on diving deeper and to send more complex forms of data, check out the AppMessage documentation.

Source code is on GitHub for both the Pebble and Android projects.

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

 

In a bit of downtime I thought I’d make myself a small app to give me local weather using PebbleKit JS to interpret a BBC Weather feed for my local town.

pebble-screenshot_2014-03-15_22-31-01

Quite stylish, I think! It’s for personal use, as I have neither the time or energy to generalise it enough, and I’m sure there are plenty of weather apps out there already!

Updates
- Corrected select_click_callback() signature (thanks to MatthewTote!)

Required Reading

Pebble SDK 2.0 Tutorial #1: Your First Watchapp

Pebble SDK 2.0 Tutorial #2: Telling the Time

Pebble SDK 2.0 Tutorial #3: Images and Fonts

Pebble SDK 2.0 Tutorial #4: Animations and Timers

Pebble SDK 2.0 Tutorial #5: Buttons and Vibrations

Pebble SDK 2.0 Tutorial #6: AppMessage for PebbleKit JS

Introduction

After a few requests, in this section we will look at using MenuLayers in a Pebble watchapp. If you pick up your Pebble now and press the select button from the watch face, what you see is a MenuLayer. It has rows, icons and actions. Let’s build one of those!

pebble-screenshot_2014-03-13_00-22-47

Setup

The first step as usual is to start a new CloudPebble project with the basic app template. Here’s that again, for convenience:

#include <pebble.h>

Window* window;

void window_load(Window *window)
{

}

void window_unload(Window *window)
{

}

void init()
{
	window = window_create();
	WindowHandlers handlers = {
		.load = window_load,
		.unload = window_unload
	};
	window_set_window_handlers(window, (WindowHandlers) handlers);
	window_stack_push(window, true);
}

void deinit()
{
	window_destroy(window);
}

int main(void)
{
	init();
	app_event_loop();
	deinit();
}

Now that’s out the way, declare a global pointer to a MenuLayer at the top of the file below the pre-processor directives.

MenuLayer *menu_layer;

This Layer type is a bit more complex to set up than the other Layers, in that it requires a large amount of information about how it will look and behave before it can be instantiated. This information is given to the MenuLayer via the use of a number of callbacks. When the MenuLayer is redrawn or reloaded, it calls these functions to get the relevant data. The advantage of this approach is that the MenuLayer rows can be filled with data that can be changed at any time, such as with Wristponder or Pebble Tube Status (shameless plugs!)

The API documentation describes all the possible MenuLayerCallbacks that can be associated with a MenuLayer, but the ones we will be using for a simple example will be:

  • .draw_row – This is used to draw the layout inside a menu item
  • .get_num_rows – This is used to feedback the total number of rows in the MenuLayer. This can be a #defined value, or an int, and so variable
  • .select_click – This is used to decide what happens when the select button is pressed, which will vary depending on which row is currently selected

Let’s define these callbacks using the signatures provided by the API documentation linked previously. These must be above window_load() as is now the norm (hopefully!):

void draw_row_callback(GContext *ctx, Layer *cell_layer, MenuIndex *cell_index, void *callback_context)
{

}

uint16_t num_rows_callback(MenuLayer *menu_layer, uint16_t section_index, void *callback_context)
{

}

void select_click_callback(MenuLayer *menu_layer, MenuIndex *cell_index, void *callback_context)
{

}

Now those are in place, let’s add code to have them do something we’d find more useful than blank callbacks. The example we are going to use is a list of fruits (boring, I know!). The list will be of seven fruits, and brief descriptions. Thus, the num_rows_callback() function becomes simply:

uint16_t num_rows_callback(MenuLayer *menu_layer, uint16_t section_index, void *callback_context)
{
	return 7;
}

For the draw_row_handler(), we will need to be able to alter what is drawn in the row depending on which row it is. This can be done by switching the cell_index->row property. You can use the presented GContext however you like for any of the SDK drawing functions, but to keep things simple we will use the pre-made drawing functions provided by the SDK. With these two last points combined, the draw_row_callback() function transforms into this beast:

void draw_row_callback(GContext *ctx, Layer *cell_layer, MenuIndex *cell_index, void *callback_context)
{
	//Which row is it?
	switch(cell_index->row)
	{
	case 0:
		menu_cell_basic_draw(ctx, cell_layer, "1. Apple", "Green and crispy!", NULL);
		break;
	case 1:
		menu_cell_basic_draw(ctx, cell_layer, "2. Orange", "Peel first!", NULL);
		break;
	case 2:
		menu_cell_basic_draw(ctx, cell_layer, "3. Pear", "Teardrop shaped!", NULL);
		break;
	case 3:
		menu_cell_basic_draw(ctx, cell_layer, "4. Banana", "Can be a gun!", NULL);
		break;
	case 4:
		menu_cell_basic_draw(ctx, cell_layer, "5. Tomato", "Extremely versatile!", NULL);
		break;
	case 5:
		menu_cell_basic_draw(ctx, cell_layer, "6. Grape", "Bunches of 'em!", NULL);
		break;
	case 6:
		menu_cell_basic_draw(ctx, cell_layer, "7. Melon", "Only three left!", NULL);
		break;
	}
}

The NULL references are in the place that a row icon reference would be placed (if a GBitmap were to be shown). Thus, each layer will be drawn with its own unique message.

The final callback, select_click_callback() will do something different depending on which row is selected when the select button is pressed. To illustrate this, we will use a series of vibrations that signifies the numerical value of the row. Here’s how this is done (or Vibes 101!):

void select_click_callback(MenuLayer *menu_layer, MenuIndex *cell_index, void *callback_context)
{
	//Get which row
	int which = cell_index->row;

	//The array that will hold the on/off vibration times
	uint32_t segments[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

	//Build the pattern (milliseconds on and off in alternating positions)
	for(int i = 0; i < which + 1; i++)
	{
		segments[2 * i] = 200;
		segments[(2 * i) + 1] = 100;
	}

	//Create a VibePattern data structure
	VibePattern pattern = {
		.durations = segments,
		.num_segments = 16
	};

	//Do the vibration pattern!
	vibes_enqueue_custom_pattern(pattern);
}

With those three callbacks in place, we can actually create the MenuLayer and add it to the main Window. This is done in four stages:

  • Create the MenuLayer and assign it to the global pointer
  • Set it up to receive clicks from the Window
  • Set the callbacks we just wrote to give the MenuLayer the information it needs
  • Add the MenuLayer to the main Window

Here’s the code for that sequence, with annotations (Note the casts used in the MenuLayerCallbacks structure creation):

void window_load(Window *window)
{
	//Create it - 12 is approx height of the top bar
	menu_layer = menu_layer_create(GRect(0, 0, 144, 168 - 16));

	//Let it receive clicks
	menu_layer_set_click_config_onto_window(menu_layer, window);

	//Give it its callbacks
	MenuLayerCallbacks callbacks = {
		.draw_row = (MenuLayerDrawRowCallback) draw_row_callback,
		.get_num_rows = (MenuLayerGetNumberOfRowsInSectionsCallback) num_rows_callback,
		.select_click = (MenuLayerSelectCallback) select_click_callback
	};
	menu_layer_set_callbacks(menu_layer, NULL, callbacks);

	//Add to Window
	layer_add_child(window_get_root_layer(window), menu_layer_get_layer(menu_layer));
}

As always, de-init the MenuLayer:

void window_unload(Window *window)
{
	menu_layer_destroy(menu_layer);
}

If all has gone well, after compilation you should be greeted with the screen below, as well as the corresponding vibrations when each row is selected:

pebble-screenshot_2014-03-13_01-27-12

Conclusions
So that’s how to setup a basic MenuLayer. An extended application like those mentioned previously will use char[] buffers to store each row’s text, modified in a in_received signature AppMessage callback, and calling menu_layer_reload_data() in that AppMessage callback, thus updating the MenuLayer with the new data.

The source code can be found on GitHub HERE!

Let me know any queries you have. Enjoy!

Follow

Get every new post delivered to your Inbox.

Join 107 other followers