Feeds

It's all in the wrist: How to write apps for the Pebble smartwatch

Code and example video - the (bouncing on-screen) ball's in your court

Build a business case: developing custom apps

It all clicks

Let’s add some user interaction. Pebble OS now uses a system akin to its event handling mechanism, the better to help the coder give the user more ways to control the three-button watch. This lets you directly accommodate single clicks short and long, double-clicks, and press-and-hold events, rather than simply waiting for a push on a specific button and then trying to deal with the user’s intentions.

The Pebble SDK, then, defines a ClickConfigProvider entity which is essentially an array of function calls for specific buttons and the various ways each of them can be used. This list of calls is attached to the host window.

Pebble smartwatch

The Pebble’s three buttons for user interaction

First, we need to add the line

    window_set_click_config_provider(&window, (ClickConfigProvider)config_provider);

to the handle_init() initialisation function, and we need to run it after the app’s Window - reached using the pointer variable window - has been pushed onto the OS’s Window stack, or it will be ignored. The above line tells the window where to get its array of button configurations from, which it does by calling a second function, the config_provider passed in the first call. Here’s what it looks like in our app:

void config_provider(ClickConfig **config, Window *window)
{
    config[BUTTON_ID_UP]->click.handler = (ClickHandler)up_single_click_handler;
    config[BUTTON_ID_DOWN]->click.handler = (ClickHandler)down_single_click_handler;
}

We punch into the array of configurations handlers for the button-related events we’re interested in. Here, that’s a couple of single-click handlers, added to the click.handler, fields, but we could have added function calls for each button’s multi_click.handler, its long_click.handler and/or its long_click.release_handler fields. Other fields specify how many clicks in a group we’re interested in, or whether we’re only interested in the final click in a batch - it doesn’t matter how many times the user presses the button in rapid sequence, we just deal with the last one. The mechanism can handle hold-to-repeat actions too.

With your handlers registered this way, all you have to do now is write the handlers themselves. This app uses the top and bottom buttons to trigger a sudden change in the bouncing ball’s direction:

void up_single_click_handler(ClickRecognizerRef recognizer, Window *window)
{
    delta_x = delta_x * -1;
}

and

void down_single_click_handler(ClickRecognizerRef recognizer, Window *window)
{
    delta_y = delta_y * -1;
}

These handlers and, indeed, the config_provider function require declarations at the top of the file, or in a separate header file if you’re using one.

Updating the ball’s movement every second doesn’t make for a very dynamic display, of course. As I say, Pebble OS’ .tick_info handler doesn’t generate more than one event a second, so we’ll have to use its timer_handler instead. To do so, edit the pbl_main function, to remove the .tick_info section from the list of PebbleAppHandlers and replace it with .timer_handler = &handle_timer. The function should now look like this:

void pbl_main(void *params)
{
    AppContextRef ctxt = (AppContextRef) params;
    
    PebbleAppHandlers handlers =
    {
        .init_handler = &handle_init,
        .deinit_handler = &handle_deinit,
        .timer_handler = &handle_timer
    };
  
    app_event_loop(ctxt, &handlers);
}

You’ll notice that I’ve also added a second new handler, .deinit_handler, which is called when the app quits, and is here used to politely cancel any timers in the event queue that have yet to fire:

void handle_deinit(AppContextRef ctxt)
{
    app_timer_cancel_event(ctxt, timerHandle);
}

The value timerHandle is a global variable declared at the start of the program alongside Window window. It provides a reference to the AppTimerHandle timer in memory. We’ll set this up in the handle_init() with an extra line at the end of that function:

    timerHandle = app_timer_send_event(ctxt, 500, 1);

It takes the usual AppContextRef pointer to the app itself, a time in milliseconds before which the timer fires - half a second here - and a unique integer, called a “cookie” by the SDK, to identify the specific timer to the handler when you’re running more than one at once. The timer’s handle and its cookie value are passed to the timer handler function when it’s triggered, so you can check which timer has triggered the code.

Delete the handle_tick() function if you like, but copy it first to form the basis for your handle_timer() function:

void handle_timer(AppContextRef ctxt, AppTimerHandle handle, uint32_t cookie)
{
    pos_x = pos_x + delta_x;
    pos_y = pos_y + delta_y;
    
    if (pos_x > 140)
    {
        pos_x = 132;
        delta_x = -8;
    }
    
    if (pos_x < 4)
    {
        pos_x = 12;
        delta_x = 8;
    }
    
    if (pos_y > 162)
    {
        pos_y = 154;
        delta_y = -8;
    }
    
    if (pos_y < 4)
    {
        pos_y = 12;
        delta_y = 8;
    }
    
    timerHandle = app_timer_send_event(ctxt, 100, 1);
    
    Layer *root = window_get_root_layer(&window);
    layer_mark_dirty(root);
}

One extra line goes in, to call the app_timer_send_event() function as per the line in handle_init(). As yet, Pebble OS timers don’t fire continuously, so we add a new one to the event queue each time the timer fires. I’ve set this time to 100ms.

Compile the app, transfer it to your watch, select it from the Pebble’s menu and you should see a ball moving at a moderate pace around the screen. You can change its direction with a press of the top or bottom buttons. Not very impressive, of course, but the code I've outlined here can form the basis for much more interesting and more useful apps.

The essential guide to IT transformation

Next page: Resources boom

More from The Register

next story
Reg man looks through a Glass, darkly: Google's toy ploy or killer tech specs?
Tip: Put the shades on and you'll look less of a spanner
So, Apple won't sell cheap kit? Prepare the iOS garden wall WRECKING BALL
It can throw the low cost race if it looks to the cloud
One step closer to ROBOT BUTLERS: Dyson flashes vid of VACUUM SUCKER bot
Latest cleaner available for world+dog in September
Samsung Gear S: Quick, LAUNCH IT – before Apple straps on iWatch
Full specs for wrist-mounted device here ... but who'll buy it?
Apple promises to lift Curse of the Drained iPhone 5 Battery
Have you tried turning it off and...? Never mind, here's a replacement
Now that's FIRE WIRE: HP recalls 6 MILLION burn-risk laptop cables
Right in the middle of Burning Mains Man week
Apple's iWatch? They cannae do it ... they don't have the POWER
Analyst predicts fanbois will have to wait until next year
Tim Cook in Applerexia fears: New MacBook THINNER THAN EVER
'Supply chain sources' give up the goss on new iLappy
HUGE iPAD? Maybe. HUGE ADVERTS? That's for SURE
Noo! Hand not big enough! Don't look at meee!
prev story

Whitepapers

Top 10 endpoint backup mistakes
Avoid the ten endpoint backup mistakes to ensure that your critical corporate data is protected and end user productivity is improved.
Implementing global e-invoicing with guaranteed legal certainty
Explaining the role local tax compliance plays in successful supply chain management and e-business and how leading global brands are addressing this.
Backing up distributed data
Eliminating the redundant use of bandwidth and storage capacity and application consolidation in the modern data center.
The essential guide to IT transformation
ServiceNow discusses three IT transformations that can help CIOs automate IT services to transform IT and the enterprise
Next gen security for virtualised datacentres
Legacy security solutions are inefficient due to the architectural differences between physical and virtual environments.