Pebble SDK 2.0 Tutorial #2: Telling the Time.

Required Reading

Pebble SDK 2.0 Tutorial #1: Your First Watchapp

Introduction

So far, if you were stopped in the street and asked about your Pebble, you’d be able to wow them by telling them you wrote your own watch app. Less impressive will it seem when it can’t do anything useful, so let’s fix that now!

Getting the Time

To get the time for your watchface, Pebble SDK allows you to subscribe to an event service that runs a ‘handler’ whenever it ticks.

For example, if you create a function called tick_handler() and register it to be run when the minute changes, you would be able to allow the watch to sleep the remaining time. Obviously there is a trade off between frequency of updates and power usage, but being the watch face designer, you get to choose!

So, create a new cloudpebble project and paste in the code you wrote in the last tutorial section into the main .c file. Let’s get the time! First, go to Settings and change the ‘App kind’ to Watch Face.

The next step is to create the empty handler function we will use to update the time display when the minute ticks over. Here’s how you’d go about doing that:

void tick_handler(struct tm *tick_time, TimeUnits units_changed)
{
	//Here we will update the watchface display
}

The ‘tick_time’ argument contains the time data and ‘units_changed’ contains which unit was changed. You could use this to run an Animation once every hour, for example. Make sure the function is, as always, defined BEFORE it is first encountered. So in this case above where window_load() is defined.

You register this handler function with the tick event service using the function below, inserted into init().

tick_timer_service_subscribe(MINUTE_UNIT, (TickHandler) tick_handler);

The MINUTE_UNIT specifier tells the tick service that we want an update on each new minute, and no more often. You can choose other types from the API list here.

Once again, we can undo what we set up when the watch face is unloaded, so add the corresponding unsubscribe call in deinit(). This is virtually redundant as nothing can happen once the watchface is closed, but is a good practice to get into:

tick_timer_service_unsubscribe();

So now we will be getting our timely updates, we can update the TextLayer we created last time to show the time to the user. First, reserve a global character buffer that will live as long as the TextLayer below the pointers at the top of the file (as the text is not copied when being set as the TextLayer‘s current text):

char buffer[] = "00:00";

Next, add the following calls to your tick handler to format the character buffer with the time and set it as the TextLayer‘s new text:

void tick_handler(struct tm *tick_time, TimeUnits units_changed)
{
	//Format the buffer string using tick_time as the time source
	strftime(buffer, sizeof("00:00"), "%H:%M", tick_time);

	//Change the TextLayer text to show the new time!
	text_layer_set_text(text_layer, buffer);
}

A full listing of the modifiers can be found here. Now when the tick event service counts a new minute, it will call our handler function, which in turn updates the time shown to the user. Nifty! Go to ‘Compilation’ and try it.

But what if we want to start the watchface with the time already showing? We can just call out tick handler (it is, after all, just a function!) and supply the current time and tick interval it is expecting. The code segment below shows how this is accomplished, placed at the end of window_load():

//Get a time structure so that the face doesn't start blank
struct tm *t;
time_t temp;
temp = time(NULL);
t = localtime(&temp);

//Manually call the tick handler when the window is loading
tick_handler(t, MINUTE_UNIT);

Aesthetic Touches

At the moment this watch face is pretty dull. Let’s make it a bit more pleasing to the eye! You can use the text_layer_set_* functions (found here) to change how the TextLayer looks. Here’s an example replacement of the relevant code segment in window_load() that looks a little more realistic:

text_layer = text_layer_create(GRect(0, 53, 132, 168));
text_layer_set_background_color(text_layer, GColorClear);
text_layer_set_text_color(text_layer, GColorBlack);
text_layer_set_text_alignment(text_layer, GTextAlignmentCenter);
text_layer_set_font(text_layer, fonts_get_system_font(FONT_KEY_BITHAM_42_BOLD));

layer_add_child(window_get_root_layer(window), (Layer*) text_layer);

Now that the current time is large, front and center, we will add one final aesthetic touch before moving on to GBitmaps, custom GFonts, Animations etc in the next section of the tutorial.

Introducing the InverterLayer! When placed on top of other layers, this Layer simply inverts all the pixels under it from white to black and vice versa. Let’s add one on top of our time display to show the effect.

First, declare an InverterLayer pointer at the top of the file in global scope:

InverterLayer *inv_layer;

Next, add the relevant function calls in window_load() to create and position the InverterLayer. Make sure it is added as a child layer AFTER the TextLayer, or the TextLayer won’t be subject to its inverting effects (it won’t be below it):

//Inverter layer
inv_layer = inverter_layer_create(GRect(0, 50, 144, 62));
layer_add_child(window_get_root_layer(window), (Layer*) inv_layer);

Re-compile and install from the Compilation screen and you should see a smart banner of black across the middle of your watch face, inverting the previously black time text to white.

But we’re still not done! Remember we must destroy what we create in the window_unload() function. Here’s how said function should look now:

void window_unload(Window *window)
{
	//We will safely destroy the Window's elements here!
	text_layer_destroy(text_layer);

	inverter_layer_destroy(inv_layer);
}

Conclusion

So there you have it! A more interesting watch face, ready for any way of improvement. I’ll cover a few that I’ve already mentioned such as images, custom fonts and animations in the next tutorial sections. In the meantime check what you have against the example project available on GitHub, or if you got stuck.

As always, let me know if you have any queries of feedback. Stay tuned for the next section!

Advertisements
19 comments
  1. Ender said:

    Great job once again! I know this would take a lot more work but having pictures of the watch face showing the effects of each block of code or how the program is progressing (i.e. invert layer vs no invert layer) would be awesome!

    Keep up the great tutorials!

    • bonsitm said:

      Not a bad idea. Will consider for the future! Thanks.

  2. I’ve caught up on all the SDK1 stuff and just worked through SDK2 and I’m finally getting my head wrapped around this coding thing. I can’t thank you enough for providing these for us! CloudPebble is turning out to be a pretty powerful tool, is it just as easy to code and compile outside of cp? Keep up the awesome work! I’m looking forward to the next installment.

    • bonsitm said:

      Thanks for the feedback! Glad it is of use.

      Outside CloudPebble on Windows you will need to set up an Ubunu Linux virtual machine, install the tool chain but once all that is done rapid testing can be done much faster and with better feedback. If you have a small amount of experience with a terminal, I’d recommend the guide on the Pebble dev site.

  3. how do you change the mode from 24 to 12 or vice/versa based on the choice on the Pebble?

    • OMG I figured it out!!! please still answer in case I found a wrong way to do it that somehow works 😛

      • bonsitm said:

        Hi Patrick. The way I would recommend is using clock_is_24hr_style()

      • Right, but how do I empliment this into the code?

      • bonsitm said:

        It returns a bool type, so use an if statement. An SDL 1.X example can be seen in section 1 of the old tutorial, available from the same contents page. It may require a couple of tweaks for 2.0, however.

  4. Alan said:

    You could be clearer where things go. You should finish each section with a run down of what is where or even a full rundown of the file as I am getting a lot of errors doing as you have mentioned.

    • bonsitm said:

      Thanks for the feedback, Alan. You can find a complete source code listing to compare to your code on GitHub.

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: