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

Secure remote control for conventional and virtual desktops

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.

Beginner's guide to SSL certificates

Next page: Resources boom

More from The Register

next story
Xperia Z3: Crikey, Sony – ANOTHER flagship phondleslab?
The Fourth Amendment... and it IS better
Don't wait for that big iPad, order a NEXUS 9 instead, industry little bird says
Google said to debut next big slab, Android L ahead of Apple event
Microsoft to enter the STRUGGLE of the HUMAN WRIST
It's not just a thumb war, it's total digit war
Ex-US Navy fighter pilot MIT prof: Drones beat humans - I should know
'Missy' Cummings on UAVs, smartcars and dying from boredom
Netscape Navigator - the browser that started it all - turns 20
It was 20 years ago today, Marc Andreeesen taught the band to play
A drone of one's own: Reg buyers' guide for UAV fanciers
Hardware: Check. Software: Huh? Licence: Licence...?
The Apple launch AS IT HAPPENED: Totally SERIOUS coverage, not for haters
Fandroids, Windows Phone fringe-oids – you wouldn't understand
Apple SILENCES Bose, YANKS headphones from stores
The, er, Beats go on after noise-cancelling spat
prev story

Whitepapers

Forging a new future with identity relationship management
Learn about ForgeRock's next generation IRM platform and how it is designed to empower CEOS's and enterprises to engage with consumers.
Why and how to choose the right cloud vendor
The benefits of cloud-based storage in your processes. Eliminate onsite, disk-based backup and archiving in favor of cloud-based data protection.
Three 1TB solid state scorchers up for grabs
Big SSDs can be expensive but think big and think free because you could be the lucky winner of one of three 1TB Samsung SSD 840 EVO drives that we’re giving away worth over £300 apiece.
Reg Reader Research: SaaS based Email and Office Productivity Tools
Read this Reg reader report which provides advice and guidance for SMBs towards the use of SaaS based email and Office productivity tools.
Security for virtualized datacentres
Legacy security solutions are inefficient due to the architectural differences between physical and virtual environments.