Archive

Monthly Archives: April 2014

One of the most requested features for Wristponder I get is the ability to manually choose a small set of ‘favourite’ contacts, perhaps as only having the last contacts and cumulative incoming contacts wasn’t enough. I realised that after adding favourite contacts the main menu would get much longer, and so the UI would need an overhaul to better navigate the list of contacts. So the menu was redesigned to contain Last SMS, Last Call, Top Incoming -> Incoming contacts, Favourites -> Favourite contacts. This way it’s quicker to navigate where you want to go, as opposed to always scrolling the whole list.

wp 2 screens

Thankfully using the new data streaming method to download contacts as opposed to sending them synchronously from Android (which was prone to errors and missed messages, which the new method is not) I was able to present the synchronising of contacts as a fancy progress bar as the app opens. Behind the scenes as well, I split up one huge C file into a header-source pair for each window with as few globals as needed. Much easier to work with now!

wp sync

The favourite contacts themselves are selected using a new button in the main Android app’s ActionBar, and chosen using a series of Spinners.

wp 2 heart

The reason for this is that I had great difficulty finding a way to integrate the Contact Picker’s Activity life-cycle (using startActivityForResult) into a ListActivity. Short of using another Activity as a blank go-between, this was the best option I found. It’s very likely I’ll make a better interface in the future if the inspiration strikes me.

Screenshot_2014-04-26-21-31-39As with the last update, the response list is synchronised only when changed using two checksums calculated internally on both sides, then compared when a contact is selected. This makes use of the app a lot faster and illustrates the usefulness of the Persistent Storage API!

As always, you can get Wristponder from Google Play:

Download
Get it on Google Play

Edit: A gist of the PebbleGestureModel.java file is now available, but not polished.

In snatches of down time over the last few weeks I created a stream of Pebble accelerometer data and integrated it into a new version of my Android game engine, which I have plans for using over the summer for a proper implementation of a few game ideas I’ve toyed with over the last year or so.

After further small bits of work, I created a class called PebbleGestureModel, which receives new X, Y and Z data and performs threshold and duration checks (to prevent continuous firing) before executing abstract actions, implemented upon instantiation. Below is an example with no actions assigned for an acceleration threshold of 800 (g is approximately 1000), a minimum duration between firing actions and operating in the tilt mode :

PebbleGestureModel model = new PebbleGestureModel(800, 1000L, PebbleGestureModel.MODE_TILT) {

	@Override
	public void onWristUp() {

	}

	@Override
	public void onWristRight() {

	}

	@Override
	public void onWristLeft() {

	}

	@Override
	public void onWristDown() {

	}

	@Override
	public void onActionEnd() {

	}

};

The result of this is a simple ‘game’ consisting of a randomly generated ‘world’ of 10 x 10 blocks, with two blocks nominated as the Finish and Player respectively. Touching the ‘world’ generates a new random one. At the moment the Player and Finish are randomly placed on valid non-solid tiles, but are not path-checked. If no path connects them, I just touch for a new one until a valid one is found.

Screenshot_2014-04-22-01-48-53

The Player block is controlled by accelerometer data from the Pebble, and can operate in two modes: MODE_FLICK and MODE_TILT. In MODE_FLICK a flick of the extended watchface-up wrist in each direction will trigger an abstract method to allow an action to be taken. Similarly in MODE_TILT the actions are triggered when the wrist is tilted left or right, or the arm is pointed up or down. The START button is used to start the data stream and the INSTALL button is used to install the streaming watchapp. The four black squares show the current actuating direction induced by the watch, and the first sample of the last received AppMessage (currently 5 samples per message) is shown at the bottom.

Here is a video of the ‘game’ in action, showing the accelerometer control:

I’m not releasing the source code to this yet, as it’s untidy due to it’s ad-hoc development and it doesn’t do much game-wise, but may tidy it up and release it soon.

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.